我是一个涉及C的新手,我的小项目是编写一个简单的SOCKS4代理。感谢这里的帮助,我到目前为止使用非阻塞套接字和poll()作为我的例程。但是在这一点上我似乎有两个问题:
如果传入的Socket rcvSocket被关闭,则传出的Socket dstSocket不会关闭,反之亦然。我不在循环中检查这个,但我不知道如何。我试过POLLHUP作为revents,但这似乎没有办法。正常检查似乎是recv()是否返回0,但是对于非阻塞套接字也是有效的吗?如果是这样,那怎么能用revents,我似乎无法弄清楚我会把它放在哪里,因为如果POLLIN | POLLPRI设置在我看来recv()永远不应该返回0?另外我不明白POLLIN和POLLPRI之间究竟有什么区别,在我看来只是检查“数据是否可供阅读”?
代理似乎适用于我使用netcat测试的连接。但是,如果我使用浏览器,它说(当我定位网站时)我是否要保存“二进制数据”。我检查了wireshark中的数据,并且从服务器收到的内容正好逐字节地正确转发到客户端。如果有人可能知道为什么这个程序会发生这种情况会很好:)
附上相关代码(谨防编程新手):
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);
}
}
答案 0 :(得分:1)
recv()
在干净的远程关闭时返回0 - 即使对于非阻塞套接字也是如此。在这种情况下,将返回POLLIN
- 远程端已关闭套接字的通知被视为“可读”事件。
您不应该使用POLLPRI
进行SOCKS / HTTP连接 - 它表示TCP“紧急数据”,这些协议不使用这些协议(实际上,根本没有使用)。
除了您的直接问题,您还需要做更多工作来实施可靠的代理:
poll()
,而不仅仅是一次。你写它的方式是繁忙循环,这通常不被认为是可以接受的做法。SIGPIPE
将signal(SIGPIPE, SIG_IGN);
的处置设置为忽略。这使您可以优雅地处理写入失败。send()
的结果。请注意,它可以写入少于您请求的数量 - 在这种情况下,您必须保持未发送的数据被缓冲,返回poll()
并尝试在POLLOUT
被引发时再次发送剩余数据插座。您只想在 未发送的数据时请求POLLOUT
,因此您需要确保在每次.events
来电之前正确设置poll()
。errno
或recv()
返回的值小于0,则应检查send()
EINTR
并忽略EWOULDBLOCK
;任何其他错误都应被视为连接失败。fds[0]
时,您应该调用shutdown(fds[1], SHUT_WR);
,反之亦然;只有当两个都已关闭(或发生连接失败)时,才应在两个文件描述符上调用close()
并完成。