rke*_*erm 8 c++ network-programming tcp cross-platform
我有一个小应用程序,它通过网络将文件发送到位于Windows操作系统上的代理.
当此应用程序在Windows上运行时,一切正常,通信正常,文件全部复制成功.
但是,当这个应用程序在Linux上运行时(RedHat 5.3,接收器仍然是Windows) - 我在Wireshark中看到TCP Zero Window和TCP Window Full的网络跟踪消息每1-2秒出现一次.然后,代理会在几分钟后关闭连接.
Windows - Linux代码几乎相同,非常简单.唯一的非平凡操作是setsockopt,SO_SNDBUF和值0xFFFF.删除此代码没有帮助.
有人可以帮我解决这个问题吗?
编辑:添加发送代码 - 它看起来它正确处理部分写入:
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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
提前致谢.
Rob*_*nes 12
没有看到你的代码,我将不得不猜测.
在TCP中获得Zero窗口的原因是因为接收器的recv缓冲区中没有空间.
有很多种方法可以实现.此问题的一个常见原因是当您通过LAN或其他相对较快的网络连接发送时,一台计算机明显快于另一台计算机.作为一个极端的例子,假设你有一台3Ghz计算机通过千兆以太网尽快发送到运行1Ghz cpu的另一台机器.由于发送方的发送速度比接收方能够读取的速度快得多,因此接收方的recv缓冲区将填满,导致TCP堆栈向发送方通告零窗口.
现在,如果他们还没有准备好处理这个问题,这会在发送方和接收方都造成问题.在发送端,如果您使用非阻塞I/O,这可能导致发送缓冲区填满并调用发送阻止或失败.在接收方面,您可能会在I/O上花费大量时间,以至于应用程序无法处理任何数据并且看起来被锁定.
编辑
从你的一些答案和代码来看,听起来你的应用程序是单线程的,你出于某种原因试图进行非阻塞发送.我假设您在代码的其他部分中将套接字设置为非阻塞.
一般来说,我会说这不是一个好主意.理想情况下,如果您担心应用程序挂在a上send(2),则应在套接字上设置长超时,setsockopt并使用单独的线程进行实际发送.
SO_RCVTIMEO和SO_SNDTIMEO指定接收或发送超时,直到报告错误.该参数是struct timeval.如果输入或输出功能在这段时间内阻塞,并且数据已被发送或接收,则该功能的返回值将是传输的数据量; 如果没有传输数据并且已达到超时,则返回-1,并将errno设置为EAGAIN或EWOULDBLOCK,就好像套接字被指定为非阻塞一样.如果超时设置为零(默认值),则操作永远不会超时.
您的主线程可以将每个文件描述符推送到queue使用说升级互斥锁进行队列访问,然后启动1-N个线程以使用具有发送超时的阻塞I/O进行实际发送.
你的发送功能应该是这样的(假设你正在设置超时):
// 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;
}
Run Code Online (Sandbox Code Playgroud)
该MSG_NOSIGNAL标志确保通过写入已被对等方关闭或重置的套接字来杀死您的应用程序.有时I/O操作会被信号中断,并且检查EINTR允许您重新启动send.
通常,您应该doSend使用大小的数据块调用循环TCP_MAXSEG.
在接收端,您可以在单独的线程中使用超时编写类似的阻塞recv函数.
我尝试禁用 Nagle 算法(使用 TCP_NODELAY),不知何故,它有所帮助。传输速率更高,TCP 窗口大小未满或重置。奇怪的是,当我改变窗口大小时,它没有任何影响。
谢谢。