Ошибка при передаче файлов на Android с использованием сокета Windows C++ Bluetooth OBEX

OBEX, вероятно, является наиболее часто используемым методом передачи файлов через устройства, поддерживающие Bluetooth. Напротив, несмотря на то, что он является наиболее распространенным, он не очень хорошо документирован, как это на самом деле работает.

У меня не так много опыта работы в OBEX. Моя цель — разработать клиентский сервер OBEX на моем ноутбуке, который должен отправлять файлы на мое устройство 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." );
    

Этот процесс подключения пока успешен, и без каких-либо ошибок начинается связь с моим устройством Android с адресом BC:41:01:11:BC:2F. Статус функции сокета Connect() возвращает ноль (также успех).

Теперь, согласно OBEX 1.4 pdf, мне нужно начать сеанс после успешного установления соединения. Итак, на странице 38 PDF-файла хорошо описан начальный сеанс подключения, и я попытался имитировать синтаксис подключающегося пакета следующим образом.

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, который определен на странице 40 PDF-файла. попробовал сделать такую ​​же имитацию и в итоге получил вот такую ​​упаковку.

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

После отправки этого запроса PUT с именем файла ABCDE.TXT (1 байт) ответ от моего 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, сокет закрывается с нулевым кодом ошибки в ответ на запрос PUT. Следовательно, всплывающий диалог на моей стороне Android не должен появляться.

Теперь все, что я хочу, это отправить файл из клиента Winsock2 OBEX, написанный с нуля, на мой Android, где на моем устройстве Android должно появиться всплывающее окно с просьбой принять или отклонить входящий файл или запрос. после принятия должна начаться передача файла.

Так что это как-то похоже на мою ошибку, которую я не могу диагностировать. Вот почему я здесь, чтобы попросить добрых советов и помощи у тех, кто имеет большой опыт в этой области.

Спасибо и с уважением.

1. Длина вашего имени составляет 23 байта, но на самом деле оно 20. 2. Вы не завершили имя с помощью /0x0000 3. Вы можете протестировать свой Android с помощью этого (включая OPP, FTP-клиенты и OPP-сервер): btframework.com/ bluetoothframework.htm

Mike Petrichenko 26.05.2024 06:28

@MikePetrikova ОК изменил длину имени на 21 вместе с добавлением NULL-символа. Но все равно не повезло! Я проверил свой Android с помощью OBEX FTP, и появилось диалоговое окно, позволяющее ноутбуку получить доступ к файлам. При приеме ничего не происходит. Не могли бы вы потратить немного времени на то, чтобы я исправил и отправил код запроса PUT? Я буду очень благодарен за это.

CoderBittu 26.05.2024 06:42

Проверьте свою длину. Похоже, что длина тела также неверна, я не помню, возможен ли END-BODY без заголовка BODY. К сожалению, я внедрил OBEX много лет назад и не помню всех подробностей. Вы подключаетесь к OPP, а не к FTP. Они похожи, но на самом деле разные. Вам также необходимо просмотреть профиль Bluetooth OBEX (OPP), поскольку он расширяет стандартный OBEX некоторыми предварительными требованиями.

Mike Petrichenko 26.05.2024 06:47
0
3
164
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

У меня есть запрограммированный на C клиент OBEX, который успешно отправляет на Android следующие команды. Имя = hello.txt. Данные = Здравствуйте. Размер блока = 400. Размер данных = 5. Размер данных должен быть отправлен в 4-байтовом элементе 0xC3, ваш код, похоже, имеет значение 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};   

Размеры, которые необходимо установить, следующие. Каждая команда: байты [1] = старший байт и [2] = младшие байты — это размер всего пакета. Итак, отправка составляет 39 байт = 0x0027. Команда put (0x82 или 0x02) может содержать несколько элементов, каждый из которых имеет идентификатор заголовка 01, C3, 49 в примере. Два старших бита идентификатора определяют размер элемента. 10xxxxxx — это 1 байт. 11xxxxxx (C3) — это 4 байта. За 00xxxxxx и 01xxxxxx (01, 49) следуют два байта размера. Элемент 01 занимает 23 байта от начального 01 до конечного 0, как и 0x0017. Это размер элемента, а не файла. Элемент C3 = размер файла.

Это моя процедура отправки файлов. Функции write_node и read_node отправляют и получают пакеты Bluetooth. Вам понадобится собственная реализация.

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

Это сработало как шарм! Большое спасибо, друг мой! однако вы сами рассчитывали размер стрелки или использовали какой-либо алгоритм?

CoderBittu 26.05.2024 13:24

Еще вопрос: как размер файла с именем «Hello.txt» равен 0x17 или 23?

CoderBittu 26.05.2024 13:36

И почему значение второго байта после конца тела (0x49) равно 8?

CoderBittu 26.05.2024 13:54

Размеры, которые необходимо установить, следующие. Каждая команда: байты [1] = старший байт и [2] = младшие байты — это размер всего пакета. Итак, отправка составляет 39 байт = 0x0027. Команда put (0x82 или 0x02) может содержать несколько элементов, каждый из которых имеет идентификатор заголовка 01, C3, 49 в примере. Два старших бита идентификатора определяют размер элемента. 10xxxxxx — это 1 байт. 11xxxxxx (C3) — это 4 байта. За 00xxxxxx и 01xxxxxx (01, 49) следуют два байта размера. Элемент 01 занимает 23 байта от начального 01 до конечного 0, как и 0x0017. Это размер элемента, а не файла. Элемент C3 = размер файла.

petzval 26.05.2024 13:54

эй, друг, какой у тебя самый безопасный размер байтового блока пакета при передаче через Bluetooth Obex? Я пишу приложение для передачи файлов и хочу обезопасить себя, избегая любой потери данных.

CoderBittu 27.05.2024 19:28

Я отредактировал ответ, добавив свою процедуру отправки файлов. Я использую размер пакета 400, потому что я видел, как Windows иногда давала сбой с пакетами большего размера, когда передача была медленной. Я предполагаю, что сервер OBEX сообщит об ошибке в случае потери данных.

petzval 28.05.2024 12:14

Упс! Заметил ошибку в коде отправки - исправлено.

petzval 28.05.2024 13:47

После обновления ваш ответ стал лучше, чем когда-либо, не только для меня, но и для всех будущих посетителей.

CoderBittu 28.05.2024 13:58

Спасибо, но нашел еще одну ошибку, теперь исправлена. Должно быть flen = ftell(поток)

petzval 28.05.2024 14:50

После прекрасного объяснения @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 этот массив отправит андроиду файл с именем ABCDE.TXT размером 5 байт, внутри которого будет строка «BITTU». Не забудьте после этого отключить запрос обекса.

Если данные не помещаются в один пакет, перед финальными 0x82/0x49 необходимо поставить операции с кодом операции 0x02 и идентификатором заголовка элемента данных 0x48. Сервер должен отправлять на них ответы 0x90 CONTINUE, а не 0xA0 SUCCESS. После первого put последующие puts содержат только один элемент (0x48 или 0x49). Код sendfileobex() делает это для больших файлов.

petzval 28.05.2024 15:36

Отправляет полный файл правильно!

CoderBittu 28.05.2024 15:38

Фейнд, мне снова нужна твоя помощь. На странице 90 PDF-файла OBEX15 есть демонстрационный код для Vcard, который я сымитировал, но он не работает (код ответа: 0xCB). Не могли бы вы добавить эту информацию в свой ответ?

CoderBittu 28.05.2024 18:30

Я никогда не использовал функции Vcard, поэтому не могу с этим помочь.

petzval 29.05.2024 11:36

Другие вопросы по теме