c-如何通知服务器客户端正在关闭连接



我有一个服务器正在运行select()循环,当客户端从其一侧关闭连接时,该循环有时会继续阻塞。select()循环正确处理所有其他读/写操作,并在fd_set中设置正确的文件描述符,这让我相信服务器端的文件描述符设置没有问题。

我计划处理客户端关闭连接的方式是,由于套接字上的活动(从客户端关闭),select()中断,查看为该套接字设置了fd,然后尝试从中读取-如果读取返回0,则关闭连接。但是,由于select()并不总是在客户端关闭连接时返回,因此不会尝试检查fd_set并随后尝试从套接字读取。

作为一种变通方法,我实现了一个"停止代码",客户端在关闭连接之前将其写入服务器,该写入会导致select()断开,服务器读取"停止码"并知道关闭套接字。此解决方案的唯一问题是,"停止代码"是一个可能出现在常规流量中的任意字节字符串,因为正在写入的正常数据可能包含可能包含"停止码"的随机字符串。有没有更好的方法来处理客户端从一端关闭连接的问题?还是我所描述的方法是一般的"最佳实践"?

我认为我的问题与OpenSSL有关,因为有问题的连接是一个OpenSSL隧道,它是集合中唯一给我带来问题的文件描述符。

我计划处理客户端关闭连接的方式是,由于套接字上的活动(从客户端关闭),select()中断,查看是否为该套接字设置了fd,然后尝试从中读取-如果读取返回0,则关闭连接。然而,由于select()并不总是在客户端关闭连接时返回,因此不会尝试检查fd_set并随后尝试从套接字读取。

无论您是否使用SSL,select()都可以告诉您套接字何时可读(有数据可供读取),而优美的闭包是可读条件(后续读取操作报告0字节读取)。只有异常断开连接select()才能报告(除非您使用exceptfds参数,但即使这样也不能始终保证)。处理异常断开连接的最佳方法是简单地在自己的代码中使用超时。如果您有一段时间没有从客户端接收到数据,请关闭连接。如果客户端想要保持连接,则必须定期发送数据,例如一个小的心跳命令。

此外,在使用OpenSSL时,如果您使用的是较旧的ssl_...API函数(ssl_new()ssl_set_fd()ssl_read()ssl_write()等),请确保您不是在任何时候都盲目调用select(),而是仅在OpenSSL通知您时才调用它(当SSL读/写操作报告SSL_ERROR_WANT_(READ|WRITE)错误时)。在这个领域,许多OpenSSL新手往往会犯同样的错误。他们试图在预先存在的套接字逻辑之上使用OpenSSL,该套接字逻辑在读取数据之前等待可读通知。这是使用ssl_...API的错误方法。您需要要求OpenSSL无条件地执行读/写操作,然后如果它需要等待新数据到达或待处理数据发送出去,它会告诉您,然后您可以相应地调用select(),然后再重试SSL读/写操作。

另一方面,如果您正在使用较新的bio_...API函数(bio_new()bio_read()bio_write()等),则可以控制底层的套接字I/O,而不让OpenSSL为您管理它,因此您可以使用select()(或任何其他所需的套接字API)执行任何操作。

作为一种变通方法,我实现了一个"停止代码",客户端在关闭连接之前将其写入服务器,该写入会导致select()中断,服务器读取"停止码"并知道关闭套接字。

这是许多Internet协议中非常常见的方法,无论是否使用SSL。对于客户端来说,这是一种非常独特和明确的方式来说"我完成了",然后双方都可以关闭各自的套接字。

此解决方案的唯一问题是"停止代码"是一个可能出现在常规流量中的任意字节字符串,因为正在写入的正常数据可能包含可能包含"停止码"的随机字符串。

那么,要么是您的通信协议设计不正确,要么是代码没有正确处理协议。在一个正确设计和处理的协议中,不会有任何这样的歧义。协议定义的各种命令之间需要有明确的区别。您的"停止代码"将是其他命令中的一个这样的命令。一个命令中的随机数据不应被错误地视为不同的命令。如果你遇到了这个问题,你需要解决它。

最新更新