Надежный способ определения переменной длины сообщения при получении из сокета - PullRequest
0 голосов
/ 12 января 2019

У меня есть некоторый код Python для захвата изображений с камеры и отправки их на сервер C #.

При отправке сообщений от клиента - я предшествую данным размером сообщения, поэтому я знаю, сколько данных нужно извлечь из сокета на стороне сервера.

В большинстве случаев это работает хорошо, но иногда - сообщение не начинается с размера сообщения.

Я не уверен, почему это происходит, но я не могу понять, как с этим справиться.

Код Python:

while True:
    send_message("SEND_FRAME_DATA_HERE")

def send_message(message):

    message_size = len(message.encode())

    print (f"Message: {message_size} - {message}")

    my_socket.sendall(struct.pack(">L", message_size) + message.encode())

C #

private const int MESSAGE_CHUNK_SIZE = 4096;
private const int MESSAGE_PREFIX_SIZE = 4;

private void _receiveMessage(IAsyncResult ar)
{
    StateObject state = (StateObject)ar.AsyncState;
    Socket handler = state.workSocket;
    List<byte> messageBuffer = new List<byte>();
    byte[] tempBuffer = new byte[MESSAGE_CHUNK_SIZE];

    try
    {
        handler.EndReceive(ar);
        messageBuffer.AddRange(state.messageBuffer);

        while (true)
        {
            while (messageBuffer.Count < MESSAGE_PREFIX_SIZE)
            {
                handler.Receive(tempBuffer, 0, MESSAGE_CHUNK_SIZE, 0);
                messageBuffer.AddRange(tempBuffer);
            }

            int messageLength = _getMessageLength(messageBuffer);

            // Occasionally the four bytes determining message length
            // are read from what appears to be mid message
            if (messageLength > 20)
            {
                Console.Write("halp");
            }

            messageBuffer = messageBuffer.Skip(MESSAGE_PREFIX_SIZE).ToList();

            while (messageBuffer.Count < messageLength)
            {
                handler.Receive(tempBuffer, 0, StateObject.messageChunkSize, 0);
                messageBuffer.AddRange(tempBuffer);
            }

            var wholeMessage = messageBuffer.Take(messageLength).ToList();
            var messageString = Encoding.Default.GetString(wholeMessage.ToArray());

            Console.WriteLine(messageString);

            messageBuffer = messageBuffer.Skip(messageLength).ToList();
        }
    }
    catch (SocketException ex)
    {
        Console.WriteLine(ex.Message);
    }
}

private int _getMessageLength(List<byte> message)
{
    byte[] bytes = { message[3], message[2], message[1], message[0] };
    return BitConverter.ToInt32(bytes, 0);
}

Буфер сообщений должен выглядеть примерно так:

enter image description here

На хорошем пробеге:

enter image description here

На плохой трассе:

enter image description here

enter image description here

1 Ответ

0 голосов
/ 12 января 2019

Проблема, кажется, с этим кодом:

handler.Receive(tempBuffer, 0, StateObject.messageChunkSize, 0);
messageBuffer.AddRange(tempBuffer);

Socket.Receive() возвращает количество байтов, фактически прочитанных в tempBuffer. Вам нужно сохранить это значение, а затем использовать его для копирования правильного количества байтов в messageBuffer.

int bytesRead = handler.Receive(tempBuffer, 0, StateObject.messageChunkSize, 0);
messageBuffer.AddRange(tempBuffer.Take(bytesRead));
...