Разъем Bluetooth RFCOMM недоступен для записи, пока подключается другой разъем (Linux, blueZ, C ++) - PullRequest
1 голос
/ 06 января 2020

Я реализую программу на 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 через один и тот же порт, верно?

...