异步套接字在一次发送大量数据包时总是读取0字节

本文关键字:数据包 0字节 读取 套接字 一次 异步 | 更新日期: 2023-09-27 18:25:45

我研究了很多小时,尝试了很多不同的方法来解决这个问题,但我被卡住了。我正在尝试创建一个异步套接字,它将从多个线程进行访问。它往往运行良好,直到我有几个线程试图同时发送。发送没有错误,唯一的问题是当EndReceive()尝试读取数据时,它返回0个字节,套接字错误为"success"。我读到这意味着这是一次优雅的关闭,服务器没有更多的数据可发送,但我连接的服务器类型不应该发送所有数据,它不是那种类型的服务器。应该总是有要读取的数据,而且不应该关闭。

我过去发送的线程有几个System.Threading.Timers,但我读到它们并不完全是线程安全的。我也尝试过使用System.Timers.Timer,但结果是一样的。我还尝试过只使用一个线程一次发送大量数据包,在这两个线程之间睡眠100毫秒,我会得到同样的结果。

接收代码如下。对于发送,我只尝试了常规阻止发送和BeginSend,但没有什么不同。

void Receive()
    {
        try
        {
            if (_datacb == null)
                _datacb = new AsyncCallback(OnRecvData);
            byte[] buffer = new byte[FBufferSize];
            FSocket.BeginReceive(buffer, 0, FBufferSize, SocketFlags.None, _datacb, buffer);
        }
        catch (SocketException ex)
        {
            OnSocketError(ex);
        }
    }
    void OnRecvData(IAsyncResult ar)
    {
        try
        {
            SocketError err;
            int bytesRead = FSocket.EndReceive(ar, out err);
            if (bytesRead == 0)
            {
                throw new SocketException();
            }
            byte[] buffer = ar.AsyncState as byte[];
            FReceived.AddBytes(buffer, bytesRead);
            ByteBuffer message = FReceived.GetNextMessage();
            while (message != null)
            {
                Process(message);
                message = FReceived.GetNextMessage();
            }
        }
        catch (SocketException ex)
        {
            OnSocketError(ex);
        }
        Receive();
    }

我以为可能是服务器因为发送速度太快而关闭了套接字,但实际上我有一个在VS2008中制作的阻塞版本,我可以使用50个不同的定时器,每秒发送1个数据包,而且我没有断开连接。我在VS2010中尝试了几乎相同的代码,bytesRead为0。我还在VS2008中尝试了asycsocket代码,只是想看看是否发生了什么奇怪的事情,但在VS2008中,它仍然给了我bytesRead==0。对于不会断开我连接的阻塞代码:

tcpClient = new TcpClient(host, 443);
networkStream = tcpClient.GetStream();
mReader = new BinaryReader(networkStream);
mWriter = new BinaryWriter(networkStream);
receiveMessage = new Thread(new ThreadStart(ReceiveMessages));
receiveMessage.Start();
private void ReceiveMessages()
    {
        while (true)
        {
            if ((tcpClient != null) && (tcpClient.Connected))
            {
                if (tcpClient.Available >= 4)
                {
                    if (!isConnected) isConnected = true;
                    try
                    {
                        ByteBuffer message = new ByteBuffer();
                        message.AddBytes(mReader.ReadBytes(4));
                        int mSize = message.ReadInt();
                        message.AddBytes(mReader.ReadBytes(mSize - 4));
                        processor.Process(message);
                    }
                    catch (Exception ex)
                    {
                        Print("Recv Msg: " + ex.Message);
                    }
                }
                Thread.Sleep(1);
            }
}

发送:

mWriter.Write(send);
mWriter.Flush();

对它不太满意,因为它使用了大量的CPU。我尝试了其他方法来在不使用那么多CPU的情况下实现这样的阻塞套接字,但它们也往往会断开连接。

异步套接字在一次发送大量数据包时总是读取0字节

套接字不是线程安全的。参见此处

您应该同步对套接字的访问,或者让一个线程处理所有这些(我认为这更干净,也会使调试更容易)。

显然,它与实际发送无关,而是与正在发送的数据有关。服务器期望每个数据包都发送一个特定的指纹,这是一种加密类型。每次获取指纹时,都必须调整一个值。我对实际发送进行了线程锁定,但它需要在计算指纹的部分进行锁定。有些数据包发送时带有错误的指纹,因为它们的计算顺序不正确,所以服务器以这种方式断开了连接。

我可能应该在我的问题中提到这一点,但我认为这无关紧要。

Socket可以读取0个字节。这很正常。您要做的是再次读取,直到获得完整的数据包。当人们期望套接字读取的字节数与服务器发送的字节数相同时,这是一个常见的错误。但事实上,数据可能会变得支离破碎,你只会得到数据块。因此,您的代码应该准备好从套接字组装数据块,因为数据碎片迫在眉睫。