forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   Библиотеки (http://forum.boolean.name/forumdisplay.php?f=124)
-   -   Lib_binsock: прием/отправка бинарных данных через сокет (http://forum.boolean.name/showthread.php?t=8960)

ViNT 20.08.2009 19:09

Lib_binsock: прием/отправка бинарных данных через сокет
 
Вложений: 1
Lib_binsock - библиотека для работы с сокетами в трех режимах:
-побайтно(прием/передача)
-прием/передача массивов бинарных данных
-InputStream(Resource)

Обновление. В версии 1.1 добавлена функция flush для немедленной отправки данных

Библиотека содержит следующие функции:

procedure debug_register(idx:integer);
Устанавливает библиотеке идентификатор, который будет передаваться
в обработчик ошибок (о нем ниже) в случае возникновения исключительной ситуации(Exception).

procedure enable_debug(flag:integer);
Включает/отключает режим отладки.
flag = 1 - включить
flag = -1 - выключить
По умолчанию отладка отключена.

Если отладка включена, то в случае возникновения ошибки
вызывается процедура-обработчик, определенная в главной программе, например:

PHP код:

//sender - числовой идентификатор библиотеки, присваиваемый с помощью debug_register
//exception - системное сообщение об ошибке
//errstr - дополнительное сообщение (указанное при компиляции библиотеки)
//code - код ошибки (указанный при компиляции библиотеки)
procedure onerror(sender:Integer;exception:string;errstr:string;code:integer);
//Имя процедуры, порядок и тип параметров должны быть соблюдены !
begin
 cmOK
:=CreateCommand('OK',CM_OK,1);
 
ShowAlert('Error #'+IntegerToString(code),'Exception: '+exception+chr(10)+chr(13)+'ErrorString: '+errstrLoadImage('/icon.png'),ALERT_ERROR);
 
delay(20000);
 
ShowForm;
end

Параметр code может иметь следующие значения:

int ERR_OPENERROR = 0; - ошибка открытия сокета
int ERR_CLOSEERROR = 1; - ошибка закрытия сокета
int ERR_AVAILERROR = 2; - ошибка при определении размера буфера
int ERR_READERROR = 3; - ошибка получения данных
int ERR_WRITEERROR = 4; - ошибка отправки данных


Внимание!
В случае, если отладка включена, обработчик должен присутствовать обязательно,
иначе возможен сбой в работе мидлета; если отладка отключена, обработчик объявлять не обязательно.


function open(url:string):integer;
Открывает соединение с адресом url.
Формат url стандартный:
socket://url:port

Возвращает 1 в случае успеха, иначе возвращает -1 и передает в обработчик сообщение об ошибке ERR_OPENERROR.

procedure close;
Закрывает соединение.

В случае неудачи передает в обработчик сообщение об ошибке ERR_CLOSEERROR.

function available:integer;
Возвращает размер доступных для приема данных.

В случае неудачи возвращает -1 и передает в обработчик сообщение об ошибке ERR_AVAILERROR.

function read_byte:integer;
Возвращает следующий байт данных из буфера приема, в случае неудачи возвращает -1 и передает в обработчик сообщение об ошибке ERR_READERROR.

function write_byte(data:integer):integer;
Записывает в буфер передачи байт data.

Возвращает 1 в случае успеха, иначе возвращает -1 и передает ошибку ERR_WRITEERROR.

function get_in_stream:resource;
Возвращает поток resource для подключения.

function write_bin(data:string):integer;
Записывает массив байт data в поток передачи.

Возвращает 1 в случае успеха, иначе возвращает -1 и передает ошибку ERR_WRITEERROR.

function read_bin(len:integer):string;
Возвращает массив байт из входящего потока.

В случае неудачи возвращает null и передает в обработчик сообщение ERR_READERROR.

function rx_count:integer;
Возвращает количество принятых данных.

function tx_count:integer;
Возвращает размер принятых данных.

function flush:integer; c версии 1.1

Инициирует немедленную отправку данных на сервер

Возвращает 1 в случае успеха, иначе возвращает -1 и передает в обработчик сообщение об ошибке
ERR_FLUSHERROR.

Внимание!
---------

Библиотека требует доступа к сети

Проверено на Motorola L9.

Отправку данных проверить не смог, но должно работать.


P.S. Предлагаю всем разработчикам в своих новых библиотеках (еще лучше и старые переделать) использовать такой же принцип обработки ошибок. Чуть позже опишу принцип подробнее. Основная идея состоит в том, чтобы использовать общий обработчик для обработки ошибок от всех библиотек.

Skythrone 24.08.2009 22:44

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
ViNT, спасибо за либу! Весьма полезна.

Но в процессе тестирования на реальных телефонах обнаружилось следующее:

1. Даже если не делать обработчик ошибок, всё равно на некоторых самсунгах может не запускаться. И через обфускатор proguard не проходит. Если создать хотя бы пустую процедуру onerror, то всё ОК. Надеюсь, эта информация пригодится тем, кто будет использовать данную либу.

2. Приём данных я делаю примерно так:

PHP код:

count := binsock.available;
content := '';
while (
count>0) do
begin
 content 
:= content binsock.read_bin(count);
 
count := binsock.available;
end

т.е. всё скачиваю в текстовую переменную.
Я так понял, что при загрузке сначала заполняется некий буфер, а потом уже из него можно скачивать в бинарный массив.
Подобный цикл while..end понадобился, поскольку размер буфера значительно меньше размера реально скачиваемого с сервера контента.
На эмуляторе kEmulator переменная count, отображающая binsock.available, принимает максимальное значение 8192. То есть когда я скачиваю с сервера 20 кб данных, цикл проходит 3 раза.
Но бинарная картинка, скачанная таким образом, нормально собирается и отображается на эмуляторе.

На мобильниках всё не так радужно. Например, на Samsung E-200 размер буфера всего 512 байт. Текстовый контент в приведённом мной выше цикле скачивается и собирается нормально, а вот картинка - нет, приходит битая.

Может, сможешь что-нибудь посоветовать?

ViNT 25.08.2009 03:03

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
Спасибо за подробный баг-репорт, буду разбираться.
На счет некорректного приема картинки - надо бы сохранить данные в файл и сравнить с оригиналом, может станет понятно, в чем дело.
По поводу размера буфера - посмотрю, что по этому поводу написано в доках.

rangel 03.12.2009 11:54

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
а можно примерчит отправки скажем текста
на определённый айпишник, через udp

Topaz1977 23.02.2010 16:36

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
Работает нормально, но при проверке на современных телефонах (SE C510 и некоторые Nokia) -не отправляет данные сразу. В процессе разбора полетов выяснилось-телефоны упорно буферизируют данные на отправку. Вылечилось добавлением
public static int write_byte(int data){
tx++;
try{
os.write(data);
os.flush(); <-----Вот этого
return 1;
}catch(Exception e){
err(e, ERR_WRITEERROR, "Error writing byte to stream");
return -1;
}
}

public static int write_bin(String data){
byte d[] = data.getBytes();
tx = tx+d.length;
try{
os.write(d);
os.flush(); <-----и этого
}catch(Exception e){
err(e, ERR_WRITEERROR, "Error writing binary to stream");
return -1;}
return 1;
}

odd 23.02.2010 22:44

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
Да. FLUSH нужно делать обязательно.
На любых телефонах.

peps 10.04.2010 20:59

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
Цитата:

Сообщение от Topaz1977 (Сообщение 139021)
Работает нормально, но при проверке на современных телефонах (SE C510 и некоторые Nokia) -не отправляет данные сразу. В процессе разбора полетов выяснилось-телефоны упорно буферизируют данные на отправку. Вылечилось добавлением
public static int write_byte(int data){
tx++;
try{
os.write(data);
os.flush(); <-----Вот этого
return 1;
}catch(Exception e){
err(e, ERR_WRITEERROR, "Error writing byte to stream");
return -1;
}
}

public static int write_bin(String data){
byte d[] = data.getBytes();
tx = tx+d.length;
try{
os.write(d);
os.flush(); <-----и этого
}catch(Exception e){
err(e, ERR_WRITEERROR, "Error writing binary to stream");
return -1;}
return 1;
}

Выложите пожалуйста исправленную версию кто нибудь.

ViNT 10.04.2010 21:51

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
Добавил функцию flush

peps 10.04.2010 21:54

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
Цитата:

Сообщение от ViNT (Сообщение 144440)
Добавил функцию flush

Спасибо! Очень оперативно!:super:

AssA 27.04.2012 18:47

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
добрый день
спасибо за либу, пользую, работает отлично.

есть вопрос:
присылаю с сервера (delphi 2007, ServerSocket) текст с русскими буквами:

тел. НОКИА:
текст в ANSI: read_bin возвращает в строке символы ANSI, читаю посимвольно, преобразую в UTF8 - вопросов нет.
текст в UTF8: русские буквы приходят по 2 байта (как и положено)

тел. LG:
текст в ANSI: read_bin возвращает только часть строки ДО первой русской буквы
текст в UTF8: принимает на ура, даже преобразовывать ничего не надо.

ВОПРОС
каким образом можно оптимально заставить оба тел работать? сам я решение предполагаю, и не одно, но они мне не нравяться. LG при получении бинарного потока корректно распознает в нем русские буквы при копировании в строку, нокиа же, просто раскладывает - в каждый символ один байт.

буду признателен всем ответившим.


привожу свою функцию для преобразования данных из сокета в utf8 с русскими символами, может пригодиться кому нибудь. (функция фильтрует управляющие символы. этой же функцией можно читать русский текст из RecordStore)

Код:

function scr_frBIN(s: string): string; // (from BIN, ANSI to UTF)
var
  i,l,co: integer;
  ch: char;
  re: string;
begin
  re:= '';
  l:= length(s)-1;
  for i:=0 to l do begin
    ch:= getChar(s,i);
    co:= ord(ch);
    if co>31 then
      if co<127 then re:= re+ch
      else if co>191 then re:= re+chr(co+848)
        else if co=168 then re:= re+chr(1025)
          else if co=184 then re:= re+chr(1105);
  end;
  scr_frBIN:= re;
  re:= '';
end;


ViNT 28.04.2012 00:26

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
Просто режет строку, в обработчик ошибок ничего не сваливается, мидлет не зависает? Вообще, если режет символы даже в бинарном режиме, то вряд ли что-то модно сделать. Хотя, надо посмотреть, может и есть решение.

AssA 02.05.2012 12:33

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
провел тест на LG ( gw300 ):

binsock.debug_register(0);
binsock.enable_debug(-1);

ansi:
отправил: 123фф123
на тел:123? ( len = 4 )
utf-8:
отправил: 123фф123
на тел: 123фф123 ( len = 8 )

binsock.debug_register(1);
binsock.enable_debug(1);

результат тот же, ошибок нет. после получения русских "вопросиков" мидлет продолжает работать. вот исходник теста: (в первом тесте процедуры onerror не было)

Код:

program testscr;
uses
  binsock;

var
  IsConn, l : integer;
  rx: string;
  b: integer;
  res: resource;

  cmOK: command;

procedure onerror(sender:Integer;exception:string;errstr:string;code:integer);
begin
  cmOK:= CreateCommand('OK',CM_OK,1);
  ShowAlert('Error #'+IntegerToString(code),'Exception: '+exception+chr(10)+chr(13)+'ErrorString: '+errstr, LoadImage('/icon.png'),ALERT_ERROR);
  repeat delay(200);
  until getClickedCommand = cmOK;
end;

procedure clr;
begin
  showCanvas;
  setFont(FONT_FACE_SYSTEM, 0, FONT_SIZE_SMALL);
  SetColor(0,0,0);
  fillRect(0,0, getwidth, getheight);
  SetColor(0,255,0);
end;

procedure txt(s: string; y: integer;);
begin
  drawtext(s, 5, y);
  repaint;
end;

begin
  // scr
  clr;

  // сокеты
  binsock.debug_register(1);
  binsock.enable_debug(1);

  // connect
  IsConn:= binsock.open( 'socket://xxxx:2286' );
  delay(3000);

  if IsConn=1
    then txt('connected!', 10)
    else halt;

  // loop
  repeat
    rx:= '';

    // read sock
    l:= binsock.available;
    while l>0 do begin
      rx:= rx + binsock.read_bin(l);
      l:= binsock.available;
    end;

    // show
    l:= length(rx);
    if l>0 then begin
      clr;
      txt( integertostring(GetCurrentTime), 10 );
      txt( 'rx len='+IntegerToString(l), 30  );
      txt( rx, 50 );
    end;

    delay(100);
  until GetKeyPressed <> KE_NONE;

end.

отправка с сервера в сокет выглядела так (delphi 2007):
Код:

procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  us: UTF8String;
begin
  us:= AnsiToUtf8( Edit1.Text );
  for i:= 0 to ServerSocket1.Socket.ActiveConnections-1 do
    if CheckBox1.Checked then // птичка "отправлять в utf-8"
      ServerSocket1.Socket.Connections[i].SendText( us )
    else
      ServerSocket1.Socket.Connections[i].SendText( Edit1.Text );

end;

продолжил тесты:

функция binsock.read_byte возвращает именно байты. если пришел ansi - получим именно их коды, utf - по два байта на каждую русскую букву.

то есть засада в функции binsock.read_bin() - мне кажется, когда присваивается результат, NOKIA и LG воспринимают это по-разному: нокиа строго раскладывает побайтно, lg в этом потоке бинарных данных рассматривает utf8 символы и преобразует находу. можно ли и NOKIA заставить "распознавать" строку UTF-8 ?

ViNT 06.05.2012 00:17

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
Совершенно непонятно, в чем дело. Скорее всего, какие-то внутренние особенности реализации обработки данных. Не думаю, что с этим можно что-то сделать.

LIDERSERVIS 28.05.2012 17:26

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
Цитата:

Сообщение от AssA (Сообщение 227056)
провел тест на LG ( gw300 ):

binsock.debug_register(0);
binsock.enable_debug(-1);

ansi:
отправил: 123фф123
на тел:123? ( len = 4 )
utf-8:
отправил: 123фф123
на тел: 123фф123 ( len = 8 )

......

то есть засада в функции binsock.read_bin() - мне кажется, когда присваивается результат, NOKIA и LG воспринимают это по-разному: нокиа строго раскладывает побайтно, lg в этом потоке бинарных данных рассматривает utf8 символы и преобразует находу. можно ли и NOKIA заставить "распознавать" строку UTF-8 ?


1. На Delphi :
Перевожу русские буквы в base64, и отправляю в сокет ...Socket.Connections[i].SendText(AnsiToBase64('ваш текст' + '@')),
где например @ --- дополнительно обозначаю конец текста

PHP код:

function AnsiToBase64(String):String;
var
        
SS string;
begin
        SS
:=Utf8Encode(S);
        
SS:=EncodeBase64(SS);
        
result:=SS;
end

где, Utf8Encode --- стандартная ф-ция библиотеки System

EncodeBase64 :
PHP код:

function EncodeBase64(ValueString): String;
const 
 
b64alphabetPChar 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  
padPChar '===='

  function 
EncodeChunk(const ChunkString): String
  var 
    
WLongWord
    
inByte
  
begin 
    n 
:= Length(Chunk); := 0
    for 
:= 0 to n do 
      
:= Ord(Chunk[1]) shl ((i) * 8); 
    
Result := b64alphabet[(W shr 18) and $3f] + 
              
b64alphabet[(W shr 12) and $3f] + 
              
b64alphabet[(W shr 06) and $3f] + 
              
b64alphabet[(W shr 00) and $3f]; 
    if 
<> 3 then 
      Result 
:= Copy(Result01) + Copy(pad0n); 
  
end

begin 
  Result 
:= ''
  while 
Length(Value) > do 
  
begin 
    Result 
:= Result EncodeChunk(Copy(Value03)); 
    
Delete(Value13); 
  
end
end

********************************************

2. На мобильном клиенте
Считываю данные,

PHP код:

Function ReadData():string;
var
        
kinteger;
        
S:string;
begin
            S
:='';
            
K:=0;
                        
            
i:=binsock.available;
            
            if 
i=(-1then
            begin
            
... ОШИБКА ПОЛУЧЕНИЯ ДАННЫХ
            end 
ELSE if i<>0 then
                                BEGIN
                                K
:=0;
                                
repeat
                                
while ((ConnectError=true) and (binsock.available<>0) and (chr(k)<>'@')) do
                                        
begin
                                        k 
:= binsock.read_byte;
                                        if 
k=(-1then 
                                                begin
                                                
... ОШИБКА ПОЛУЧЕНИЯ ДАННЫХ
                                                end 
else                                        
                                        if 
chr(k)<>'@' then S := chr(k);                                                                
                                        
end;
                                
until ((ConnectError=false) or (chr(k)='@'));
                                
END;
                                
            
ReadData:=S;                                                
end

Разкодирую данные в utf с помощью библиотеки proweb http://forum.boolean.name/showthread.php?t=8417
PHP код:

S:=base64_decode(S);
    
S:=encode(S'utf-8''ошибка'); 

P.S. Как правило работает на всех телефоннах

Gonzo 13.08.2012 14:09

Ответ: Lib_binsock: прием/отправка бинарных данных через сокет
 
Возможно ли открытие одновременно более одного соединения?


Часовой пояс GMT +4, время: 00:22.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot