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

标签 c sockets select openssl

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

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

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

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

最佳答案

The way I planned on handling the client closing the connection was to have the select() break due to activity on the socket (closing it from the client-side), see that the fd was set for that socket, and then try to read from it - and if the read returned 0, then close the connection. However, because the select() doesn't always return when the client side closes the connection, there is no attempt to check the fd_set and subsequently try to read from the socket.

无论您是否使用 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)。

As a workaround, I implemented a "stop code" that the client writes to the server just before closing the connection, and this write causes the select() to break and the server reads the "stop code" and knows to close the socket.

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

The only problem with this solution is the "stop code" is an arbitrary string of bytes that could potentially appear in regular traffic, as the normal data being written can contain random strings that could potentially contain the "stop code".

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

关于c - 如何通知服务器客户端正在关闭连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51865694/

相关文章:

c - getmaxyx() 代码:: block 中的错误

mysql - 获取刚刚发布的记录 ID 的最简单方法

mysql - 如何从MySQL中的列值大于b表计数值的表中仅选择行

javascript - Jquery:设置基于 <select> 属性检查的 <option>

c - 在C中合并两个文件

检查子 PID 是否正在等待 Scanf

Windows 上的 C 模块需要 Linux 系统包含

java - 套接字写入与磁盘写入的性能

C#套接字问题

node.js - nginx 作为网络服务器,包括。 socket.io 和 node.js/ws ://400 Bad Request