You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ubuntu 20.04下C++ RFCOMM蓝牙监听socket未监听问题排查

RFCOMM监听程序listen调用后,getsockopt检测显示未处于监听状态的问题解决

问题描述

在Ubuntu 20.04系统上使用VSCode编写C++代码,实现可接收蓝牙连接请求的RFCOMM监听程序。已调用listen函数且返回成功,但通过getsockopt的SO_ACCEPTCONN选项检测时,显示socket未处于监听状态。已确认蓝牙适配器正常、本地MAC地址正确,寻求问题原因及解决办法。

代码实现

#include <iostream>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <unistd.h>
#include <cstring>
#include <cstdio>

using namespace std;

int main() {
    struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 }; 
    char buf[1024] = { 0 }; 
    int s;
    int client, bytes_read; 
    socklen_t opt = sizeof(rem_addr); 

    s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); 
    loc_addr.rc_family = AF_BLUETOOTH;
    str2ba("12:34:56:78:90:12", &loc_addr.rc_bdaddr);
    loc_addr.rc_channel = (uint8_t) 1;

    if (s == -1) {
        perror("socket");
        return 1;
    }

    if (bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr)) == -1) {
        perror("bind");
        return 1;
    }
    if (listen(s, 3) == -1) {
        perror("listen");
        return 1;
    } else {
        cout << "listening" << endl;
    }

    struct sockaddr_rc name = { 0 };
    socklen_t len = sizeof(name);
    if (getsockname(s, (struct sockaddr *)&name, &len) == -1) {
        perror("getsockname");
        return 1;
    }

    char addr[18] = { 0 };
    ba2str(&name.rc_bdaddr, addr);
    cout << "Local address: " << addr << endl;
    cout << "Local port: " << (int)name.rc_channel << endl;

    // 第二次调用getsockname,未检查返回值
    getsockname(s, (struct sockaddr *)&name, &opt);
    ba2str( &name.rc_bdaddr, buf );
    cout << "local mac adress: " << buf << endl;

    // 检测监听状态
    int val;
    socklen_t len2 = sizeof(val);
    if (getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, &val, &len2) == -1) {
        perror("getsockopt");
        return 1;
    } else if(val) {
        printf("getsockopt() succeeded: val=%d, len2=%d\n", val, len2);
    } else if(getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, &val, &len2) == 0) {
        printf("socket %d is not listening\n", s);
    }

    opt = sizeof(rem_addr);
    client = accept(s, (struct sockaddr *)&rem_addr, &opt);
    ba2str( &rem_addr.rc_bdaddr, buf );
    if(client < 0){
        cout << "error accepting connection" << endl;
    } else {
        cout << "accepted connection from " << buf << endl;
    }

    memset(buf, 0, sizeof(buf));

    // 读取客户端数据
    bytes_read = read(client, buf, sizeof(buf));
    if( bytes_read > 0 ) {
        printf("received [%s]\n", buf);
    } 

    // 关闭连接
    close(client); 
    close(s); 
    return 0;
}

程序运行输出

listening
Local address: 12:34:56:78:90:12
Local port: 1
local mac adress: 12:34:56:78:90:12
socket 3 is not listening

问题原因分析

  • SO_ACCEPTCONN选项对RFCOMM套接字的兼容性问题
    SO_ACCEPTCONN是POSIX标准中针对TCP套接字设计的选项,用于检测套接字是否处于监听状态。但蓝牙RFCOMM套接字属于AF_BLUETOOTH地址族,部分Linux系统的蓝牙套接字实现并未支持该选项,导致即使listen调用成功,getsockopt返回的val仍为0,出现误判。

  • 代码逻辑冗余且错误
    在监听状态检测部分,当第一次getsockopt返回val=0时,重复调用getsockopt属于冗余操作,且逻辑分支设计混乱。实际上只需一次调用即可判断状态,重复调用不会改变结果。

  • 未检查第二次getsockname的返回值
    代码中第二次调用getsockname时未校验返回结果,若该调用失败可能导致变量异常,但这并非监听状态误判的直接原因。

解决方法

  • 忽略SO_ACCEPTCONN的检测结果,以listen返回值为准
    只要listen函数返回成功(未触发perror("listen")),即可认定套接字已进入监听状态,无需依赖SO_ACCEPTCONN的检测结果。

  • 使用系统工具验证监听状态
    可以通过ss命令查看蓝牙端口的监听状态,确认RFCOMM端口是否正常监听:

    ss -a --bluetooth
    

    若输出中包含LISTEN状态的rfcomm端口,说明监听已成功。

  • 修正代码逻辑
    简化监听状态检测的代码,移除冗余的getsockopt调用:

    int val;
    socklen_t len2 = sizeof(val);
    if (getsockopt(s, SOL_SOCKET, SO_ACCEPTCONN, &val, &len2) == -1) {
        perror("getsockopt");
        return 1;
    }
    if (val) {
        printf("socket %d is listening\n", s);
    } else {
        printf("SO_ACCEPTCONN not supported, but listen succeeded\n");
        // 此处可直接执行accept逻辑,无需额外判断
    }
    
  • 修复代码中的换行符问题
    将代码中所有非标准换行符替换为\n,避免输出格式混乱。

内容的提问来源于stack exchange,提问作者MGThegathering

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 17:48:09