使用 Windows C++ 蓝牙 OBEX 套接字将文件传输到 Android 时出错

问题描述 投票:0回答:2

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");
    }
    
    
}

输出看起来像, enter image description here 即使连接会话成功并返回值 0xA0,套接字也会以错误代码 0 关闭作为 PUT 请求的响应。因此,我的 Android 端的弹出对话框不应该出现。

现在,我想要的只是从从头开始编写的 Winsock2 OBEX 客户端发送一个文件到我的 Android,其中我的 Android 设备中应该出现一个弹出窗口,要求用户接受或拒绝传入的文件或请求。接受后,文件传输应开始。

所以这似乎是我的错,我无法诊断。这就是为什么我来这里向该领域经验丰富的人寻求一些建议和帮助。

感谢和问候。

android c++ bluetooth winsock2 obex
2个回答
1
投票

我有一个 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);
  }

0
投票

在 @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 的连接。

© www.soinside.com 2019 - 2024. All rights reserved.