Я реализую программу на C ++, которая связывается через Bluetooth RFCOMM с несколькими различными роботами через Bluetooth. В этом сценарии связи роботы являются серверами, и моя программа состоит из нескольких клиентов.
Я реализовал два потока: 1. ConnectThread подключается к нескольким серверам через разные сокеты. Соединения устанавливаются одно за другим. Если соединение потеряно, оно будет восстановлено позже. 2. DataSendThread отправляет данные на все подключенные серверы через их сокеты.
Проблема: Если DataSenThread в данный момент отправляет данные через сокет A и новое соединение устанавливается устанавливается в потоке подключения через сокет B, затем сокет A не может быть записан в течение нескольких секунд. Но для моего приложения мне нужно постоянное соединение с роботами.
Я использую несколько интерфейсов Bluetooth. Приложение требует, чтобы соединения с серверами были распределены по нескольким интерфейсам Bluetooth. Таким образом, перед «подключением» к сокету для него назначается определенный c интерфейс Bluetooth с использованием «bind». Каждый сокет, назначенный интерфейсам Bluetooth, имеет свой собственный порт.
Все объекты, используемые обоими потоками, передаются как std :: shared_ptr.
Из-за сложности я показываю программу как псевдо код:
ConnectThread:
1. search for available bt-servers ('hci_inquiry')
2. for each available bt-servers:
if available bt-server is new
create a new bt-server object
if bt-server is not connected
2.1 choose the bt-interfaces with the fewest connections
2.2 create a socket (socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM))
2.3 make socket nonblocking ('fcntl')
2.4 bind the choosen bt-interfaces to the socket ('bind')
2.5 connect socket nonblocking ('connect')
2.6 wait for the connection to be established ('select')
DataSendThread:
1. for each connected bt-server:
1.1 if socket writeable ('select')
1.1.1 send data ('send')
У кого-нибудь есть идеи, почему подключение одного разъема влияет на существующее подключение другого разъема?
Большое спасибо!
Редактировать :
Проблема возникает, только если два сервера подключены через один и тот же интерфейс Bluetooth. Если они подключены через разные интерфейсы Bluetooth, помехи отсутствуют. Я предполагаю, что интерфейс Bluetooth очень занят во время процесса подключения и поэтому игнорирует уже существующие подключения других сокетов. Возможно ли это?
Edit2: Вот минимальный пример, который вызывает проблему:
#include <iostream>
#include <thread>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <unistd.h>
#include <cstring>
struct sockaddr_rc server1_addr = { 0 };
struct sockaddr_rc server2_addr = { 0 };
int socketServer1;
int socketServer2;
int portToLocalBtAdapter = 1;
int toggleData = 0;
bool stopThread = false;
int connectToServer(struct sockaddr_rc* server_addr){
char serverAddrStr[19];
ba2str(&(server_addr->rc_bdaddr),serverAddrStr);
//valid RFCOMM Ports: 1..31
if(portToLocalBtAdapter > 31)
return -1;
//local bluetooth adapter
struct sockaddr_rc local_addr = { 0 };
bdaddr_t bdaddr_any = {{0, 0, 0, 0, 0, 0}};
local_addr.rc_family = AF_BLUETOOTH;
local_addr.rc_bdaddr = bdaddr_any;
local_addr.rc_channel = (uint8_t) portToLocalBtAdapter;
int sock;
if((sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0)
return -1;
if(bind(sock, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0)
return -1;
socklen_t sOfServer = sizeof(*server_addr);
std::cout << "connect begin" << std::endl;
if(connect(sock, (struct sockaddr *)server_addr, sOfServer) < 0)
return -1;
std::cout << "connect end" << std::endl;
//check if there is an socket error (e.g. "Host is down")
int error = 0;
socklen_t len = sizeof(error);
getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len);
if(error){
errno = error;
return -1;
}
std::cout << "Connected socket " << sock << " to server "<< serverAddrStr << " using port " << +local_addr.rc_channel << " of local bt adapter." << std::endl;
//every server connection gets its own port to local bt adapter
portToLocalBtAdapter++;
return sock;
}
void sendThread(){
while(!stopThread){
fd_set wset;
FD_ZERO(&wset);
FD_SET(socketServer1, &wset);
struct timeval tval;
tval.tv_sec = 1; //1s timeout
tval.tv_usec = 0;
int ret;
//check if socketServer1 is writeable within 1s
if((ret = select(socketServer1+1, NULL, &wset, NULL, &tval)) > 0){
//tval contains the remaining time until timeout
double blockingTime = 1.0 - ((double)tval.tv_sec+((double)tval.tv_usec)/1000000.0);
if(blockingTime > 0.001)
std::cout << "send on socket " << socketServer1 << " blocked for " << blockingTime <<"s"<< std::endl;
//data to send, red and blue LED blinking
uint8_t redData[] = {255,255,2,32,160,5,255,0,0,0,57};
uint8_t blueData[] = {255,255,2,32,160,5,0,0,255,0,57};
uint8_t* dataToSend;
if(toggleData++ % 2)
dataToSend = redData;
else
dataToSend = blueData;
if(send(socketServer1, dataToSend, 11 , 0) < 0)
std::cout << "send failed on socket " << socketServer1 << std::endl;
}
else
std::cout << "socket " << socketServer1 << " not writeable, ret = " << ret << std::endl;
usleep(33000); //33ms
}
}
int main(int argc, char** argv){
server1_addr.rc_family = AF_BLUETOOTH;
str2ba("68:86:E7:09:71:87", &server1_addr.rc_bdaddr );
server1_addr.rc_channel = (uint8_t) 1;
server2_addr.rc_family = AF_BLUETOOTH;
str2ba("68:86:E7:07:4C:19", &server2_addr.rc_bdaddr );
server2_addr.rc_channel = (uint8_t) 1;
std::cout << "Try to connect server 1" << std::endl;
while((socketServer1 = connectToServer(&server1_addr)) < 0){
std::cout << "connect to server 1 failed: "<< std::strerror(errno) << std::endl;
usleep(1000000); //1s
}
std::cout << "start sending data to server 1" << std::endl;
std::thread sendThrd(&sendThread);
usleep(3000000); //3s
std::cout << "Try to connect server 2" << std::endl;
while((socketServer2 = connectToServer(&server2_addr)) < 0){
std::cout << "connect to server 2 failed: "<< std::strerror(errno) << std::endl;
}
usleep(3000000); //3s
stopThread = true;
sendThrd.join();
std::cout << "stopped sending data to server 1" << std::endl;
close(socketServer1);
close(socketServer2);
return 0;
}
Вывод:
Try to connect server 1
connect begin
connect end
Connected socket 4 to server 68:86:E7:09:71:87 using port 1 of local bt adapter.
start sending data to server 1
Try to connect server 2
connect begin
send on socket 4 blocked for 0.060255s
send on socket 4 blocked for 0.100941s
send on socket 4 blocked for 0.035571s
send on socket 4 blocked for 0.039356s
send on socket 4 blocked for 0.105597s
send on socket 4 blocked for 0.03558s
send on socket 4 blocked for 0.039496s
send on socket 4 blocked for 0.105447s
send on socket 4 blocked for 0.035594s
send on socket 4 blocked for 0.039317s
send on socket 4 blocked for 0.105614s
send on socket 4 blocked for 0.035575s
send on socket 4 blocked for 0.039457s
send on socket 4 blocked for 0.105492s
send on socket 4 blocked for 0.035557s
send on socket 4 blocked for 0.105619s
send on socket 4 blocked for 0.104394s
send on socket 4 blocked for 0.104443s
send on socket 4 blocked for 0.035453s
send on socket 4 blocked for 0.039335s
send on socket 4 blocked for 0.105745s
send on socket 4 blocked for 0.035549s
send on socket 4 blocked for 0.03926s
send on socket 4 blocked for 0.033224s
send on socket 4 blocked for 0.026101s
send on socket 4 blocked for 0.03556s
send on socket 4 blocked for 0.039261s
send on socket 4 blocked for 0.105614s
send on socket 4 blocked for 0.035519s
send on socket 4 blocked for 0.03935s
send on socket 4 blocked for 0.10563s
send on socket 4 blocked for 0.054211s
connect end
Connected socket 6 to server 68:86:E7:07:4C:19 using port 2 of local bt adapter.
stopped sending data to server 1
Сокет часто недоступен для записи до 105 мс в течение 2-3 с (при подключении другого сокета). Интересно, что поведение не меняется, даже если portToLocalBtAdapter не увеличивается. Это приведет к тому, что все сокеты будут взаимодействовать с адаптером Bluetooth через один и тот же порт, верно?