Chat - serwer client .NET pomożcie

0

Witam

Pisze sobie aplikację która ma pełnić rolę zwykłego chata jaki jest dostępny np na wp.pl albo onet.pl. I mam 2 aplikacje - serwera i klienta. Wszystko działa mi ok tylko nie potrafię zrobić jednej rzeczy a mianowicie, po odpaleniu aplikacji klienta i zalogowaniu sie na serwer uzytkownik nie jest dodawany (bądź też w przypadku wylogowania usuwany) do listbox w aplikacji klienta.

Mam taki kodzik serwera - metoda logowania sie i wylogowania sie:

public Dictionary<string,string> signedList;

    public cChatServer()
    {
        signedList = new Dictionary<string,string>();
    }

    public bool signIn (string _name)
    {
        Console.Write("[" + _name + "] loguje się ...");
        if (signedList.ContainsKey(_name))
        {
            Console.WriteLine("BŁĄD, użytkownik o takiej nazwie już istnieje");
            return false;
        }
        else
        {
            signedList.Add(_name,"");
            Console.WriteLine("OK");
            return true;
            
        }
    }
    
    public bool signOut (string _name)
    {
        Console.Write("[" + _name + "] wylogowuje się ...");
        if (!signedList.ContainsKey(_name))
        {
            Console.WriteLine("BŁĄD, użytkownik o takiej nazwie nie był zalogowany");
            return false;
        }
        else
        {
            signedList.Remove(_name);
            Console.WriteLine("OK");
            return true;
        }
    }

oraz kod klienta (dokładniej timera które jest uruchamiany co 0,5 sekundy w celu sprawdzenia czegoś - w tym wypadku czy ktos się nowy nie zalogował):

void refreshTimer_Tick(object sender, EventArgs e)
{

        //Wykonuje się tylko jak jesteś podłączony
        if (state == APP_STATES.CONNECTED)
        {
            outputTextBox.Text += GLOBAL.server.feedMessages(nickTextBox.Text);

         
           

if (!listBox1.Items.Contains(nickTextBox.Text))
{
listBox1.Items.Add(nickTextBox.Text);
}

        }
    }

I teraz pytanie. Jak zrobić by do listBox1 dodac w tym wypadku _name z signedList.Add(_name,"") - kod serwera. Bo teraz mam zawsze 1 uzytkownika pokazanego jako dostepnego tzn tego ktory odpalił aplikacje klienta.

Co musze dopisać - jaki warunek do pogrubionego kodu aby aby dodac do listBox1 uzytkownika??

Mysle ze trzeba było by zrobic jakas funkcje ktora zwraca bieżącą wartosć signedList.Add(_name,"") (i ją wywołać w Timerze ale nie wiem jak) i dopisać listBox1.Items.Add[signedlist] i takze dopisac warunek nad pogrubioną czescia kodu ze jesli np funkcja public bool signIn (string _name) i funkcja public bool signOut (string _name) zwracaja true to wykonuje sie cos takiego jak ponizej tylko ze listBox1.Items.Add(signedlist{indeks]).

Proszę o pomoc i jakis skromny kod ktory rozwiazałby moj problem.</wiki>

0

1.Po pierwsze... jak przesyłasz nicki osób które się zalogowały do clienta z srwera?
2.Ja bym proponował je wysyłać jako gotową tablice przesłać zserializowaną a potem ją zdeserializować i wkopiować do programu

0

No własnie tu jest problem - bo nie wiem jak je przesłać. Do zapamietania nicków słuzy lista signedlist(_name) do której po zalogowaniu jest dodawany element --> signedList.Add(_name,""); zaś po wylogowaniu jest usuwany --> signedList.Remove(_name);

Problem polega na tym ze funkcja do logowania i wylogowywania w ktorej wykorzystuje listę jest bool czyli zwraca true lub false zas nie zwraca mi konkrentej wartosci signedlist[index].

I nie wiem jak na kliencie wyłuskać dowolna wartość z signedlist[index] bo ta lista jest widziana i zainicjalizowana na serwerze podczas logowania i wylogowywania sie. Mysłąem o jakiej metodzie ktora zwroci mi wartosc po zaistnieniu jednego z powyzszych dwoch zdarzen (logowania sie lub wylogowani sie) ale nie wiem jak sie do nie zabrac.

Jesli mogłbym prosic o pomoc to prosze podac maila lug gg to podesle moj projekt - moze to wyjani tyci problem. Dziekuje za zainteresowanie oim problemem.

Pozdrawiam

0

A dodam jeszcze ze temat serializacji i deserializacji tablic jest dla mnie nowoscią i nie slyszałem o tym więc nie wiem jak zabrac sie za taką tablicę. Proszę ewentalnie o jakis kod na serwerze i kliencie który by wykonywal te czynności jesli to nie sprawi problemu:)

0

jeśli chodzi o pierwsze to napisz procedure zwracającą zamiast bool to string z nickiem osoby...
Potem Serializuj ją... (opcja serialize) i przesyłaj przez port... tylko nie napisze Ci kodu bo serializacji używałem raz w życiu polecam poszukać w artykułach na www.codeguru.pl tam dobrze opisują

0

Serializacja nie jest najlepszym wyjsciem, bo za kazdym razem jak sie ktos zaloguje to bedziesz przesylal pelna tablice osob na czacie. Szkoda zachodu i zapychac lacza.

Nie widze za bardzo kodu, ktory przesyla jakakolwiek informacje do klienta, ze cos sie stalo... Skad klient ma wiedziec, ze ktos nowy sie zalogowal?

0

No też racja... Aktualnie jako 17 letni fascynat c# i amator pisze sobie taką aplikacje... Aktualnie pisze serwer gdzie będzie rejestracja i zapis do bazy loginów odczyt logowanie... i mam właśnie klasy ban itp itd i teraz też zastanawiam się nad tym jak zrobić żeby wszystko na 1 poręce grało a głównie to pobieranie czy ktoś jest online /ewentualnie pobranie wszystkich osób)

Mam jeszcze takie pytanko jak przeszukuje dane w bazie i sprawdzam czy taki login jest to mam wysyłane msg czy zalogowało czy źle ,lecz gdy mam więcej niż 1 rekord w bazie to sprawdza po kolei i po kolei wysyła mi komunikaty czyli źle /źle dobrze dla 3 rekordów...

Przepraszam że kodu nie wkleje ale nie jestem u siebie i nie mam do tego dostępu

0

hmm dzięki za podpowiedzi-sprobuje cos wykombinować:)

Więc może nakreśle sprawę w całości najpierw kod serwera:

using System;
using System.Collections.Generic;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.IO;
using System.Text;
using remote_types;

namespace chat_server
{
public class cChatServer : MarshalByRefObject, remote_types.iChatServer
{

    int licz=0;
    public Dictionary<string,string> signedList;
         
    public cChatServer()
    {
        signedList = new Dictionary<string,string>();
    }

    public bool signIn (string _name)
    {
        Console.Write("[" + _name + "] loguje się ...");
        if (signedList.ContainsKey(_name))
        {
            Console.WriteLine("BŁĄD, użytkownik o takiej nazwie już istnieje");
            return false;
        }
        else
        {
            signedList.Add(_name,"");
            Console.WriteLine("OK");
            return true;
            return signedList[licz];
            licz++;
            
        }
    }
    
    public bool signOut (string _name)
    {
        Console.Write("[" + _name + "] wylogowuje się ...");
        if (!signedList.ContainsKey(_name))
        {
            Console.WriteLine("BŁĄD, użytkownik o takiej nazwie nie był zalogowany");
            return false;
        }
        else
        {
            signedList.Remove(_name);
            Console.WriteLine("OK");
            return true;
        }
    }

    //uwaga, funkcja na razie nie obsługuje wysyłania do konkretnego użytkownika
    //każda wiadomość wysyłana jest do wszystkich userów
    public bool send(cMessage _message)
    {
        Console.WriteLine("Widomość "+_message.ToString() );

        string[] tmp = new string[signedList.Keys.Count];
        signedList.Keys.CopyTo(tmp,0);
        int licznik = 0;         
        foreach (string _k in tmp)
        {
            if (_message.to == "")
            {

                signedList[_k] += _message.from + ":" + _message.text + Environment.NewLine;
            }
            else
            {
                if (signedList.ContainsKey(_message.to))
                {

                    signedList[_message.to] += _message.from + ":" + _message.text + Environment.NewLine; ;
                    licznik++;

                }
            }
            if (licznik == 1)
            {
                break;
            }
            
        }
        tmp = null;

        return false;
    }


    //pobierz wiadomości 
    public string feedMessages(string _name)
    {
        if (signedList.ContainsKey(_name))
        {
            string tmp = signedList[_name];
            signedList[_name] = "";
            return tmp;
        }
        else
        {
            return "";
        }
    }

}

}

A teraz kod klienta:

using System;
using System.Collections.Generic;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using System.IO;
using System.Text;
using remote_types;

namespace chat_server
{
public class cChatServer : MarshalByRefObject, remote_types.iChatServer
{

    int licz=0;
    public Dictionary<string,string> signedList;
         
    public cChatServer()
    {
        signedList = new Dictionary<string,string>();
    }

    public bool signIn (string _name)
    {
        Console.Write("[" + _name + "] loguje się ...");
        if (signedList.ContainsKey(_name))
        {
            Console.WriteLine("BŁĄD, użytkownik o takiej nazwie już istnieje");
            return false;
        }
        else
        {
            signedList.Add(_name,"");
            Console.WriteLine("OK");
            return true;
            return signedList[licz];
            licz++;
            
        }
    }
    
    public bool signOut (string _name)
    {
        Console.Write("[" + _name + "] wylogowuje się ...");
        if (!signedList.ContainsKey(_name))
        {
            Console.WriteLine("BŁĄD, użytkownik o takiej nazwie nie był zalogowany");
            return false;
        }
        else
        {
            signedList.Remove(_name);
            Console.WriteLine("OK");
            return true;
        }
    }

    //uwaga, funkcja na razie nie obsługuje wysyłania do konkretnego użytkownika
    //każda wiadomość wysyłana jest do wszystkich userów
    public bool send(cMessage _message)
    {
        Console.WriteLine("Widomość "+_message.ToString() );

        string[] tmp = new string[signedList.Keys.Count];
        signedList.Keys.CopyTo(tmp,0);
        int licznik = 0;         
        foreach (string _k in tmp)
        {
            if (_message.to == "")
            {

                signedList[_k] += _message.from + ":" + _message.text + Environment.NewLine;
            }
            else
            {
                if (signedList.ContainsKey(_message.to))
                {

                    signedList[_message.to] += _message.from + ":" + _message.text + Environment.NewLine; ;
                    licznik++;

                }
            }
            if (licznik == 1)
            {
                break;
            }
            
        }
        tmp = null;

        return false;
    }


    //pobierz wiadomości 
    public string feedMessages(string _name)
    {
        if (signedList.ContainsKey(_name))
        {
            string tmp = signedList[_name];
            signedList[_name] = "";
            return tmp;
        }
        else
        {
            return "";
        }
    }

}

}

oraz jezcze potrzebne i niezbedne rzeczy do obslugi chata:

using System;
using System.Collections.Generic;
using System.Text;

namespace remote_types
{
[Serializable]
public class cMessage
{
string pFrom;
string pTo;
string pText;

    public cMessage(string _from, string _to, string _text)
    {
        pFrom = _from;
        pTo = _to;
        pText = _text;
    }

    public string from
    {
        get { return pFrom; }
    }
    public string to
    {
        get { return pTo; }
    }
    public string text
    {
        get { return pText; }
    }

    public override string ToString()
    {
        return "FROM:" + pFrom + " TO:" + pTo + " TEXT:" + pText;
    }
}

}

oraz

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

namespace remote_types
{
public interface iChatServer
{

   bool signIn(string _name);
   bool signOut(string _name);
   bool send(cMessage _message);
   string feedMessages(string _name);

}

}

Tak to wyglada. Czy mógłbys zmodyfikować metode refresch timera (odswieżania timera-2 wklejony przeze mnie kod) tak aby do listbox byli wpisywani obecni uzytkownicy chata (1 kod wklejony przeze mnie motoda signIn oraz signout??

0

2x wkleiles kod serwera.

ALE - czy mi sie zdaje czy w zamieszczonym kodzie nie ma nic o polaczeniu miedzy klientem a serwerem? W jaki sposob wysylasz cokolwiek do kogokolwiek skoro nie ma polaczenia? Nigdzie tez nie widac, zebys wywolywal metode send...

Klasyczny serwer wyglada np. tak:
Ma tablice otwartych polaczen do klientow, np. w tablicy hashowanej, gdzie kluczem jest powiedzmy nick (jesli jest unikalny).
Serwer ma zapuszczona jedna metode w kolko, ktora to metoda nasluchuje na jakims porcie na nadejscie klienta. Jak sie ktos polaczy to przekazuje polaczenie np. do klasy PolaczenieSerwerKlient (czyli od strony serwera), ktora to klasa zajmuje sie wysylaniem i odbieraniem danych. To samo po stronie klienta, czyli PolaczenieKlientSerwer, ale widziane od strony klienta.
Jak sie juz polacza to sobie chwile uzgadniaja szczegoly (negocjacja nicku, warunkow polaczenia, przesylanie np. listy aktualnie zalogowanych, itp).

Przesylanie danych zalogowanych klientow mozesz zrobic tak, ze polaczysz moja sugestie z sugestia Aniooola. Czyli na poczatku wysylasz cala tablice zalogowanych, zeby na starcie mozna bylo zobaczyc kto w ogole jest. Moze byc serializowana, moze byc w formacie czysto tekstowym, zeby bylo latwo sprawdzac, np taki tekst:

--Poczatek listy zalogowanych--
nick 1, moderator
nick2, uzytkownik
nick3, uzytkownik
--Koniec listy zalogowanych--

Oczywiscie mozesz kompresowac, zeby bylo szybciej, mozesz chronic przed bledem przez dodanie jakiegos unikalnego stringu wynegocjowanego na poczatku polaczenia, itp. Zalezy do czego Ci ten czat. Jak w celach cwiczeniowych, to lepiej tekst, bo latwo pozniej sprawdzac co przyszlo/wyszlo chocby snifferem, jezeli cos nie dziala.

Tak samo mozesz zrobic za kazdym razem jak sie ktos loguje, czyli wiadomosc typu

--nowy--
nick4

Na Twoim miejscu, jezeli to rzeczywiscie jest caly kod aplikacji zajal bym sie na razie wysylaniem/odbieraniem wiadomosci i negocjacja polaczenia, a pozniej bawil w graficzne przedstawienie.

0
  1. johny_bravo mówisz o czymś innym - to działa tak, że wystawiasz przez np. HTTP obiekt na świat, który klienci traktują jakby był ich lokalnym obiektem
  2. co nie zmienia faktu, że nie widzę nigdzie ani otwarcia kanału ani wystawienia obiektu
    2a. a w kliencie nie widzę połączenia z serwerem i pobrania obiektu
  3. aby to działało obiekt na serwerze musi być singletonem
  4. obiekt taki może mieć normalne zdarzenia, pod które mogą podpiąć się klienci więc serwer zamiast odsyłać cokolwiek w "standardowy" sposób po prostu generuje zdarzenie, które dociera do wszystkich klientów - możesz np. wysyłać powiadomienie o nowej wiadomości + nr klienta do którego ona jest + nr wiadomości i po przefiltrowaniu przez klientów reagować na ten event będzie tylko adresat i będzie "pobierał" odpowiednią wiadomość - zminimalizuje to ruch w sieci. Jednak eventy w remotingu są trochę bardziej skomplikowane i jeśli będziesz chciał to jutro mogę Ci dać przykład i trochę go opisać.

Działa to bardzo fajnie i w sieci lokalnej przy kilkudziesięciu klientach nie czuć opóźnień.

i jeszcze prośba do pytacza - zapoznaj się z czymś takim jak tagi, a szczególnie z < code=csharp>

a zapomniałem - jedyny feler jest taki, że klient nie wyłapuje zerwania połączenia lub zamknięcia serwera - ale i na to jest sposób - brzydki bo brzydki ale działa w 100% :p

0

A sorki, nie zauwazylem z czego korzysta autor. Ja na tej zasadzie pisalem czaty w javie akurat, wiec w c# tez sie da zapewne, choc nie probowalem. Takiego sposobu o jakim Ty piszesz nie znalem :)

0

Ty pisałeś z tego co wywnioskowałem o "normalnym" czacie opartym na gniazdach i własnym protokole - oczywiście da się tak, ale remoting załatwia za Ciebie dużo pracy, przede wszystkim cała pętla wysyłania, odbierania i dekodowania komunikatów. Ma to też wadę w postaci nadmiaru przesyłanych informacji - nie wiem ile bo nie miałem czasu potestować ale pewnie zależy od samej implementacji.

Wyobraź sobie, że piszesz czat zawierający się w jednym exeku, tzn masz klasę serwer która nadzoruje cały system i ma metodę wyślij i zdarzenie nowawiadomość - możesz sobie podłączyć ile chcesz okienek pod to zdarzenie i wywoływać metodę wyślij z ilu chcesz przycisków - bez wymyślania protokołu, sprawdzania, czy cała ramka dotarła itp. TO właśnie daje Ci remoting

0

Brzmi niezle, chociaz ja tez nie zaglebialem sie w to czy ramka doszla czy nie :P od tego jest TCP/IP :) Generalnie te, ktore pisalem sprowadzaly sie do obsluzenia tekstu, ktory przyszedl. Chociaz z tego co piszesz tego tez tutaj w zasadzie nie ma, wiec latwiej :)

0

Przepraszam zapomiałem dodać start serwera i wybór kanału:

using System;

using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace chat_server
{
class Program
{
[STAThread]
static void Main(string[] args)
{
int port;
string portStr;
Console.WriteLine("Podaj nr poru, na którym będzie nasłuchiwać serwer:");
portStr = Console.ReadLine();

        try
        {
            port = Convert.ToInt32(portStr);
        }
        catch
        {
            port = 8123;
            Console.WriteLine("Podano zły numer portu, przyjęto numer domyślny 8123");
        }

        TcpChannel channel = new TcpChannel(port);
        ChannelServices.RegisterChannel(channel,false);

        RemotingConfiguration.RegisterWellKnownServiceType(
            Type.GetType("chat_server.cChatServer"),
            "chatserver",
            WellKnownObjectMode.Singleton
        );

        Console.WriteLine("CHAT SERVER START");
        Console.ReadLine();
        Console.WriteLine("CHAT SERVER STOP");
        Console.ReadLine();
       
    }
}

}

Teraz to juz wszystko:) Więc macie pomysł jakiś??

0
AdaskoJ napisał(a)

Więc macie pomysł jakiś??

a czytałeś mój post?

0

Czytałem bardzo uważnie ale nie wiem jak takie coś zrobić:( Szukam na różnych kursach ale jak na razie na moim poziomie to dla mnie wyzsza szkoła jazdy. Byłbym wdzięczny za jakiś pomysł w postaci prostego kodu umieszczonego na forum. Nie wiem jak zrobic metode ktora pobiera jako argumenty signedlist i zwraca element - cały czas komilator wyrzuca mi bład.

1 użytkowników online, w tym zalogowanych: 0, gości: 1