Eh.. Jednak mam problem z tą listą xD

0

Gdzieś tydzień temu zakończyłem temat o wysyłaniu wiadomości do wszystkich połączonych klientów.. Ok dzięki wam nauczyłem się jak robić metody xD. Ucze się i ucze wiec nie miejcie pretensji.. Przez kilka dni dałem sobie troche spokoju z C# i dzisiaj znów daje czadu, no ale cóż, myślałem, że poradze se z tą listą.. Coś wspominaliście o sortowaniu.. Odpalałem tą listę petlą for, ale dupa.. Pomożcie, aby wiadomość, która dostała sie do servera, to ten wysłał do wszystkich klientów...

class Program
    {
        static class ProgramHead
        {
            public static class Server
            {
                public static Socket listener = null;
                public static List<ReadyToListen> MAX_CLIENTS = new List<ReadyToListen>();

                public static void Start()
                {
                    listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    IPEndPoint ip = new IPEndPoint(IPAddress.Any, 5000);
                    listener.Bind(ip);
                    listener.Listen(10);
                    Console.WriteLine("Server Started  ;)");

                    while (true)
                    {
                        Socket klient = listener.Accept();
                        byte[] buffername = new byte[256];
                        int bytesReceive;
                        bytesReceive = klient.Receive(buffername);
                        IPEndPoint klientIP = (IPEndPoint)klient.RemoteEndPoint;
                        Console.WriteLine("[connect] " + Encoding.UTF8.GetString(buffername, 0, bytesReceive) + " połączył się z serverem [{0}:{1}]",klientIP.Address,klientIP.Port);
                        new ReadyToListen(klient);
                        ProgramHead.Server.MAX_CLIENTS.Add(new ReadyToListen(new Socket(new SocketInformation()))); // przy działaniu tej pętli wyskakuje warning lub error
                    }       
                }
            }
        }

        class ReadyToListen
        {
            Socket klientSocket = null;
            public ReadyToListen(Socket klient)
            {
                klientSocket = (Socket)klient;
                Thread oh = new Thread(new ThreadStart(ListenClient));
                oh.Start();
            }

            private void ListenClient()
            {
                byte[] message = new byte[256];
                int bytesReceive;

                while (true)
                {
                    bytesReceive = 0;

                    try
                    {
                        bytesReceive = klientSocket.Receive(message);
                    }
                    catch
                    {
                        Console.WriteLine("Client disconnected");
                        break;
                    }

                    string tekst = Encoding.UTF8.GetString(message, 0, bytesReceive);
                    Console.WriteLine(tekst);
                    byte[] SendPacket = Encoding.UTF8.GetBytes(tekst);
                    // tutaj miała być pętla, no ale MAX_CLIENTS jest w innej classie (Server)
                    klientSocket.Send(SendPacket); // tutaj stare jeszcze, do jednego klienta
                } 
            }
        }
0

w czym dokladnie jest problem?
co z tego że Max_Clients jest w innej klasie? Jest jako public wiec to nie problem sie do niej dostac.
<ort>poza tym </ort>nazwa jest troche nietrafiona.

lecisz przez tablice forem i wysyłasz dla kazdego soketu różnego od tego z klasy wysyłającej.

Ale tak na marginesie, takie wysyłanie, w tylu wątkach... hm nie wiem czy nie było by to troche chaotyczne. tzn klienci dostawali by wiadomości nie we właściwej kolejności.

Mogłbyś zrobić to tak, że jest wątek dodający nowych klientow, ten masz, mniej wiecej.
są wątki klientów odbierające od nich, tez masz
i jest jeden wątek wysyłający do klientów.

wtedy... klient odebrał, daje znać wątkowi ktory wysyła do reszty, tzn dodaje do jakiejś tam kolejki wiadomości do wysłania. Wydaje mi się że tak było by to bardziej uporządkowane. Ale może się mylę.
Ogolnie chodzi mi o kolejkowanie wiadomości odebranych, a nastepnie o wysyłanie po kolei do klientów.

Aczkolwiek i tak proponował bym Ci robic chat na UdpCliencie korzystając z MultiCastGroup

0

Nie wiem mi się zdaje, że każda klasa i wątek sa stworzone dla każdego oddzielnego klienta.. Więc nie powinno być kolidacji.. Ja wolę TCP :)

Ok zrobiłem, że klasa ProgramHead obejmuje też klase ReadyToListen, no cóż, nie wiem jak to posortować. <ort>na razie </ort>tylko tyle chce, jeśli bedą jakieś problemy to napisze.. Bo jak posortuje to już bedę mógł operować to petlą?

0

twoj kod to niezla masakra :)
ale od poczatku

po zaakceptopwaniu przez serwer polaczenia, dostajesz socket klienta
Socket klient = listener.Accept();
zakladam ze pierwsza wiadomosc od klienta to takie "Helo" dlatego ja czytasz i wyswiatlasz
na marginesie w Console.WriteLine moglbys uzywac formatowania, a nie klejenia stringow, w przypadku 2 parametrow tak zrobiles, czemu nie w przypadku calosci, to jest zagadka
Console.WriteLine("[connect] {2} połączył się z serverem [{0}:{1}]",klientIP.Address,klientIP.Port, Encoding.UTF8.GetString(buffername, 0, bytesReceive));
na postawie socketa klient powienienes utworzyc instancje klasy ReadyToListen, co robisz, ale czemu trafia to w proznie? a nie jest dodawane do listy?
MAX_CLIENTS.Add(new ReadyToListen(klient));

a ta linijka jest totalnie do czapy
ProgramHead.Server.MAX_CLIENTS.Add(new ReadyToListen(new Socket(new SocketInformation()))); // przy działaniu tej pętli wyskakuje warning lub error
dodajesz do listy, klase, ktora startuje watek, ktory nasluchuje na jakims lewym sockecie, bo SocketInformation jest puste

podsumowywujac

                        byte[] buffername = new byte[256];
                        int bytesReceive;
while (true)
                    {
                        Socket klient = listener.Accept();

                        bytesReceive = klient.Receive(buffername);
                        IPEndPoint klientIP = (IPEndPoint)klient.RemoteEndPoint;
                        Console.WriteLine("[connect] {2} połączył się z serverem [{0}:{1}]",klientIP.Address,klientIP.Port, Encoding.UTF8.GetString(buffername, 0, bytesReceive));
                        lock(locker)
                        {
                            MAX_CLIENTS.Add(new ReadyToListen(klient));
                        }
                    }  

dalej w klasie serwer deklarujesz pole
public static readonly object locker = new object();

w klasie ReadyToListen klientSocket zrob publiczne

teraz w metodzie ListenClient()
zeby wyslac do wszystkich klientow informacje
lock(ProgramHead.Server.locker)
{
ProgramHead.Server.MAX_CLIENTS.ForEach(x => SendSomeMessage(x.klientSocket));
}

oczywiscie SendSomeMessage musisz sobie napisac sam :)

0

a może być coś takiego? :

 for (int i = 0; i < 20; i++)
{
     ProgramHead.Server.MAX_CLIENTS[i].klientSocket.Send(SendPacket);
}
0

Dlaczego 20?

0

moge nawet 50 jejuś.. więc?

0

No ale co będzie jak dasz 50, a klientów podłączonych będzie tylko 30-tka? 31-wszy nie będzie już istniał i wystąpi błąd w pętli.

0
risen napisał(a)

a może być coś takiego? :

 for (int i = 0; i < 20; i++)
{
     ProgramHead.Server.MAX_CLIENTS[i].klientSocket.Send(SendPacket);
}

tak, mozesz takze tak
ale jak ktos zauwazyl co to k... jest zeby na sztywno od czapi wpisac ograniczenie na iterator, iterujac generic list!!!

for(int i=0; i < ProgramHead.Server.MAX_CLIENTS.Count; i++)

majac inny (koslawy) kod ryzykujesz ze jesli w tablicy bedzie mniej elementow niz 20 - dostaje ArgumentOutOfRangeEcveption
a jesli wiecej, to do czesci klientow nie wyslesz
to jest idea listy, ze ilosc elementow moze byc zmienna

0
ProgramHead.Server.MAX_CLIENTS[i].ForEach(klient => klient.klientSocket.Send(SendPacket));

zastanów się nad nazwą listy - MAX_CLIENTS za bardzo nie pasuje do tego co ta lista trzyma

0

powyższe rozwiązanie też ma wady, bo lista może być edytowana w trakcie iteracji i równiez wywali wyjątek ; p.

0

ahh wam chodzi o "wypełnienie luk". Ok dzieki za tę metode .Cout, tera musze czekac az kumpel wbije na kompa i przetestujemy : )

0

No tylko zrób sobie osobną zmienną ilosc_userow = lista.Count(); i dopiero wtedy iteruj (i zalockuj)

0

i jesli masz zamiar zmieniać zawartość listy to iteruj od końca

1

A nie wystarczy zalockować na czas iteracji?

0

Chodzi o dwie różne rzeczy, o:
-zmiane wielkości i indeksów kolekcji podczas iteracji co zaowocuje błędnym działaniem.
-synchronizacje przy działaniu wątków na jednym obiekcie. Na to sposobów jest dosyć dużo, od lock-ów do autoresetevent-ów.

Jeżeli mowa o tym że obiekt moze byc czytany przez przykladowo 2 watki, a nadpisywany przez 1 to mógłbyś pokombinować z ReaderWriterLockSlim.
tylko że z tego co wiem, poniżej .Net 3.0 miał on jakieś błędy.

0

to jak juz dochodzimy do synchronizacji dostepu, to nalezy jeszcze wskazac ne konicznosc synchronizacji, nie tylko dostepu do listy, ale takze do gniazd klientow, bo jeden watek wysle do wszystkich klientow wiadomosc, ale inne w tym czasie nasluchuja wiadomosci przychodzacych
mysle ze rozwiazanie z lock (oczywiscie przy uzyciu nie jednej zminnej) powinno sie sprawdzic

0

Jeżeli mowa o tym że obiekt moze byc czytany przez przykladowo 2 watki, a nadpisywany przez 1 to mógłbyś pokombinować z ReaderWriterLockSlim.
tylko że z tego co wiem, poniżej .Net 3.0 miał on jakieś błędy.

ReaderWriterLockSlim nie istniał w starszych werjach .NET, lecz ReaderWriterLock - który rzeczywiście miał błędy i już się go nie używa.

Co do rozwiązania - to idealnie pasuje tu kolejka - "Producer - Consumer Queue"

http://msdn.microsoft.com/en-us/library/yy12yx1f%28VS.80%29.aspx
http://en.wikipedia.org/wiki/Producer-consumer_problem

Po prostu twój program składał by się z:

  • kolejki wiadomości z zabezpieczeniem dot. wielowątkowości - wystarczy Queue<T> + lock()
  • wątkiem, który wrzuca maile do wysłania na kolejkę (starczy ci 1 wątek)
  • wątki, które biorą maile z kolejki i po kolei wysyłają (jeśli w danym czasie ma być wysyłany tylko jeden mail - używasz do tego tylko 1 wątku).

Ten sposób rozwiąże wszystkie twoje problemy.

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