Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 来自不同线程的同一套接字上的发送和接收不工作_C++_Sockets_Tcp_Send_Recv - Fatal编程技术网

C++ 来自不同线程的同一套接字上的发送和接收不工作

C++ 来自不同线程的同一套接字上的发送和接收不工作,c++,sockets,tcp,send,recv,C++,Sockets,Tcp,Send,Recv,我读到它应该可以同时从不同的线程中安全运行,但是我的程序有一些奇怪的行为,我不知道出了什么问题 我有与客户端套接字通信的并发线程 一个正在发送到套接字 一个做选择,然后从同一插座recv 当我还在发送时,客户端已经收到数据并关闭了套接字。 同时,我在这个套接字上执行select和recv,它返回0(因为它是关闭的),所以我关闭这个套接字。但是,发送尚未返回…由于我在此套接字上调用close,因此使用EBADF发送调用失败 我知道客户端已经正确地接收到了数据,因为我在关闭套接字后输出了数据,并且数

我读到它应该可以同时从不同的线程中安全运行,但是我的程序有一些奇怪的行为,我不知道出了什么问题

我有与客户端套接字通信的并发线程

  • 一个正在发送到套接字
  • 一个做选择,然后从同一插座recv
  • 当我还在发送时,客户端已经收到数据并关闭了套接字。 同时,我在这个套接字上执行select和recv,它返回0(因为它是关闭的),所以我关闭这个套接字。但是,发送尚未返回…由于我在此套接字上调用close,因此使用EBADF发送调用失败

    我知道客户端已经正确地接收到了数据,因为我在关闭套接字后输出了数据,并且数据是正确的。然而,在我这方面,我的send调用仍然返回一个错误(EBADF),所以我想修复它,使它不会失败

    这种情况并不总是发生。这种情况可能40%都会发生。我不在任何地方睡觉。我应该在发送或接收之间暂停吗

    下面是一些代码:

    发送:

    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;
    
    }