C++ 来自不同线程的同一套接字上的发送和接收不工作
我读到它应该可以同时从不同的线程中安全运行,但是我的程序有一些奇怪的行为,我不知道出了什么问题 我有与客户端套接字通信的并发线程C++ 来自不同线程的同一套接字上的发送和接收不工作,c++,sockets,tcp,send,recv,C++,Sockets,Tcp,Send,Recv,我读到它应该可以同时从不同的线程中安全运行,但是我的程序有一些奇怪的行为,我不知道出了什么问题 我有与客户端套接字通信的并发线程 一个正在发送到套接字 一个做选择,然后从同一插座recv 当我还在发送时,客户端已经收到数据并关闭了套接字。 同时,我在这个套接字上执行select和recv,它返回0(因为它是关闭的),所以我关闭这个套接字。但是,发送尚未返回…由于我在此套接字上调用close,因此使用EBADF发送调用失败 我知道客户端已经正确地接收到了数据,因为我在关闭套接字后输出了数据,并且数
while(true)
{
// keep sending until send returns 0
n = send(_sfd, bytesPtr, sentSize, 0);
if (n == 0)
{
break;
}
else if(n<0)
{
cerr << "ERROR: send returned an error "<<errno<< endl; // this case is triggered
return n;
}
sentSize -= n;
bytesPtr += n;
}
while(true)
{
//继续发送,直到发送返回0
n=发送(_sfd,bytesPtr,sentSize,0);
如果(n==0)
{
打破
}
否则,如果(n我必须承认,我很惊讶你像你一样经常看到这个问题,但是当你处理线程时,这总是一种可能性
您最终会进入内核,将数据附加到其中的套接字缓冲区,因此很可能会有上下文切换,可能会切换到系统中的另一个进程。与此同时,内核可能会非常快地缓冲和传输数据包。我猜您正在本地网络上测试,所以另一个d接收数据并关闭连接,然后非常快地将相应的FIN发送回您的终端。这一切都可能发生在发送计算机仍在运行其他线程或进程时,因为本地以太网网络上的延迟非常低
现在FIN来了-您的接收线程最近没有做很多事情,因为它一直在等待输入。因此,许多调度系统会将其优先级提高很多,并且很有可能下一个运行它(您没有指定您正在使用的操作系统,但这至少可能发生在Linux上,例如)。此线程由于其零读取而关闭套接字。在此之后不久,发送线程将重新唤醒,但可能内核在从阻止的send()
返回并返回EBADF
之前注意到套接字已关闭
现在,这只是关于确切原因的猜测——其中很大程度上取决于您的平台。但您可以看到这是如何发生的
最简单的解决方案可能是在发送线程中也使用poll()
,但要等待套接字变为写就绪而不是读就绪。显然,您还需要等待,直到有任何缓冲数据要发送-如何发送取决于哪个线程缓冲数据。poll()
调用将通过使用POLLHUP
标记连接来检测连接何时关闭,您可以在尝试send()之前检测到该标记
作为一般规则,在确定发送缓冲区已完全刷新之前,不应关闭套接字-只有在send()命令
调用已返回,表明所有剩余的数据都已用完。我过去处理过,在读取为零时检查发送缓冲区,如果不是空的,则设置“关闭”在您的情况下,发送线程会在刷新所有内容后将其作为关闭的提示。这很重要,因为如果远程端使用shutdown()进行半关闭
然后,即使它可能仍在读取,您也会得到零读取。不过,您可能不关心一半关闭,在这种情况下,您的上述策略是可以的
最后,我个人会避免发送和接收线程的麻烦,只需要一个线程就可以同时执行这两个任务-这或多或少就是select()
和poll()的要点
,允许单个执行线程处理一个或多个文件句柄,而不必担心执行会阻塞和耗尽其他连接的操作。发现了问题。这是我的循环。请注意,这是一个无限循环。当我没有更多的发送空间时,我的sentSize为0,但我仍然会循环尝试发送此时,另一个线程已关闭此线程,因此我对0字节的发送调用返回一个错误
我将循环更改为在sentSize为0时停止循环,从而解决了此问题!请添加一些代码。可能存在与您怀疑的错误无关的错误。谢谢,我将添加代码。我将始终为套接字提供引用计数。@CaptainObvlious您的意思是什么?保留引用如何防止此错误?我想d和RECV还好吗?谢谢你的回答。你描述的问题似乎很有可能,但我不认为我遇到了,因为它似乎需要一个非常具体的情况,我得到的错误经常发生。此外,我需要使用多线程,因为这是学校的作业规范。结束标志注释听起来非常有用,我想补充一点,如果它是为了一个作业,但这只是为了标记哈哈。@cartroo-当recv是一个阻塞调用时,你怎么能在同一个套接字和同一个线程上进行recv和send呢?poll()和select()听起来像是完全用于多个套接字。@joemanici都send()
和recv()
不一定会阻止操作。首先,它们只在无法取得任何进展时才会阻止,否则它们会返回部分成功—select()
和poll()
的目的是检测何时可能成功(即。
while(true)
{
memset(bufferPointer,0,sizeLeft);
n = recv(_sfd,bufferPointer,sizeLeft, 0);
if (debug) cerr << "Receiving..."<<sizeLeft<<endl;
if(n == 0)
{
cerr << "Connection closed"<<endl; // this case is triggered
return n;
}
else if (n < 0)
{
cerr << "ERROR reading from socket"<<endl;
return n;
}
bufferPointer += n;
sizeLeft -= n;
if(sizeLeft <= 0) break;
}