OBEX 可能是通过支持蓝牙的设备传输文件最常用的技术。相反,由于它是最常见的,所以它的实际工作原理并没有很好的记录。
我在OBEX方面没有太多经验。我的目标是在我的笔记本电脑上设计一个 OBEX 客户端服务器,它应该通过像this这样的弹出窗口将文件发送到我的 Android 设备。
我选择了 C++ 作为在 Windows 上从头开始对服务器进行编程的语言。我正在使用的此操作的 UUID 是这个。
00001105-0000-1000-8000-00805F9B34FB
我已经像这样初始化了 OBEX 客户端。
GUID OBEX_GUID;
UuidFromString((unsigned char*)"00001105-0000-1000-8000-00805F9B34FB",(UUID*)&OBEX_GUID);
SOCKET soc,acpt;
SOCKADDR_BTH BluetoothAddress;
BluetoothAddress.addressFamily = AF_BTH;
BluetoothAddress.port = BT_PORT_ANY;
str2ba((char*)"BC:41:01:11:BC:2F",&BluetoothAddress.btAddr);
BluetoothAddress.serviceClassId = OBEX_GUID;
//OBEXObjectPushServiceClass_UUID;
soc = socket(AF_BTH , SOCK_STREAM , BTHPROTO_RFCOMM);
if(soc>0){
printf("Socket initialisation success\n");
}
stat = connect(soc,(sockaddr*)&BluetoothAddress,sizeof(BluetoothAddress));
printf("%s\n\n",(stat==0) ? "CONNECTED to the remote device!" : "Remote device is unavailable." );
此连接过程已成功,并且没有任何错误,它开始与地址为 BC:41:01:11:BC:2F 的 Android 设备进行通信。套接字的状态 connect() 函数返回零(也成功)。
现在,根据 OBEX 1.4 pdf ,我需要在成功建立连接后启动会话。所以在PDF的第38页中,连接的启动会话得到了很好的描述,我尝试模仿这样的连接包的语法
char connect_req[] = {
0x80 , 0x00 , 0x11 , 0x10 , 0x00 , 0x20 , 0x00 , 0xc0 , 0x00 , 0x00 , 0x00 , 0x01 , 0xc3 , 0x00 , 0x00 , 0xf4 , 0x83
// CNCT | 2B Length | Vers | Flag | Max Pack 8K | Count | 4 byte file count | Len Header | Total length of hex
};
将此连接包发送到我的 Android 后,它成功返回,值为 0xA0。这意味着会话终于开始了。
会话开始后,我需要发送一个虚拟文件,该文件需要作为 PUT 方法 进行请求,该方法在 PDF 的第 40 页中定义。尝试做同样的模仿,最终得到了这样的包。
unsigned char putfile_req[] = {
0x82 , 0x00 , 0x21 , 0x01 , 0x00 , 0x17 , 0x00,0x41 , 0x00,0x42 , 0x00,0x43 , 0x00,0x44 , 0x00,0x45 , 0x00,0x2c , 0x00,0x74 , 0x00,0x78 , 0x00,0x74 , 0xC3 , 0x00 , 0x00 , 0x00 , 0x01 , 0x49 , 0x00 , 0x01 , 0x74
// PUT | 2b Len | HI Name | NameLen | A B C D E . T X T | Len | 1 Byte | end | 1 Byte |
};
发送文件名为 ABCDE.TXT(1 字节)的 PUT 请求后,我的 Android 的响应以某种方式返回不成功,值为零 (0)
问题是,直到连接会话状态每个请求和响应都工作得很好,但每当我发送带有文件名的 PUT 请求时,Android 都会收到它,但由于某些未知的原因,Android 无法处理它并返回零.
我听到有人说 Android 不支持 OBEX,但如果这是实际因素,则在开始发送连接包时不会返回成功(A0)状态。
如果重要的话,这是完整的代码...
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2bth.h>
#include <bthsdpdef.h>
#include <bluetoothapis.h>
#include <ws2bth.h>
GUID OBEX_GUID;
int str2ba(const char *straddr, BTH_ADDR *btaddr)
{
int i;
unsigned int aaddr[6];
BTH_ADDR tmpaddr = 0;
if (sscanf(straddr, "%02x:%02x:%02x:%02x:%02x:%02x",
&aaddr[0], &aaddr[1], &aaddr[2],
&aaddr[3], &aaddr[4], &aaddr[5]) != 6)
return 1;
*btaddr = 0;
for (i = 0; i < 6; i++) {
tmpaddr = (BTH_ADDR) (aaddr[i] & 0xff);
*btaddr = ((*btaddr) << 8) + tmpaddr;
}
return 0;
}
int main(){
unsigned char response[10000]={0};
UuidFromString((unsigned char*)"00001105-0000-1000-8000-00805F9B34FB",(UUID*)&OBEX_GUID);
WSADATA ws;
WSAStartup(MAKEWORD(2,2),&ws);
int stat;
SOCKET soc,acpt;
SOCKADDR_BTH BluetoothAddress;
BluetoothAddress.addressFamily = AF_BTH;
BluetoothAddress.port = BT_PORT_ANY;
str2ba((char*)"BC:41:01:11:BC:2F",&BluetoothAddress.btAddr);
BluetoothAddress.serviceClassId = OBEX_GUID;
OBEXObjectPushServiceClass_UUID;
soc = socket(AF_BTH , SOCK_STREAM , BTHPROTO_RFCOMM);
if(soc>0){
printf("Socket initialisation success\n");
}
stat = connect(soc,(sockaddr*)&BluetoothAddress,sizeof(BluetoothAddress));
printf("%s\n\n",(stat==0) ? "CONNECTED to the remote device!" : "Remote device is unavailable." );
char connect_req[] = {
0x80 , 0x00 , 0x11 , 0x10 , 0x00 , 0x20 , 0x00 , 0xc0 , 0x00 , 0x00 , 0x00 , 0x01 , 0xc3 , 0x00 , 0x00 , 0xf4 , 0x83
// CNCT | 2B Length | Vers | Flag | Max Pack 8K | Count | 4 byte file count | Len Header | Total length of hex
};
unsigned char putfile_req[] = {
0x82 , 0x00 , 0x21 , 0x01 , 0x00 , 0x17 , 0x00,0x41 , 0x00,0x42 , 0x00,0x43 , 0x00,0x44 , 0x00,0x45 , 0x00,0x2c , 0x00,0x74 , 0x00,0x78 , 0x00,0x74 , 0xC3 , 0x00 , 0x00 , 0x00 , 0x01 , 0x49 , 0x00 , 0x01 , 0x74
// PUT | 2b Len | HI Name | NameLen | A B C D E . T X T | Len | 1 Byte | end | 1 Byte |
};
printf("PUT packet Length: %d\n\n",sizeof(putfile_req)/sizeof(unsigned char));
send(soc,(const char*)connect_req,sizeof(connect_req)/sizeof(connect_req[0]),0);
int Recvd = recv(soc,(char*)response,sizeof(response),0);
printf("OBEX CONNECT status: %X\n",response[0]);
if(response[0]==0xA0){
printf(" SUCCESS\n");
}
memset(response,0,sizeof(response));
send(soc,(char*)putfile_req,sizeof(putfile_req)/sizeof(putfile_req[0]),0);
Recvd = recv(soc,(char*)response,sizeof(response),0);
printf("PUT Request status: %X\n",response[0]);
if(response[0]==0x00){
printf("Something went wrong!\n");
}
}
输出看起来像, 即使连接会话成功并返回值 0xA0,套接字也会以错误代码 0 关闭作为 PUT 请求的响应。因此,我的 Android 端的弹出对话框不应该出现。
现在,我想要的只是从从头开始编写的 Winsock2 OBEX 客户端发送一个文件到我的 Android,其中我的 Android 设备中应该出现一个弹出窗口,要求用户接受或拒绝传入的文件或请求。接受后,文件传输应开始。
所以这似乎是我的错,我无法诊断。这就是为什么我来这里向该领域经验丰富的人寻求一些建议和帮助。
感谢和问候。
我有一个 C 编程的 OBEX 客户端,它成功地将以下命令发送到 Android。名称 = 你好.txt。 数据=你好。块大小 = 400。数据大小 = 5。 数据大小必须在 0xC3 4 字节项中发送,您的代码似乎值为 1。
unsigned char connect[7] = {0x80,0x00,0x07,0x10,0x00,0x01,0x90};
unsigned char send[39] = {0x82,0x00,0x27,
0x01,0x00,0x17,0,'h',0,'e',0,'l',0,'l',0,'o',0,'.',0,'t',0,'x',0,'t',0,0,
0xC3,0,0,0,5,
0x49,0,8,'H','e','l','l','o'};
unsigned char disconnect[3] = {0x81,0x00,0x03};
必须设置的尺寸如下。每个命令:bytes [1] = hi 字节和 [2] = lo 字节是整个数据包的大小。所以发送的是 39 个字节 = 0x0027。 put 命令(0x82 或 0x02)可以包含多个项目,在示例中每个项目都有一个标头标识符 01、C3、49。标识符的两个 hi 位指定项目的大小。 10xxxxxx 是 1 个字节。 11xxxxxx (C3) 是 4 字节。 00xxxxx 和 01xxxxxx (01, 49) 后面跟着两个大小字节。 01项从最初的01到最后的0共23个字节,0x0017也是如此。这是项目的大小而不是文件的大小。 C3 项 = 文件大小。
这是我的文件发送例程。 write_node 和 read_node 函数发送和接收蓝牙数据包。您将需要自己的实现。
int sendfileobex(int node,char *filename)
{
int n,k,fn,flen,nlen,ndat,ntogo,nblock,len,err;
unsigned char inbuf[64],send[512];
static unsigned char connect[7] = {0x80,0x00,0x07,0x10,0x00,0x01,0x90};
static unsigned char disconnect[3] = {0x81,0x00,0x03};
char *fname;
FILE *stream;
fn = 0; // strip path
n = 0;
while(filename[n] > 32 && n < 1022)
{
if(filename[n] == '/' || filename[n] == '\\')
fn = n+1; // start of file name
++n;
}
fname = filename+fn;
printf("Sending file %s\n",filename);
stream = fopen(filename,"r");
if(stream == NULL)
{
printf("File open error\n");
return(0);
}
fseek(stream,0,SEEK_END);
flen = ftell(stream); // file length
fseek(stream,0,SEEK_SET);
ntogo = flen;
nlen = strlen(fname);
nblock = 400;
connect[5] = (nblock >> 8) & 0xFF;
connect[6] = nblock & 0xFF;
// OBEX connect
write_node(node,connect,7);
inbuf[0] = 0;
// wait for Success reply 0x0A
len = read_node_endchar(node,inbuf,64,PACKET_ENDCHAR,EXIT_TIMEOUT,5000);
if(len == 0 || inbuf[0] != 0xA0)
{
printf("OBEX Connect failed\n");
fclose(stream);
return(0);
}
else if((inbuf[1] << 8) + inbuf[2] >= 7)
{
n = (inbuf[5] << 8) + inbuf[6];
if(n < nblock)
nblock = n;
}
send[3] = 0x01;
n = 2*nlen + 5;
send[4] = (n >> 8) & 0xFF;
send[5] = n & 0xFF;
k = 6;
for(n = 0 ; n < nlen ; ++n)
{
send[k] = 0;
send[k+1] = fname[n];
k += 2;
}
send[k] = 0;
send[k+1] = 0;
k += 2;
send[k] = 0xC3;
send[k+1] = (flen >> 24) & 0xFF;
send[k+2] = (flen >> 16) & 0xFF;
send[k+3] = (flen >> 8) & 0xFF;
send[k+4] = flen & 0xFF;
k += 5;
err = 0;
do
{
if(ntogo <= nblock - 3 - k)
{
send[k] = 0x49;
send[0] = 0x82;
ndat = ntogo + 3;
}
else
{
send[k] = 0x48;
send[0] = 0x02;
ndat = nblock - k;
}
send[k+1] = (ndat >> 8) & 0xFF;
send[k+2] = ndat & 0xFF;
k += 3;
ndat -= 3;
if(fread(send+k,1,ndat,stream) != ndat)
{
printf("File read error\n");
err = 1;
}
else
{
ntogo -= ndat;
k += ndat;
send[1] = (k >> 8) & 0xFF;
send[2] = k & 0xFF;
write_node(node,send,k); // send k bytes
inbuf[0] = 0;
len = read_node_endchar(node,inbuf,64,PACKET_ENDCHAR,EXIT_TIMEOUT,5000);
if(len == 0 || (inbuf[0] != 0xA0 && inbuf[0] != 0x90))
{
printf("Send failed\n");
err = 1;
}
}
k = 3;
}
while(ntogo > 0 && err == 0);
fclose(stream);
write_node(node,disconnect,3);
inbuf[0] = 0;
len = read_node_endchar(node,inbuf,64,PACKET_ENDCHAR,EXIT_TIMEOUT,5000);
if(len == 0 || inbuf[0] != 0xA0)
printf("OBEX Disconnect failed\n");
return(1);
}
在 @petzval 的精彩解释之后,这是我对 obex PUT 请求如何工作的理解
unsigned char putfile_req[] = {
/*1*/ 0x82 , 0x00 , 0x28 , /* PUT, PackSize 2 bytes*/
/*2*/ 0x01 , 0x00 , 0x17 , /* Name, Namelength 2 Bytes*/
/*3*/ 0x00,'A' , 0x00,'B' , 0x00,'C' , 0x00,'D' , 0x00,'E' , 0x00,'.' , 0x00,'T' , 0x00,'X' , 0x00,'T' 0x00,0x00,
/* Name of the file to be sent, in unicode with null terminator */
/*4*/ 0xC3 , 0x00 , 0x00 , 0x00 , 0x05 , //Actual File Length attribute with 4 byte length describing its actual length
/*5*/ 0x49 , 0x00 , 0x08 , 'B','I','T','T','U'
/* File body or binary data.*/
};
1. 0x82: is the PUT and end transmission command for OBEX followed
by two bytes at 1st and 2nd index. The two bytes define the
package size as a whole in 2 byte integer. Therefore in this
case, the size would be
sizeof(putfile_req) = 40 or 0x00,0x28 (two byte definition of 40)
2. 0x01: is the Name header that defines name attributes of the object transmitted
It is also followed by a 2 byte integer that contais the length of
of the whole object including the filename.
so, the value of name header length is,
strlen("ABCDE.txt") = 9
with null terminator it becomes 9 + 1 = 10
because file name is in unicoded, size is,
10 * 2 = 20, with name header included it becomes
20 + 3 = 23 (name header + 2 byte length)
Formula: (strlen("ABCDE.TXT")+1)*2 + 3 = 23
or 0x00,0x17 (two byte definition of 23)
3. 0x00,'A'... 0x00,0x00: The name of the file which is tobe sent.
file name must be put as 2 byte Unicode with a
2 byte null terminator included as well. total
length of filename is 20 as described in the
second point.
4. 0xC3: This is Length header of the object being sent.
followed by a 4 byte integer which stores the actual
size of file. The file has 5 bytes of charecters
"BITTU". Hence the value will be
strlen("BITTU") = 0x00,0x00,0x00,0x05 (4 byte definition of 5)
5. 0x49: It is an End-Body header that informs the receiver
that the transmission is complete. Followed by an
integer of 2 bytes that store the binary data of the
file as a chunk.
in this case, the value of End-Body header length is,
strlen("BITTU")+3 = 0x00,0x08 (2 byte definition of 8)
成功的 OBEX 连接请求后,该数组将向 Android 发送一个名为 ABCDE.TXT 的文件,大小为 5 字节,其中包含字符串 “BITTU”。之后不要忘记断开请求 obex 的连接。