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 должно появиться всплывающее окно с просьбой принять или отклонить входящий файл или запрос. после принятия должна начаться передача файла.
Так что это как-то похоже на мою ошибку, которую я не могу диагностировать. Вот почему я здесь, чтобы попросить добрых советов и помощи у тех, кто имеет большой опыт в этой области.
Спасибо и с уважением.
@MikePetrikova ОК изменил длину имени на 21 вместе с добавлением NULL-символа. Но все равно не повезло! Я проверил свой Android с помощью OBEX FTP, и появилось диалоговое окно, позволяющее ноутбуку получить доступ к файлам. При приеме ничего не происходит. Не могли бы вы потратить немного времени на то, чтобы я исправил и отправил код запроса PUT? Я буду очень благодарен за это.
Проверьте свою длину. Похоже, что длина тела также неверна, я не помню, возможен ли END-BODY без заголовка BODY. К сожалению, я внедрил OBEX много лет назад и не помню всех подробностей. Вы подключаетесь к OPP, а не к FTP. Они похожи, но на самом деле разные. Вам также необходимо просмотреть профиль Bluetooth OBEX (OPP), поскольку он расширяет стандартный OBEX некоторыми предварительными требованиями.
У меня есть запрограммированный на 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);
}
Это сработало как шарм! Большое спасибо, друг мой! однако вы сами рассчитывали размер стрелки или использовали какой-либо алгоритм?
Еще вопрос: как размер файла с именем «Hello.txt» равен 0x17 или 23?
И почему значение второго байта после конца тела (0x49) равно 8?
Размеры, которые необходимо установить, следующие. Каждая команда: байты [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 = размер файла.
эй, друг, какой у тебя самый безопасный размер байтового блока пакета при передаче через Bluetooth Obex? Я пишу приложение для передачи файлов и хочу обезопасить себя, избегая любой потери данных.
Я отредактировал ответ, добавив свою процедуру отправки файлов. Я использую размер пакета 400, потому что я видел, как Windows иногда давала сбой с пакетами большего размера, когда передача была медленной. Я предполагаю, что сервер OBEX сообщит об ошибке в случае потери данных.
Упс! Заметил ошибку в коде отправки - исправлено.
После обновления ваш ответ стал лучше, чем когда-либо, не только для меня, но и для всех будущих посетителей.
Спасибо, но нашел еще одну ошибку, теперь исправлена. Должно быть flen = ftell(поток)
После прекрасного объяснения @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() делает это для больших файлов.
Отправляет полный файл правильно!
Фейнд, мне снова нужна твоя помощь. На странице 90 PDF-файла OBEX15 есть демонстрационный код для Vcard, который я сымитировал, но он не работает (код ответа: 0xCB). Не могли бы вы добавить эту информацию в свой ответ?
Я никогда не использовал функции Vcard, поэтому не могу с этим помочь.
1. Длина вашего имени составляет 23 байта, но на самом деле оно 20. 2. Вы не завершили имя с помощью /0x0000 3. Вы можете протестировать свой Android с помощью этого (включая OPP, FTP-клиенты и OPP-сервер): btframework.com/ bluetoothframework.htm