U
U
unknown32021-08-11 15:20:18
C++ / C#
unknown3, 2021-08-11 15:20:18

Sockets, passing serialized objects, sharing in a stream. How?

There is a server, there is a client. They communicate with each other by messages. Let it be the Message class, rendered into a separate dll and connected to the server and client project.

Message class
[Serializable]
    public class Message {
        public int id;
        public string title;
        public string message;
    }

The essence of the client is simple, send 100 messages to the server, one after the other, without delay.
Client class
TcpClient client = new TcpClient("127.0.0.1",2255);//Коннект к серверу
            
            for (int i = 0; i < 100; i++){//Шлём 100 сообщений
                using (var stream = new NetworkStream(client.Client)){//Берем поток сокета
                    BinaryFormatter bf = new BinaryFormatter();
                    var msg = new Message(){//Создаем сообщение которое хотим послать на сервер
                        id = i,
                        message = "Hello world",
                        title = "My Title"
                    };

                    bf.Serialize(stream, msg);//сериализуем и пишем в поток
                }
                //Thread.Sleep(100); //Если добавить задержку то всё ок
            }

            Console.WriteLine("end");
            Console.ReadKey();


The essence of the server is a little more complicated, to receive these 100 messages with the asynchronous BeginReceive method and print them to the console.

Server class
static void Main(string[] args)
        {
            TcpListener listener = new TcpListener(IPAddress.Any, 2255);
            listener.Start(100);//Запускаем сервер, слушаем порт 2255
            Console.WriteLine("Listen 2255");
            while (true){
                new ClientSocket(listener.AcceptSocket());//Ждем подключения клиента, при подключении создаем новый класс ClientSocket
                Console.WriteLine("Socket connect");//Сокет подключен, ждем других клиентов
            }
        }

ClientSocket Server Class
public class ClientSocket{
        private Socket socket;
        private MemoryStream recieveData = new MemoryStream();
        public const int BufferSize = 256;
        byte[] buffer = new byte[BufferSize];

        public ClientSocket(Socket s){
            this.socket = s;
            socket.BeginReceive(this.buffer, 0, ClientSocket.BufferSize, 0, new AsyncCallback(ReceiveCallback), this);

        }
        private static void ReceiveCallback(IAsyncResult ar){
            ClientSocket state = (ClientSocket)ar.AsyncState;
            Socket client = state.socket;
            int bytesRead = 0;
            try{
                bytesRead = client.EndReceive(ar);//количество байт записанных в буффер

            }
            catch (SocketException se){
                if (se.ErrorCode == 10054){
                    Console.WriteLine("Клиент отключился");
                    return;
                }
                else
                    throw se;
            }
            if (bytesRead > 0){
                state.recieveData.Write(state.buffer, 0, bytesRead);//пишет в поток принятых байт
                Console.WriteLine("read {0} byte, available {1}", bytesRead, client.Available);
            }
            if (client.Available == 0 && state.recieveData.Length > 0){//если больше нет данных на прием и что-то есть в общем потоке 
                IFormatter formatter = new BinaryFormatter();
                state.recieveData.Seek(0, SeekOrigin.Begin);//указать потока на 0
                var m = (Message)formatter.Deserialize(state.recieveData);//десереализуем данные потока как Message
                state.recieveData.SetLength(0);//сбрасываем поток...
                Console.WriteLine("Recieve: {0} {1} {2}", m.id, m.title, m.message);
            }


            client.BeginReceive(state.buffer, 0, ClientSocket.BufferSize, 0,
                new AsyncCallback(ReceiveCallback), state);//ждем еще данные
        }
        ~ClientSocket()
        {
            if (recieveData != null)
            {
                recieveData.Close();
                recieveData.Dispose();
            }
        }
    }

If you throw one at a time or make a delay on the client before writing to the stream, then more or less ok, on the server we accept in order. If there is no delay, then it accepts everything and displays the last one.
Example of output on the server with a delay in the client
6113bffc13633219440294.png

Example of output on the server WITHOUT delay in the client
6113c01b14a97843760013.png

But in both cases, sometimes everything pops up on the server with an exception SerializationException: The end of the stream was detected before parsing was completed, or SerializationException: Invalid binary format of the input stream ...
In principle, the condition after which parsing begins is understandable, which is not entirely correct
if (client.Available == 0 && state.recieveData.Length > 0){//если больше нет данных на прием и что-то есть в общем потоке

and in general somehow everything is not right.
Perhaps it would be possible to come up with some kind of their own packages. Where, for example, the first 4 bytes contained the length of the packet, then the packet itself (serialized Message). Well, in this way we parse this entire stream of input bytes. Well, I want something more beautiful and correct.
Question: How to implement it correctly in order to receive data from clients without problems, even if the client writes them continuously in a stream?
Another important point, the client should not close the socket after sending, everything should hang on one socket, as the client connected to the server, it hangs and accesses as needed. In my example, communication is only with the server, in the future it will be in both directions.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
I
Ilya, 2021-08-11
@unknown3

Recently answered in this question
Why sometimes data over TCP reach correctly, and sometimes not?

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question