本文作者的部落格地址:http://blog.sina.com.cn/samzhen1977
作者:Sam (甄峰)
sam_code@hotmail.com
例一:發送Signaling Packet:
Signaling Command是2個Bluetooth實體之間的L2CAP層命令傳輸。所以得Signaling
Command使用CID 0x0001
.
多個Command可以在一個C-frame(control frame)中發送。
如果要直接發送Signaling
Command.需要建立SOCK_RAW類型的L2CAP串連Socket。這樣才有機會自己填充Command
Code,Identifier等。
以下是一個發送signaling Command以及接收Response的簡單例子:
int main(int argc, char** argv)
{
int l2_sck = 0;
int iRel = 0;
struct sockaddr_l2 local_l2_addr;
struct sockaddr_l2 remote_l2_addr;
char str[24] ={0};
int len = 0;
int size = 50;
char* send_buf;
char* recv_buf;
int i = 0;
int id = 1; //不要為0
send_buf = malloc(L2CAP_CMD_HDR_SIZE +
size);
recv_buf = malloc(L2CAP_CMD_HDR_SIZE +
size);
if(argc < 2)
{
printf("/n%s
<bdaddr>/n", argv[0]);
exit(0);
}
// create l2cap raw socket
l2_sck = socket(PF_BLUETOOTH, SOCK_RAW,
BTPROTO_L2CAP); //建立L2CAP protocol的RAW Packet
if(l2_sck < 0)
{
perror("/nsocket:");
return -1;
}
//bind
memset(&local_l2_addr, 0,
sizeof(struct sockaddr_l2));
local_l2_addr.l2_family = PF_BLUETOOTH;
bacpy(&local_l2_addr.l2_bdaddr ,
BDADDR_ANY);
iRel = bind(l2_sck, (struct sockaddr*)
&local_l2_addr, sizeof(struct sockaddr_l2));
if(iRel < 0)
{
perror("/nbind()");
exit(0);
}
//connect
memset(&remote_l2_addr, 0 ,
sizeof(struct sockaddr_l2));
remote_l2_addr.l2_family = PF_BLUETOOTH;
//printf("/nConnect to %s/n", argv[1]);
str2ba(argv[1],
&remote_l2_addr.l2_bdaddr);
iRel = connect(l2_sck, (struct
sockaddr*)&remote_l2_addr, sizeof(struct
sockaddr_l2));
if(iRel < 0)
{
perror("/nconnect()");
exit(0);
}
//get local bdaddr
len = sizeof(struct sockaddr_l2);
memset(&local_l2_addr, 0,
sizeof(struct sockaddr_l2));
//注意,getsockname()參數三是一個輸入輸出參數。輸入時,為參數二的總體長度。輸出時,
//為實際長度。
iRel = getsockname(l2_sck, (struct sockaddr*)
&local_l2_addr, &len);
if(iRel < 0)
{
perror("/ngetsockname()");
exit(0);
}
ba2str(&(local_l2_addr.l2_bdaddr),
str);
//printf("/nLocal Socket bdaddr:[%s]/n",
str);
printf("l2ping: [%s] from [%s](data size %d)
.../n", argv[1], str, size);
for (i = 0; i < size;
i++)
send_buf[L2CAP_CMD_HDR_SIZE +
i] = 'A';
l2cap_cmd_hdr *send_cmd = (l2cap_cmd_hdr *)
send_buf;
l2cap_cmd_hdr *recv_cmd = (l2cap_cmd_hdr *)
recv_buf;
send_cmd->ident =
id; //如所示,這一項為此Command
Identifier
send_cmd->len
= htobs(size);
send_cmd->code =
L2CAP_ECHO_REQ; //如所示,此項為Command
code.這項定為:
//
Echo
Request。對端會發送Response回來。code=L2CAP_ECHO_RSP
while(1)
{
send_cmd->ident
= id;
if(send(l2_sck, send_buf, size
+ L2CAP_CMD_HDR_SIZE, 0) <= 0)
{
perror("/nsend():");
}
while(1)
{
if(recv(l2_sck,
recv_buf, size + L2CAP_CMD_HDR_SIZE, 0) <= 0)
{
perror("/nrecv()");
}
if
(recv_cmd->ident != id)
continue;
if(
recv_cmd->code == L2CAP_ECHO_RSP)
{
//printf("/nReceive
Response Packet./n");
printf("%d
bytes from [%s] id %d/n", recv_cmd->len, argv[1],
recv_cmd->ident);
break;
}
}
sleep(1);
id ++;
}
close(l2_sck);
return 0;
}
所以說,如果想要發送接收signaling Command。只需要建立l2cap RAW socket.
並按規則填充command id, command code等。就可以接收發送了。
Command Code: 這個值放在l2cap.h中。
#define L2CAP_COMMAND_REJ 0x01
#define
L2CAP_CONN_REQ 0x02
#define
L2CAP_CONN_RSP 0x03
#define
L2CAP_CONF_REQ 0x04
#define
L2CAP_CONF_RSP 0x05
#define L2CAP_DISCONN_REQ 0x06
#define L2CAP_DISCONN_RSP 0x07
#define
L2CAP_ECHO_REQ 0x08
#define
L2CAP_ECHO_RSP 0x09
#define
L2CAP_INFO_REQ 0x0a
#define
L2CAP_INFO_RSP 0x0b
例二:任意PSM的L2CAP串連間資料的傳輸:
此例子中:Server,client其實是使用網路的概念定義的。
server用來監聽指定PSM的串連,並監聽資料。同時,利用poll來查看peer是否斷掉了。
Server:
#include <stdio.h>
#include
<sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <poll.h>
#include
<bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>
void * Read_thread(void* pSK);
來源:(http://blog.sina.com.cn/s/blog_602f87700100emfb.html
)
- 實戰Linux Bluetooth編程(六) L2CAP編程執行個體_samzhen_新浪部落格
int main(int
argc, char** argv)
{
int iRel = 0;
int sk = 0;
struct sockaddr_l2 local_addr;
struct sockaddr_l2 remote_addr;
int len;
int nsk = 0;
pthread_t nth = 0;
struct l2cap_options opts;
int optlen = 0;
int slen = 0;
char str[16] = {0};
if(argc < 2)
{
printf("/nUsage:%s psm/n",
argv[0]);
exit(0);
}
// create l2cap socket
sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET,
BTPROTO_L2CAP); //發送資料,使用SOCK_SEQPACKET為好
if(sk < 0)
{
perror("/nsocket():");
exit(0);
}
//bind
local_addr.l2_family = PF_BLUETOOTH;
local_addr.l2_psm = htobs(atoi(argv[argc
-1])); //last psm
bacpy(&local_addr.l2_bdaddr,
BDADDR_ANY);
iRel = bind(sk, (struct sockaddr
*)&local_addr, sizeof(struct sockaddr));
if(iRel < 0)
{
perror("/nbind()");
exit(0);
}
//get opts
// in mtu 和 out
mtu.每個包的最大值
memset(&opts,
0, sizeof(opts));
optlen = sizeof(opts);
getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS,
&opts, &optlen);
printf("/nomtu:[%d]. imtu:[%d]. flush_to:[%d].
mode:[%d]/n", opts.omtu, opts.imtu, opts.flush_to, opts.mode);
//set opts. default value
opts.omtu = 0;
opts.imtu = 672;
if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS,
&opts, sizeof(opts)) < 0)
{
perror("/nsetsockopt():");
exit(0);
}
//listen
iRel = listen(sk, 10);
if(iRel < 0)
{
perror("/nlisten()");
exit(0);
}
len = sizeof(struct sockaddr_l2);
while(1)
{
memset(&remote_addr,
0, sizeof(struct sockaddr_l2));
nsk = accept(sk, (struct
sockaddr*)(&remote_addr),
&len);
if(nsk <
0)
{
perror("/naccept():");
continue;
}
ba2str(&(remote_addr.l2_bdaddr),
str);
printf("/npeer bdaddr:[%s]./n",
str); //得到peer的資訊
iRel =
pthread_create(&nth, NULL, Read_thread,
&nsk);
if(iRel != 0)
{
perror("pthread_create():");
continue;
}
pthread_detach(nth);
// 分離之
}
return 0;
}
void * Read_thread(void* pSK)
{
//struct pollfd fds[10];
struct
pollfd fds[100];
char buf[1024] = {0};
int iRel = 0;
int exit_val = 0;
//fds[0].fd = *(int*)pSK;
//fds[0].events = POLLIN | POLLHUP;
fds[0].fd
= (int)(*(int*)pSK);
fds[0].events
=
POLLIN
| POLLHUP;
while(1)
{
if(poll(fds, 1, -1)
< 0)
{
perror("/npoll():");
}
if(fds[0].revents
& POLLHUP)
{
//hang
up
printf("/n[%d]
Hang up/n", *(int*)pSK);
close(*(int*)pSK);
pthread_exit(&exit_val);
break;
}
if(fds[0].revents
& POLLIN)
{
memset(buf, 0
, 1024);
//read
data
iRel =
recv(*(int*)pSK, buf, 572, 0);
//printf("/nHandle[%d]
Receive [%d] data:[%s]", *(int*)pSK, iRel, buf);
}
}
return 0;
}
client:
#include <stdio.h>
#include
<sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include
<bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>
int main(int argc, char** argv)
{
int sk;
int i = 0;
char buf[24] = "Sam is Good Guy!";
struct sockaddr_l2 local_addr;
struct sockaddr_l2 remote_addr;
int iRel = 0;
if(argc < 3)
{
printf("/nUsage:%s
<bdaddr>
<PSM>/n", argv[0]);
exit(0);
}
sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET,
BTPROTO_L2CAP);
if(sk < 0)
{
perror("/nsocket():");
exit(0);
}
//bind. bluetooth好像不許有無名Socket
local_addr.l2_family = PF_BLUETOOTH;
bacpy(&local_addr.l2_bdaddr,
BDADDR_ANY);
iRel = bind(sk, (struct sockaddr
*)&local_addr, sizeof(struct sockaddr));
if(iRel < 0)
{
perror("/nbind()");
exit(0);
}
memset(&remote_addr, 0,
sizeof(struct sockaddr_l2));
remote_addr.l2_family = PF_BLUETOOTH;
str2ba(argv[1],
&remote_addr.l2_bdaddr);
remote_addr.l2_psm = htobs(atoi(argv[argc
-1]));
connect(sk, (struct
sockaddr*)&remote_addr, sizeof(struct
sockaddr_l2));
for(i = 0; i < 60; i++)
{
iRel = send(sk, buf,
strlen(buf)+1, 0);
printf("Send [%d] data/n",
strlen(buf)+1);
sleep(1);
}
close(sk);
return 0;
}
注意:
1. 在Linux
網路編程中,主動發起串連方,因為不關心地址具體是什麼,所以可以作為無名socket,也就是說可以不bind.
但Bluetooth則不可以,一定需要bind.
2.
poll可以查出串連斷連,但需要注意:斷開的revent值為:11001B。也就是說:POLLIN | POLLERR
|POLLHUP。
3.
被串連一方,一定要指定PSM。