直到 socket.Close 才收到 Windows socket.Send 数据

Posted

技术标签:

【中文标题】直到 socket.Close 才收到 Windows socket.Send 数据【英文标题】:Windows socket.Send data isn't received until socket.Close 【发布时间】:2011-01-17 04:03:06 【问题描述】:

我正在开发一个异步接受 TCP 连接 (BeginAccept/EndAccept) 和数据 (BeginReceive/EndReceive) 的服务器应用程序。该协议要求在找到 EOM 字符时发送一个 ACK​​,然后再发送下一条消息。接受和接收正在工作,但发送应用程序没有收到 ACK(同步发送)。

    private void _receiveTransfer(IAsyncResult result)
    
        SocketState state = result.AsyncState as SocketState;
        int bytesReceived = state.Socket.EndReceive(result);

        if (bytesReceived == 0)
        
            state.Socket.Close();
            return;
        

        state.Offset += bytesReceived;
        state.Stream.Write(state.Buffer, 0, bytesReceived);

        if (state.Buffer[bytesReceived - 1] == 13)
        
            // process message
            Messages.IMessage message = null;
            try
            
                var value = state.Stream.ToArray();

                // do some work
                var completed = true;

                if (completed)
                
                    // send positive ACK
                    var ackMessage = string.Format(ack, message.TimeStamp.ToString("yyyyMMddhhmm"), message.MessageType, message.Id, "AA", message.Id);
                    var buffer = ASCIIEncoding.ASCII.GetBytes(ackMessage);
                    int bytesSent = state.Socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
                
                else
                
                    // send rejected ACK
                    var ackMessage = string.Format(ack, message.TimeStamp.ToString("yyyyMMddhhmm"), message.MessageType, message.Id, "AR", message.Id);
                    state.Socket.Send(ASCIIEncoding.ASCII.GetBytes(ackMessage));
                
            
            catch (Exception e)
            
                // log exception


                // send error ACK
                if (message != null)
                
                    var ackMessage = string.Format(ack, DateTime.Now.ToString("yyyyMMddhhmm"), message.MessageType, message.Id, "AE", message.Id);
                    state.Socket.Send(ASCIIEncoding.ASCII.GetBytes(ackMessage));
                
            
        

        state.Socket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, new AsyncCallback(_receiveTransfer), state);
    

state.Socket.Send 返回正确的字节数,但在处理套接字之前不会接收到数据。

欢迎提出建议。

【问题讨论】:

Flush函数吗?我猜套接字数据正在被缓冲,等待更多数据...... string.Format() 调用看起来很奇怪。但我怀疑真正的问题出在电线的另一端。 【参考方案1】:

你给它多长时间?网络堆栈可以缓冲,这可能会延迟传输。来自MSDN:

为了提高网络效率, 底层系统可能会延迟 传输直到一个显着 收集的传出数据量。 成功完成发送 方法意味着底层 系统有空间来缓冲你的 用于网络发送的数据。

您可能想尝试使用IOControl 方法进行刷新。

编辑

实际上,IOControl 刷新会杀死缓冲区。您可能想查看Two Generals Problem,看看您的协议是否存在一些固有问题。

【讨论】:

【参考方案2】: 你不应该做任何与异步完成例程同步的事情。在负载下,您最终可能会从线程池中劫持所有 IO 完成线程并严重损害性能,直至并包括完全 IO 死锁。所以不要从异步回调中同步发送 ACK。 使用前导码的协议和格式比使用终止符的协议和格式更容易管理。 IE。在固定大小的消息头中写入消息的长度,而不是检测终止符 \0x13。当然,如果协议一开始就在您的控制之下,这适用。

至于您的问题,您没有指定与您发布的相同代码是否也在客户端。

【讨论】:

很遗憾我无法更改协议,并且客户端是用于测试协议合规性的验证应用。 创建时是否设置了任何套接字选项,例如 SO_SNDBUF?你能看到正在发送的 ACK 字节,使用像 microsoft.com/downloads/… 这样的网络监视器吗?【参考方案3】:

尝试设置 TCP_NODELAY 套接字选项

【讨论】:

【参考方案4】:

您是否将套接字上的NoDelay 属性设置为true?当设置为false(默认值)时,数据在发送前最多缓冲 200 毫秒。原因是通过限制发送的数据包数量来减少网络流量。将NoDelay 设置为true 将强制更快地发送数据。

【讨论】:

这仍然发生在一些设置了自适应中断的适配器上。比如英特尔。

以上是关于直到 socket.Close 才收到 Windows socket.Send 数据的主要内容,如果未能解决你的问题,请参考以下文章

socket.shutdown 与 socket.close

linux下socket编程中close()函数??

if(socket!=null)socket.close();题目要求是通信结束,关闭socket

当 socket.close() 抛出 Java 时我能做啥?

`io_context.stop()` 与 `socket.close()`

socket close