我写了一个转发代理。我将它用于Windows和Linux。我确实需要根据操作系统进行更改。但是,我一直看到一些加薪条件。大多数情况下,我认为他们是因为我在猜测哪个是最后一个数据包(FIN sigal)时的误解。目前我选择套接字。无论哪个套接字发出信号,我都会读取()。如果读取返回0,那么我假设它是一个FIN数据包,我关闭该套接字。可以发生我的read()给出非零值。但该数据包确实包含FIN(我认为它可能会发生)。所以,虽然它们已经关闭,但我并没有关闭它们。 我不确定代理如何检测哪个套接字已关闭?或者是已建立连接上的最后一个数据包。
我的代码如下:
我有100个fds,我已经接受了客户。我将它们存储为数组sock_array[total_size]
。
select(copy_of_sock_array,timeout)
for(int cnt=0;cnt<total_size;cnt++)
{
if(FD_ISSET(sock_array[cnt],sock_array))
{
ret = recv(sock_array[cnt],buffer,len);
if(ret<=0){
/*This must be a FIN packet */
/* Close corresponding socket which is opened with outer world */
close(/*corresponding socket*/);
}
}
}
这看起来不错吗?
由于
答案 0 :(得分:1)
你需要进行非阻塞读取,并继续读取套接字,直到你得到一个表明你应该停止阅读的返回值。
ssize_t r = 0;
for (;;) {
r = recv(sock, buf, bufsz, MSG_DONTWAIT);
if (r <= 0) {
if (r < 0 && errno == EINTR) {
continue;
}
break;
}
/* ... handle data in buf .. */
}
if (r < 0) {
if (errno == EAGAIN) {
/* ... wait in select again ... */
} else {
/* ... handle error ... */
}
} else {
/* got FIN */
}
请注意,仅仅因为收到FIN并不一定意味着应该关闭连接。 FIN仅表示不再发送数据,但对等方可能仍愿意接受更多数据。这可能发生在HTTP中,其中客户端只需要一个响应,因此它在请求后提供FIN。它仍然希望收到回复。
您的代理可能有两个套接字,比如sock1和sock2。因此,在sock1上接收FIN应该意味着在已经在其上排队的任何数据(并且镜像也是真的)之后将该指示转发到sock2上。您可以使用shutdown
转发FIN。
shutdown(sock2, SHUT_WR);
当从sock1和sock2收到FIN时,你可以在两个套接字上调用close
。
所以解决你的问题。
我的read()会给出非零值。但该数据包确实包含FIN(我认为它可能会发生)。
是的,这可能会发生。这就是为什么你继续阅读,直到你得到停止的迹象。嗯,从技术上讲,你没有必要。如果您有每个连接的公平性问题,您可以推迟,直到您处理了一些其他连接。但是,在进入选择等待之前,您需要回到它并完成阅读。
所以,虽然他们已经关闭但我没有关闭一些套接字。我不确定代理如何检测哪个套接字已关闭?或者是已建立连接上的最后一个数据包。
正如我所描述的那样,作为一个(透明)代理,一旦你在它上面转发了FIN并且在它上面收到了FIN,就可以安全地关闭套接字。如果您不是透明代理,则可以使用不同的规则集,因为在这种情况下,您确实是客户端的服务器。因此,只要您实施的应用程序协议允许,您就可以关闭套接字。
答案 1 :(得分:0)
套接字具有明确定义的行为。如果您收到数据并且之后关闭连接,则需要两个read()。第一个将返回数据,第二个将返回0,表示连接结束。
在syscall返回0之前,您必须始终阅读。
你不需要非阻塞读取来检测这个!