Данные, потерянные между Socket.Send и Socket.BeginReceive - PullRequest
0 голосов
/ 06 февраля 2019

У меня есть клиент-серверное приложение, которое я недавно изменил, чтобы BeginRecieve вызывался в другой раз на том же сокете до завершения первого.Теперь первое сообщение получено отлично, но на следующем буфере для заголовка (см. Код ниже) заполнены большие жирные нули вместо данных, которые я должен получать.

public class Client
    {
    Socket sock;
    Message message;
    MessageHeader header;
    byte[] headerBuffer = new byte[MessageHeader.HEADER_SIZE];
    byte[] dataBuffer;

    bool activeReceiver = false;

    public delegate void DisconnectedHandler(Client c);
    public event DisconnectedHandler Disconnected;

    public delegate void DataReceivedHandler(Client c, Message m);
    public event DataReceivedHandler DataReceived;

    public Client(Socket sock)
        {
        this.sock = sock;
        sock.BeginReceive(headerBuffer, 0, MessageHeader.HEADER_SIZE, SocketFlags.None, ReceiveCallback, null);
        }

    internal void Activate()
        {
        activeReceiver = true;
        sock.BeginReceive(headerBuffer, 0, MessageHeader.HEADER_SIZE, SocketFlags.None, ReceiveCallback, null);
        }

    internal void ForceReceive()
        {
        if (!activeReceiver)
            {
            sock.BeginReceive(headerBuffer, 0, MessageHeader.HEADER_SIZE, SocketFlags.None, ReceiveCallback, null);
            }
        }

    private void ReceiveCallback(IAsyncResult result)
        {
        int received;
        try
            {
            received = sock.EndReceive(result);
            if (received != MessageHeader.HEADER_SIZE && Disconnected != null)
                {
                Disconnected(this);
                return;
                }
                else if (received == MessageHeader.HEADER_SIZE)
                {
                header = new MessageHeader(headerBuffer);

                if (header.size > 0)
                    {
                    dataBuffer = new byte[header.size];
                    sock.Receive(dataBuffer);
                    message = new Message(header, dataBuffer);
                    if (DataReceived != null)
                        {
                        DataReceived(this, message);
                        }
                    }

                message = null;
                headerBuffer = new byte[MessageHeader.HEADER_SIZE];
                dataBuffer = null;
                if (sock != null && activeReceiver)
                    {
                    sock.BeginReceive(headerBuffer, 0, MessageHeader.HEADER_SIZE, SocketFlags.None, ReceiveCallback, null);
                    }
                }
            }
        catch (SocketException sex)
            {
            switch (sex.SocketErrorCode)
                {
                case SocketError.ConnectionAborted:
                case SocketError.ConnectionReset:
                    if (Disconnected != null)
                        {
                        Disconnected(this);
                        }
                    break;
                }
            }
        }

А MessageHeader - это простой маленький класс:

public class MessageHeader
    {
    public static int HEADER_SIZE = 12;
    public int type, size, source;

    public MessageHeader(int type, int size, int source)
        {
        this.type = type;
        this.size = size;
        this.source = source;
        }

    public MessageHeader(byte[] raw)
        {
        if (raw.Length != HEADER_SIZE)
            {
            type = -1;
            size = -1;
            source = -1;
            }

        type = BitConverter.ToInt32(raw, 0);
        size = BitConverter.ToInt32(raw, 4);
        source = BitConverter.ToInt32(raw, 8);
        }

    public byte[] toBytes()
        {
        byte[] bytes = new byte[HEADER_SIZE];
        BitConverter.GetBytes(type).CopyTo(bytes, 0);
        BitConverter.GetBytes(size).CopyTo(bytes, 4);
        BitConverter.GetBytes(source).CopyTo(bytes, 8);
        return bytes;
        }
    }  

То, как это работало (и работало безупречно), было activeReceiver, не было чем-то, BeginReceive вызывалось бы в конце каждого обратного вызова.Теперь новая модификация - переменная activeReceiver вместе с методами ForceReceive и Activate.До тех пор, пока не будет безопасно перевести клиента обратно в режим автоматического вызова BeginReceive, ForceReceive () вызывается для него извне после обработки данных делегатом DataReceived.Когда становится безопасным повторно вводить исходное поведение, вместо ForceReceive вызывается Activate, и с этого момента он возвращается к исходному поведению.В моем единственном возможном тестовом примере этот этап никогда не достигается.Существует 1 обмен сообщениями между сервером и клиентом, который проходит нормально, затем клиент блокируется, когда сервер не отвечает на следующее сообщение.

Обратите внимание, что когда я получаю 0-е (чтоэто второе сообщение от клиента в целом) мы вводим случай, когда в headerBuffer получено правильное количество байтов, только все 12 из них являются 0-овами.

ОБНОВЛЕНИЕ: Так что я обошел это сдействительно сложное решение, которое расточительно с точки зрения памяти, но хотело бы сделать более изящное и более эффективное решение для памяти, так что если кто-нибудь это поймет, я все равно буду рад увидеть разгадку этой тайны.

...