Socket.Send和Socket.BeginReceive之间的数据丢失

时间:2019-02-06 07:05:06

标签: c# network-programming data-loss

我有一个客户端服务器应用程序,我最近对其进行了修改,以使在第一个完成之前在同一Socket上再次调用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的模式之前,在由DataReceived委托处理数据之后,从外部对其调用ForceReceive()。当可以安全地重新输入原始行为时,将调用Activate而不是ForceReceive,然后从此恢复到原始行为。在我唯一可能的测试案例中,此阶段从未达到。服务器与客户端之间的消息交换正常进行了一次,然后当服务器不响应下一条消息时,客户端将锁定。

请注意,当我得到0-es(这是来自客户端的第二条消息)时,我们确实进入了在headerBuffer中接收到正确字节数的情况,只有12个都是0-es。

更新:因此,我使用了一个非常麻烦的解决方案来解决它,这在内存方面是浪费的,但是我想做一个更优雅,更有效的内存解决方案,所以如果有人发现了这一点,我仍然会很高兴看到这个谜题解决了。

0 个答案:

没有答案