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 代理中的非阻塞套接字和poll()怪癖-新手_C_Sockets_Nonblocking - Fatal编程技术网

C 代理中的非阻塞套接字和poll()怪癖-新手

C 代理中的非阻塞套接字和poll()怪癖-新手,c,sockets,nonblocking,C,Sockets,Nonblocking,我是一个涉足C语言的新手,我的小项目是编写一个简单的SOCKS4代理。多亏了这里的帮助,我才得以在例程中使用非阻塞套接字和poll()。然而,在这一点上,我似乎有两个问题: 如果传入套接字rcvSocket关闭,则传出套接字dstSocket不会关闭,反之亦然。我没有在循环中检查这个,但我不知道如何检查。我试着用波尔胡普作为复仇者,但这似乎不起作用。正常的检查似乎是recv()是否返回0,但这对非阻塞套接字也有效吗?如果是这样的话,这对revents是如何起作用的,我似乎不知道该放在哪里,因为如

我是一个涉足C语言的新手,我的小项目是编写一个简单的SOCKS4代理。多亏了这里的帮助,我才得以在例程中使用非阻塞套接字和poll()。然而,在这一点上,我似乎有两个问题:

  • 如果传入套接字rcvSocket关闭,则传出套接字dstSocket不会关闭,反之亦然。我没有在循环中检查这个,但我不知道如何检查。我试着用波尔胡普作为复仇者,但这似乎不起作用。正常的检查似乎是recv()是否返回0,但这对非阻塞套接字也有效吗?如果是这样的话,这对revents是如何起作用的,我似乎不知道该放在哪里,因为如果设置了POLLIN | POLLPRI,我觉得recv()永远不会返回0?另外,我不明白POLLIN和POLLPRI之间的确切区别是什么,在我看来,这两种情况都只是一个“数据可供读取”的检查

  • 代理似乎适用于我用netcat测试的连接。然而,如果我使用浏览器,它会显示(当我瞄准一个网站时)我是否要保存“二进制数据”。我检查了wireshark中的数据,从服务器接收到的数据被正确地逐字节转发到客户端。如果有人知道为什么这个项目会发生这种情况,那就太好了:)

  • 附上相关代码(注意编程新手):

    recv()。在这种情况下,将返回POLLIN
    ——远程端已关闭套接字的通知被视为“可读”事件

    SOCKS/HTTP连接不需要使用
    POLLPRI
    ,它表示TCP“紧急数据”,这些协议不使用它(或者实际上使用得太多)


    除了直接的问题,您还需要做更多的工作来实现可靠的代理:

  • 您需要对每个循环调用
    poll()
    ,而不是只调用一次。您编写它的方式是忙于循环,这通常被认为是不可接受的
  • 您应该将
    SIGPIPE
    的配置设置为与
    signal(SIGPIPE,SIG_IGN)一起忽略。这允许您优雅地处理写入失败
  • 您应该检查
    send()
    的结果。请注意,它的写入量可能小于您请求的量—在这种情况下,您必须保留未发送的数据缓冲区,返回
    poll()
    ,并在套接字上引发
    POLLOUT
    时再次尝试发送剩余的数据。如果还有未发送的数据,您只需要请求
    POLLOUT
    ,因此您需要确保在每次
    poll()
    调用之前正确设置
    .events
  • 如果
    recv()
    send()
    返回的值小于0,则应检查
    errno
    EINTR
    EWOLDBLOCK
    应忽略;任何其他错误都应视为连接失败
  • 当一个插座关闭时,不应立即关闭两个方向-必须支持非对称关闭。这意味着,当您看到远端已关闭
    fds[0]
    时,应调用
    shutdown(fds[1],shutw),反之亦然;只有在两个文件描述符都已关闭(或发生连接故障)时,才应在两个文件描述符上调用
    close()
    ,然后完成

  • 感谢您提供的信息,我添加了if(recvMsgSize==0)break;对于sndMsgSize也是一样,现在就像连接关闭的魅力一样。非常感谢您的投入,我显然意识到我的“代理”并不是真正的生产价值,它只是一个让我在学习C时无所事事的项目。我将尝试实现您所说的,但我想我必须先阅读一下。好的一面是,浏览器不使用socks代理似乎是firefox的一个缺陷,因为chrome和任何其他支持socks的应用程序都可以正常工作。@user912877:我认为firefox很有可能不使用它,因为第5点。firefox在代理版本中根本没有关闭套接字。我检查了wireshark中的数据,它得到了1:1的副本。当然你是对的,我现在做close()的方式非常糟糕。我认为我现在这样做可能会让服务器发送一些东西,关闭连接,我的代理也会在交付数据之前终止客户端连接。(除了我应该做的事情:-)虽然这里可能不是真正的问题,但最终您需要处理它:使用非阻塞套接字send()可能不会发送您请求它发送的所有字节。另外,您是否已将浏览器配置为将您的程序用作SOCKS代理?如果是这样,您需要实现SOCKS协议,浏览器才能正常工作。注意。注意| and&运算符优先级
    fds[1]。revents&POLLIN | POLLPRI
    (fds[1]。revents&POLLIN)| POLLPRI
    ,您需要
    fds[1]。revents&POLLIN | POLLPRI
    (或者就像Caff所建议的那样,删除POLLPRI显然是一个firefox bug,chrome和其他socks应用程序可以工作。好的一点是,我不知道,谢谢。
     fds[1].fd = dstSocket;
     fds[0].fd = rcvSocket;
     fds[1].events = POLLIN | POLLPRI | POLLHUP;
     fds[0].events = POLLIN | POLLPRI | POLLHUP;
    
     timer = poll(fds, 2, timeout_msecs); /* i dont use this yet */
    
     fcntl(rcvSocket, F_SETFL, O_NONBLOCK);
     fcntl(dstSocket, F_SETFL, O_NONBLOCK);
    
     while (1 == 1)
     {
         if (fds[0].revents & POLLIN | POLLPRI)
         {
            recvMsgSize = recv(rcvSocket, rcvBuffer, RCVBUFSIZE, 0);
            if (recvMsgSize > 0) {send(dstSocket, rcvBuffer, recvMsgSize, 0);}
         }
         if (fds[1].revents & POLLIN | POLLPRI)
         { 
            sndMsgSize = recv(dstSocket, sndBuffer, RCVBUFSIZE, 0);
            if (sndMsgSize > 0) { send(rcvSocket, sndBuffer, sndMsgSize, 0);}
         }         
    
         if ((fds[0].revents & POLLHUP) || (fds[1].revents & POLLHUP))
         {
            close(rcvSocket);
            close(dstSocket);
         }
    }