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

时间:2018-08-15 20:06:50

标签: c sockets select openssl

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

我打算处理关闭连接的客户端的方法是由于套接字上的活动(从客户端关闭它)而导致select()中断,请参见为该套接字设置了fd,然后尝试从中读取-如果读取返回0,则关闭连接。但是,由于客户端关闭连接时fd_set并不总是返回,因此没有尝试检查select()并随后尝试从套接字读取的尝试。

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

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

1 个答案:

答案 0 :(得分:1)

  

我打算处理关闭连接的客户端的方法是由于套接字上的活动(从客户端关闭连接)而导致select()中断,请注意fd已设置对于该套接字,然后尝试从中读取-如果读取返回0,则关闭连接。但是,由于客户端关闭连接时select()并不总是返回,因此没有尝试检查fd_set并随后尝试从套接字读取的情况。

无论您是否使用SSL,select()都可以告诉您套接字何时可读(可以读取数据),并且优美闭包是可读的条件(随后的读取操作报告读取的0字节)。只有{strong>异常断开连接,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。客户端说“我完成了”是一种非常明显的方式,然后双方都可以关闭各自的套接字。

  

此解决方案的唯一问题是“停止代码”是可能在常规流量中出现的任意字节字符串,因为正在写入的普通数据可能包含随机字符串,而这些随机字符串可能包含“停止代码”。 / p>

这可能是您的通信协议设计不正确,或者您的代码未正确处理该协议。在经过适当设计和正确处理的协议中,不会有任何歧义。协议定义的各种命令之间必须有明确的区别。您的“停止代码”将是其他命令之一。一个命令中的随机数据不应被错误地视为另一命令。如果遇到此问题,则需要修复它。

相关问题