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

时间:2012-02-26 06:27:47

标签: c# multithreading sockets

我已经研究了这么多个小时,尝试了很多不同的事情来解决这个问题,但是我被困住了。我正在尝试创建一个可以从多个线程访问的异步套接字。它往往运行正常,直到我有几个线程试图接近同一时间发送。发送时没有错误,唯一的问题是当EndReceive()尝试读取数据时,它返回0字节,套接字错误是“成功”。我读过这意味着这是一个优雅的关闭,服务器没有更多的数据要发送,但我连接的服务器类型应该永远不会发送所有数据,它不是那种类型的服务器。应始终存在要读取的数据,不应该关闭。

我用来发送一些System.Threading.Timers但我读过的线程并不完全是线程安全的。我也尝试过System.Timers.Timer,但结果相同。我也尝试过只使用1个线程一次发送大量数据包,之间睡眠时间为100毫秒,我会得到相同的结果。

接收代码如下。对于发送,我已经尝试过定期阻止Send和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中的asyc套接字代码,只是为了看看是否有一些奇怪的事情,但在VS2008中,它仍然给了我bytesRead == 0.对于阻塞代码,它没有' t断开我:

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,但它们也会断开连接。

3 个答案:

答案 0 :(得分:0)

套接字不是线程安全的。见here

您应该同步对套接字的访问权限,或让一个线程处理所有这些(我认为它更干净,并且还可以使调试更容易)。

答案 1 :(得分:0)

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

我可能应该在我的问题中提到过,但我并不认为它是相关的。

答案 2 :(得分:0)

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