代码之家  ›  专栏  ›  技术社区  ›  rkellerm

如何避免接收端的TCP零窗口/TCP窗口满?

  •  8
  • rkellerm  · 技术社区  · 14 年前

    当这个应用程序在Windows上运行时,一切正常,通信正常,文件复制成功。

    但是,当这个应用程序在Linux上运行时(redhat5.3,接收者仍然是Windows),我在Wireshark网络跟踪中看到TCP Zero Window和TCP Window Full的消息每1-2秒出现一次。然后,代理在几分钟后关闭连接。

    编辑: 添加发送代码-看起来它可以正确处理部分写入:

    int totalSent=0;
    while(totalSent != dataLen)
    {
        int bytesSent 
            = ::send(_socket,(char *)(data+totalSent), dataLen-totalSent, 0);
    
        if (bytesSent ==0) {
            return totalSent;
        }
        else if(bytesSent == SOCKET_ERROR){
    #ifdef __WIN32
            int errcode = WSAGetLastError();
            if( errcode==WSAEWOULDBLOCK ){
    #else
                if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
    #endif
                }
                else{
                    if( !totalSent ) {
                        totalSent = SOCKET_ERROR;
                    }
                    break;
                }
            }
            else{
                totalSent+=bytesSent;
            }
        }
    }
    

    4 回复  |  直到 14 年前
        1
  •  12
  •   Robert S. Barnes Antoni    14 年前

    没有看到你的代码我只能猜测。

    TCP中出现零窗口的原因是因为接收器的接收缓冲区中没有空间。

    有很多种方法可以做到这一点。此问题的一个常见原因是,当您通过LAN或其他相对快速的网络连接发送数据时,一台计算机的速度明显快于另一台计算机。作为一个极端的例子,假设你有一台3Ghz的计算机,通过千兆以太网以尽可能快的速度发送到另一台运行1Ghz cpu的机器。由于发送方的发送速度远远快于接收方的读取速度,因此接收方的recv缓冲区将被填满,从而导致TCP堆栈向发送方公布一个零窗口。

    如果发送端和接收端都没有准备好处理这个问题,那么这可能会导致问题。在发送端,如果您使用的是非阻塞I/O,则这可能会导致发送缓冲区填满,并导致发送的调用阻塞或失败。在接收端,您可能在I/O上花费了太多时间,以至于应用程序没有机会处理它的任何数据,并且看起来像是被锁定了。

    编辑

    一般来说,我认为这不是一个好主意。理想情况下,如果您担心应用程序挂在 send(2) setsockopt

    看到了吗 socket(7)

    所以你的时间和时间 指定在报告错误之前的接收或发送超时。这个 该函数将是 数据传输;如果没有数据 已传输并且超时已被取消 设置为EAGAIN或EWOULDBLOCK 如果套接字被指定为 无阻塞。如果超时设置为 零(默认值),然后执行操作

    主线程可以将每个文件描述符推入 queue

    send函数应该如下所示(假设设置了超时):

    // blocking send, timeout is handled by caller reading errno on short send
    int doSend(int s, const void *buf, size_t dataLen) {    
        int totalSent=0;
    
        while(totalSent != dataLen)
        {
            int bytesSent 
                = send(s,((char *)data)+totalSent, dataLen-totalSent, MSG_NOSIGNAL);
    
            if( bytesSent < 0 && errno != EINTR )
                break;
    
            totalSent += bytesSent;
        }
        return totalSent;
    }
    

    MSG_NOSIGNAL 标志确保您的应用程序不会因为写入已被对等方关闭或重置的套接字而终止。有时I/O操作会被信号中断,并检查 EINTR 允许您重新启动 send

    doSend 在一个循环中,数据块 TCP_MAXSEG 大小。

        2
  •  1
  •   João Pinto    14 年前

    使用TCP套接字开发时的一个常见错误是关于read()/write()行为的错误假设。

    执行读/写操作时,必须检查返回值,它们可能没有读/写请求的字节数,通常需要一个循环来跟踪并确保传输了整个数据。

        3
  •  0
  •   janm    14 年前

    最可能的问题是,您的代码中有一个错误,无法正确处理部分读取或部分写入。众所周知,Linux和Windows之间的TCP是有效的。

        4
  •  0
  •   rkellerm    14 年前

    我试图禁用Nagle的算法(使用TCP\u NODELAY),但不知怎么的,它起了作用。 传输速率更高,TCP窗口大小未满或未重置。 奇怪的是,当我改变窗口大小时,它没有任何影响。

    非常感谢。