• 蓝牙编程扫盲 RFCOMM sockets


    建立和使用RFCOMM连接可以归结为我们已经知道如何用于TCP/IP编程的套接字编程技术。唯一的区别是套接字寻址结构不同,我们对多字节整数的字节排序使用了不同的函数。例4-2和例4-3展示了如何使用RFCOMM套接字建立连接,传输一些数据,并断开连接。为了简单起见,客户端被硬编码为连接到“01:23:45:67:89:AB”。

    注意:不能在一个机器上运行下面的代码,普通的网络通信可以在一台机器上运行server和client,但蓝牙不行。

    rfcomm-server.c 代码:

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/rfcomm.h>
    
    int main(int argc, char **argv)
    {
        struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
        char buf[1024] = { 0 };
        int s, client, bytes_read;
        socklen_t opt = sizeof(rem_addr);
    
        // allocate socket
        s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    
        // bind socket to port 1 of the first available 
        // local bluetooth adapter
        loc_addr.rc_family = AF_BLUETOOTH;
        loc_addr.rc_bdaddr = *BDADDR_ANY;
        loc_addr.rc_channel = (uint8_t) 1;
        bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
    
        // put socket into listening mode
        listen(s, 1);
    
        // accept one connection
        client = accept(s, (struct sockaddr *)&rem_addr, &opt);
    
        ba2str( &rem_addr.rc_bdaddr, buf );
        fprintf(stderr, "accepted connection from %s
    ", buf);
        memset(buf, 0, sizeof(buf));
    
        // read data from the client
        bytes_read = read(client, buf, sizeof(buf));
        if( bytes_read > 0 ) {
            printf("received [%s]
    ", buf);
        }
    
        // close connection
        close(client);
        close(s);
        return 0;
    }
    

    rfcomm-client.c 代码:

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/rfcomm.h>
    
    int main(int argc, char **argv)
    {
        struct sockaddr_rc addr = { 0 };
        int s, status;
        char dest[18] = "01:23:45:67:89:AB";
    
        // allocate a socket
        s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    
        // set the connection parameters (who to connect to)
        addr.rc_family = AF_BLUETOOTH;
        addr.rc_channel = (uint8_t) 1;
        str2ba( dest, &addr.rc_bdaddr );
    
        // connect to server
        status = connect(s, (struct sockaddr *)&addr, sizeof(addr));
    
        // send a message
        if( status == 0 ) {
            status = write(s, "hello!", 6);
        }
    
        if( status < 0 ) perror("uh oh");
    
        close(s);
        return 0;
    }
    

    上面的代码对于有经验的网络程序员来说,这其中的大部分应该看起来很熟悉。与因特网编程一样,首先用socket系统调用分配一个套接字。用AF_BLUETOOTH 代替AF-INET,用BTPROTO-RFCOMM代替IPPROTO_TCP 。由于RFCOMM提供与TCP相同的传递语义,SOCK_STREAM仍然可以用于套接字类型。

    Addressing structures

    要建立与另一个蓝牙设备(传入或传出)的RFCOMM连接,请创建并填充struct sockaddr_rc寻址结构。与TCP/IP中使用的结构struct sockaddr_in 一样,寻址结构指定传出连接或侦听套接字的详细信息。

    struct sockaddr_rc {
    	sa_family_t	rc_family;
    	bdaddr_t	rc_bdaddr;//相当于tcp的ip地址
    	uint8_t		rc_channel;//相当于端口号
    };
    

    rc_family字段指定套接字的寻址系列,并且始终是AF_BLUETOOTH。对于传出连接,rc_bdaddr和rc_channel分别指定要连接的蓝牙地址和端口号。对于侦听套接字,rc_bdaddr指定要使用的本地蓝牙适配器,并且通常设置为bdaddr_ANY以指示任何本地蓝牙适配器都可以接受。对于侦听套接字,rc_channel指定要侦听的端口号。

    A note on byte ordering

    由于蓝牙处理的是从一台机器到另一台机器的数据传输,因此对多字节数据类型使用一致的字节顺序是至关重要的。与使用big-endian格式的网络字节排序不同,Bluetooth字节排序是little-endian,即先传输最低有效字节。BlueZ提供了四个方便的函数来在主机和蓝牙字节顺序之间进行转换。

    unsigned short int htobs( unsigned short int num );//主机转成蓝牙;s代表short类型
    unsigned short int btohs( unsigned short int num );//蓝牙转成主机;s代表short类型
    unsigned int htobl( unsigned int num );//主机转成蓝牙;l代表int类型
    unsigned int btohl( unsigned int num );//蓝牙转成主机;l代表int类型
    

    与网络顺序对应的函数一样,这些函数将16位和32位无符号整数转换为蓝牙字节顺序并返回。它们用于填充套接字寻址结构、与蓝牙微控制器通信以及在传输协议套接字上执行低级操作时使用。

    Dynamically assigned port numbers

    对于Linux内核版本2.6.7及更高版本,动态绑定到RFCOMM或L2CAP端口非常简单。用于绑定套接字的套接字寻址结构的rc_channel字段被简单地设置为0,内核将套接字绑定到第一个可用端口。不幸的是,对于早期版本的Linux内核,绑定到第一个可用端口号的唯一方法是尝试绑定到每个可能的端口,并在绑定没有失败时停止。下面的函数演示如何对RFCOMM套接字执行此操作。

    int dynamic_bind_rc(int sock, struct sockaddr_rc *sockaddr, uint8_t *port)
    {
        int err;
        for( *port = 1; *port <= 31; *port++ ) {
            sockaddr->rc_channel = *port;
            err = bind(sock, (struct sockaddr *)sockaddr, sizeof(sockaddr));
            if( ! err || errno == EINVAL ) break;
        }
        if( port == 31 ) {
            err = -1;
            errno = EINVAL;
        }
        return err;
    }
    

    RFCOMM summary

    通常使用setsockopt设置的高级TCP选项(如接收窗口和Nagle算法)在蓝牙中没有意义,也不能与RFCOMM套接字一起使用。除此之外,字节顺序和套接字寻址结构的不同,编程RFCOMM套接字实际上与编程TCP套接字完全相同。要使用套接字接受传入连接,请使用bind保留操作系统资源,使用listen将其置于侦听模式,并使用accept阻止和接受传入连接。创建传出连接也很简单,只需要调用connect。一旦建立了连接,读、写、发送和接收的标准调用就可以用于数据传输。

    本人微信:xiaoshitou5854

  • 相关阅读:
    HPU--1189 Ou à
    实数向整数的强制转换
    HPU--1166 阶乘问题(一)
    HPU--1163 大数A+B
    阿斯伯格综合征完全指南各章链接
    思维改变生活第10章、有效沟通
    Mathematica(MMA)闪电入门系列 目录与说明
    第二语言习得理论介绍
    第二语言习得实践方法
    复赛注意事项:关于文件读写的格式
  • 原文地址:https://www.cnblogs.com/xiaoshiwang/p/13377237.html
Copyright © 2020-2023  润新知