【发布时间】:2020-07-21 11:09:13
【问题描述】:
我创建了一个线程,可以读取手机发送的蓝牙消息。
当我意识到我无法安全地杀死那个线程时,我的问题就来了。到目前为止的方法是用户输入,它从该线程内调用exit(),当我将其移出该线程并进入主线程时,所有奇怪的事情都开始发生(检测到堆栈粉碎和分段错误)。显然,如果我有另一个用于 wifi 呼叫的线程或者只是不想完全退出所有内容,这种方法也不会真正很好地扩展。
这是我目前的代码。
将蓝牙相关内容传递给线程的结构:
typedef struct {
struct sockaddr_rc loc_addr;
struct sockaddr_rc rem_addr;
socklen_t opt;
char blu_buffer[1024];
int blu_sock;
} Bluetooth_stuff;
线程监听新连接:
void * BluetoothListiner(void * argv){
Bluetooth_stuff * bt_set = (Bluetooth_stuff * ) argv;
int bytes_read;
int local_client;
int option = 0;
listen(bt_set->blu_sock, 1);
memset(bt_set->blu_buffer, 0, sizeof(bt_set->blu_buffer));
while(true){
local_client = accept(bt_set->blu_sock, (struct sockaddr *)&((*bt_set).rem_addr), &((*bt_set).opt));
// read data from the client
bytes_read = read(local_client, bt_set->blu_buffer, sizeof(bt_set->blu_buffer));
if( bytes_read > 0 ) {
// process data
}
}
else{
// accept again?
}
// clear the buffer
memset(bt_set->blu_buffer, 0, sizeof(bt_set->blu_buffer));
// close connection
close(local_client);
usleep(10);
}
}
套接字配置:
void BluetoothSocketConfig(Bluetooth_stuff * bt_set){
// allocate socket
bt_set->blu_sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
bt_set->opt= sizeof(bt_set->rem_addr);
(*bt_set).loc_addr.rc_family = AF_BLUETOOTH;
(*bt_set).loc_addr.rc_bdaddr = *BDADDR_ANY;
(*bt_set).loc_addr.rc_channel = (uint8_t) 1;
// bind socket to port 1 of the first available
bind(bt_set->blu_sock, (struct sockaddr *)&(bt_set->loc_addr), sizeof(bt_set->loc_addr));
}
测试主要:
int main(int argc, char *argv[]){
Bluetooth_stuff bt_set;
BluetoothSocketConfig(&bt_set);
pthread_create(&blu_listiner, NULL, BluetoothListiner, bt_set);
while(1); // I modify that for testing to exit, having a global flag also gave me segmentation errors somehow
}
调用exit(1) 不会清理其他线程,而来自另一个线程的pthread_kill 或pthread_cancel 通常会导致进程仍在后台运行,从而阻止我再次运行应用程序而无需使用ps -A 手动杀死进程和kill -9 PROCESS。
基于此article,我认为最好的方法是在线程中引入if 语句并将其pthread_exit 本身置于遗忘状态。如:
if(external_exit){
ret2 = 200;
pthread_exit(&ret2);
}
现在由于read() 是一个阻塞调用,如果没有外部发生的事情,我永远无法到达if 语句。这个answer 建议使用超时来实现这一点,我按照answer 的语法在一段时间后设法从read() 突破。所以我在线程的主while循环之前添加了这个(我认为这应该只定义一次,而不是在每个循环中,虽然不确定因为找不到具体的例子)。
现在我的问题是它会在第一个循环中阻塞read(),但随后会像什么都没有一样跳过它并无限循环。我试图通过重新调用accept() listen() 和bind() 来“重新启用read()”,但没有成功。似乎使用select() 是首先检查是否有要阅读的内容的首选选项,但如手册中所述
"在 Linux 下,select() 可能会报告一个套接字文件描述符为“ready 用于阅读”,而随后的读取块。这 例如,当数据到达但经过检查时可能发生 校验和错误并被丢弃"
因此,即使使用它(我不确定它是否可以像在 tcp 套接字上一样在蓝牙套接字上工作),它仍然有可能仍然卡在read() 上而无法安全地杀死它。
1) 如何安全地终止该线程(或任何阻塞线程)?
2) select() 是否真的需要阻塞方法,因为我没有多个连接?
3) 初始超时后,在阻塞模式下重新启用read() 正确使用的程序是什么?
4) 除了初始化不同之外,所有套接字基本上都等效 unix/can/tcp/bluetooth 吗?
5) 有什么其他方法可以得到我想要的结果或我的部分逻辑上有严重缺陷吗?
感谢您提供的任何帮助,如果有人有蓝牙服务器线程实现的链接,我会很高兴看到它(或任何其他套接字 unix/can/tcp)。
【问题讨论】:
-
read()并不总是阻塞(尽管默认阻塞,这是 Unix 脑损伤,继续伤害每个人)。您可以将文件描述符设置为非阻塞,然后read()(或者更好地使用recvfrom()或recvmsg())不会阻塞。 -
这个问题太没有重点了。请把它剪成一个或分成几个。问题的当前标题is easy enough to answer,但正文在
exit(错误)、“重新启用”read(什么?)、错误使用pthread_kill和_cancel(错误)上引入了段错误,一个问题关于不同系列的套接字初始化等等。 -
accept 默认也是阻塞的,如果没有人尝试连接循环可能会卡住,所以相应地读取阻塞..
标签: c linux multithreading sockets bluez