为什么 read() 在 shutdown/close 之后没有在原始套接字上返回?
Why is read() not returning after shutdown/close on raw socket?
我有一个线程在这样的循环中从原始 HCI 套接字读取消息:
void* loop_hci (void* args) {
params_hci_t* params = (params_hci_t*) args;
int result_hci = 0;
uint8_t* buf_hci = calloc(1, HCI_EVENT_MAX_LENGTH);
while (!poll_end()) {
result_hci = read(params->hci_sock, buf_hci, HCI_EVENT_MAX_LENGTH);
if (result_hci > 0) {
// ... do stuff with the received data
}
}
ancs_pdebug("HCI loop shutting down...");
return NULL;
}
poll_end()
函数工作正常,符合预期。它 returns 0 直到收到 SIGINT 信号,之后它 returns 1.
在主线程中,我这样创建套接字:
hci_sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
还有线程:
ph->hci_sock = hci_sock;
pthread_create(&t_hci, NULL, &loop_hci, ph);
然后在一段时间后像这样调用关闭(在主线程中):
shutdown(hci_sock, SHUT_RD);
我假设 read() 应该 return 在我调用 shutdown() 之后,我在 L2CAP 套接字的不同线程中使用相同的方法并且它工作正常。但事实并非如此。我在主线程中的 pthread_join(t_hci, NULL)
调用从未 returns.
插座工作正常。我可以从中读取消息。我也尝试调用关闭(我在线程结束后调用),但结果是一样的。
可能是什么问题,或者我的假设有误?
您遇到的问题可能是因为您处理套接字和多线程的方式。您不应该将 shutdown 与原始套接字一起使用。它适用于连接的套接字,我真的从未尝试过使用原始套接字或数据包套接字。但是有一个建议是杀死或取消一个我会避免的线程,因为杀死一个线程是蛮力的,并且您有无法以有序方式处理资源的风险。相反,您应该以不同的方式设计您的解决方案:
一种可能性是将非阻塞套接字与 select、轮询或 epoll 一起使用。您将有一个在 select/poll/epoll 中等待的循环,直到套接字准备就绪或超时。当你想关闭一个套接字时,你只需设置一个变量,例如 endLoop 为 true ,表示轮询线程应该离开循环。参见 select 的人:http://man7.org/linux/man-pages/man2/select.2.html
例如,在 Thrift 服务器中使用的其他可能性是使应用程序使用特定代码向自身发送特殊消息。侦听线程解除阻塞并读取此特殊消息,该消息指示它应检查是否必须完成侦听(例如,通过读取变量值)。
因此,当他的监听线程正在监听或读取时,主线程会将变量 endLoop 变量设置为 true 以指示它完成,或者它将执行 (2) 以取消阻塞监听线程。
这两个选项是解决您的问题的优雅方法。使用非阻塞套接字 (1) 或通过向自身发送消息来解除阻塞的读取或侦听线程。
我有一个线程在这样的循环中从原始 HCI 套接字读取消息:
void* loop_hci (void* args) {
params_hci_t* params = (params_hci_t*) args;
int result_hci = 0;
uint8_t* buf_hci = calloc(1, HCI_EVENT_MAX_LENGTH);
while (!poll_end()) {
result_hci = read(params->hci_sock, buf_hci, HCI_EVENT_MAX_LENGTH);
if (result_hci > 0) {
// ... do stuff with the received data
}
}
ancs_pdebug("HCI loop shutting down...");
return NULL;
}
poll_end()
函数工作正常,符合预期。它 returns 0 直到收到 SIGINT 信号,之后它 returns 1.
在主线程中,我这样创建套接字:
hci_sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
还有线程:
ph->hci_sock = hci_sock;
pthread_create(&t_hci, NULL, &loop_hci, ph);
然后在一段时间后像这样调用关闭(在主线程中):
shutdown(hci_sock, SHUT_RD);
我假设 read() 应该 return 在我调用 shutdown() 之后,我在 L2CAP 套接字的不同线程中使用相同的方法并且它工作正常。但事实并非如此。我在主线程中的 pthread_join(t_hci, NULL)
调用从未 returns.
插座工作正常。我可以从中读取消息。我也尝试调用关闭(我在线程结束后调用),但结果是一样的。
可能是什么问题,或者我的假设有误?
您遇到的问题可能是因为您处理套接字和多线程的方式。您不应该将 shutdown 与原始套接字一起使用。它适用于连接的套接字,我真的从未尝试过使用原始套接字或数据包套接字。但是有一个建议是杀死或取消一个我会避免的线程,因为杀死一个线程是蛮力的,并且您有无法以有序方式处理资源的风险。相反,您应该以不同的方式设计您的解决方案:
一种可能性是将非阻塞套接字与 select、轮询或 epoll 一起使用。您将有一个在 select/poll/epoll 中等待的循环,直到套接字准备就绪或超时。当你想关闭一个套接字时,你只需设置一个变量,例如 endLoop 为 true ,表示轮询线程应该离开循环。参见 select 的人:http://man7.org/linux/man-pages/man2/select.2.html
例如,在 Thrift 服务器中使用的其他可能性是使应用程序使用特定代码向自身发送特殊消息。侦听线程解除阻塞并读取此特殊消息,该消息指示它应检查是否必须完成侦听(例如,通过读取变量值)。
因此,当他的监听线程正在监听或读取时,主线程会将变量 endLoop 变量设置为 true 以指示它完成,或者它将执行 (2) 以取消阻塞监听线程。
这两个选项是解决您的问题的优雅方法。使用非阻塞套接字 (1) 或通过向自身发送消息来解除阻塞的读取或侦听线程。