[C#] Podział strumienia gniazda zgodnie z wywołaniem Send()


(Musialowski Tomasz) #1

Witam!

Mam problem przy aplikacji klient-serwer. W skrócie, utworzyłem sobie takie protokół, żądanie jest wysyłane na serwer w postaci stringa ZADANIE_SERWERA;arg1;arg2;...;argn gdzie ZADANIE_SERWERA jest zdefiniowane jako stała. Ale mniejsza o to. Serwer odbiera ten ciąg i odpowiednio sobie dzieli, pierwszy argument to to co ma zrobić i wybiera sobie to w switchu. Kolejne to argumenty dla tego zadania. Kiedy wyślę jedno zadanie pod rząd to wszystko jest ok:

Klient wysyła:

ZADANIE_SERWERA;arg1;arg2;...;argn

Serwer odbiera:

ZADANIE_SERWERA;arg1;arg2;...;argn

Wszystko jest ok. Ale gdy tylko wysyłał bym to np. w pętli:

Klient wysyła 10 razy w pętli:

ZADANIE_SERWERA;arg1;arg2;...;argn

Serwer odbiera do zapełnienia bufora:

ZADANIE_SERWERA;arg1;arg2;...;argnZADA

i kolejne dane:

NIE_SERWERA;arg1;arg2;...;argn;ZADANIE_

itd.

Chodzi mi o to aby serwer odbierał strumień podzielony zgodnie z tym jak wysyłany jest przez klienta. Myślałem o jakimś ciągu, który by oznaczał koniec komendy ale go przecież też może podzielić. Nie chcę też czekać, aż serwer zwróci mi odpowiedź, że już sobie coś tam wczytał. Czy jest jakieś proste rozwiązanie tego problemu? Dodam jeszcze, że używam TcpClient, TcpListener i ich gniazda do wysyłania danych. Mam nadzieję, że to typowy problem bo kod jest dość duży i nie chcę zmuszać nikogo do analizowania go całego. Z góry dziękuję za pomoc.

:EDIT

Wyczytałem, że jednym z rozwiązań jest dodanie prefiksu z długością przesyłanego strumienia i odczytaniu tylko tylu bajtów ile wskazuje prefix, jednak nie wiem jak to zrobić.


(nnick) #2

Normalnie:

Typ pakietu(zadanie):rozmiar pakietu:argumenty

Pobierasz Receive() tyle bajtów ile potrzeba do odczytania typu pakietu i rozmiaru, a następnie pobierasz tyle bajtów Receive() ile jest Ci potrzebne (korzystając z pola rozmiaru pakietu).


(Musialowski Tomasz) #3

Raczej najpierw wielkość paczki w prefiksie. Próbowałem doklejać komenda.Length gdzie komenda była tablicą bajtów i odczytywać najpierw 4 bajty (wielkość int) ale dziwne rzeczy się zaczęły dziać, trudno mi je nawet opisać (tak jakby strumień nagle stawał się pusty, nie ma ani komendy, ani argumentów, ani tego inta).

:EDIT

Ok. Modyfikowałem nieco kod i wszystko śmiga. Zrobiłem to po swojemu:

public bool Apply(Socket client)

        {

            if (isRequest)

            {

                foreach (string arg in args)

                {

                    cmdString += ";" + arg;

                }

                cmdString = cmdString.Remove(0, 1);


                cmd = code.GetBytes(cmdString);


                try

                {

                    client.Send(BitConverter.GetBytes(cmd.Length));

                    client.Send(cmd);

                    return true;

                }

                catch

                {

                    return false;

                }

            }

            else

            {

                return false;

            }

        }

A po stronie serwera (tylko fragment kodu):

while (socket.Connected && isRunning && IsConnected(socket))

                {

                    if (socket.Available > 0)

                    {

                        if (packageSize == 0)

                        {

                            buf = new byte[4];

                            socket.Receive(buf);

                            packageSize = BitConverter.ToInt32(buf, 0);

                            addLogAsynch("[" + packageSize + "]");

                        }

                        else

                        {

                            buf = new byte[packageSize];

                            //komenda wczytana z bufora

                            string cmd = code.GetString(buf, 0, socket.Receive(buf));

                            packageSize = 0;

                            addLogAsynch("[" + cmd + "]");


                            //utworzenie obiektu komendy

                            Command response = new Command();


                            //zamiana lini komendy na nazwę akcji args[0] i argumenty - reszta tablicy

                            string[] args = cmdToArgs(cmd);

                        }

                    }

                }