Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Работа с сетью в Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 17.10.2011, 17:36   #1
Stepan_AVR
 
Регистрация: 17.10.2011
Сообщений: 3
По умолчанию Отправка картинки через сокет

Добрый день! Ребят прошу помощи у вас, уже почти неделю бьюсь над этой проблемой, понимаю что подобных тем полно, но перепробовал кучу вариантов и проблема так и не решена!
Итак имеется клиент-серверное приложение, использовал стандартные компоненты TServerSocket, TClientSocket для работы с сокетами(сокеты использую в неблокирующем режиме); использую Delphi XE. Организовал обмен текстовыми сообщениями между клиентом и сервером и все было прекрасно, но тут встала проблема - мне необходимо передавать с клиента на сервер скрин экрана, перечитав кучу всего, и гугля бесконечно написал вот что:
Итак клиентская часть:
Код:
  
  Bitmap:= TBitmap.Create;
  MS:= TMemoryStream.Create;
  Bitmap.Width:= Screen.Width;
  Bitmap.Height:= Screen.Height;
  BitBlt(Bitmap.Canvas.Handle,0,0, Screen.Width, Screen.Height,
  GetDC(GetDesktopWindow),0,0,SRCCOPY);
  Bitmap.SaveToStream(MS);
  Bitmap.SaveToFile('n.bmp');
  MS.Position:= 0;
  ClientSocket1.Socket.SendText(IntToStr(MS.Size)+#0);
  ClientSocket1.Socket.SendStream(MS);
Далее код сервера:
Код:
var
sl,s: string;
begin
  s:= Socket.ReceiveText;
  if not Reciving then begin
    SetLength(sl, StrLen(PChar(s))+1); 
    StrLCopy(@sl[1], PChar(s), Length(sl)-1);
    DataSize:= StrToInt(sl);
    Form1.Caption:= sl;
    MS:= TMemoryStream.Create;
    Delete(s, 1, Length(sl));
    Reciving:= true;
  end;
    MS.Write(s[1],length(s));
    Form1.Caption:= 'Принято '+IntToStr(MS.Size)+' из '+IntToStr(DataSize);;
    if MS.Size = DataSize then begin
      MS.Position:= 0;
      MS.SaveToFile('n.bmp');
      Form2.Image1.Picture.Bitmap.LoadFromStream(MS);
      Reciving:= false;
      GetScr:= false;
      Form2.ShowModal
    end;
Вот и все это безобразие даже работает и передает данные, но при загрузке картинки из переданного потока в image, вылетает ошибка
"Bitmap image is not valid".. Смысл ошибки понимаю, видно пакеты режутся на части и искажаются при передаче! И собственно вопрос как можно корректно передавать данные в таком случае?
PS. Размер потока данных со скрином составляет порядка нескольких мегабайт(4-8).

Последний раз редактировалось Stepan_AVR; 17.10.2011 в 17:57.
Stepan_AVR вне форума Ответить с цитированием
Старый 17.10.2011, 20:12   #2
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,426
По умолчанию

открываете картинку TFileStream и режете на куски и кусками передаёте воттакой рекорд:
Код:
type
  TDataRec=record
  Buff:array[0..32000] of Byte; //кусок картинки
  BuffOffset:Cardinal; //Место, куда его приклеить
и передаёте через указатели.

На сервере, ловите, создаёте файл стрим, и пишете буфер в указанный офсет.
Человек_Борща вне форума Ответить с цитированием
Старый 20.10.2011, 22:41   #3
Stepan_AVR
 
Регистрация: 17.10.2011
Сообщений: 3
Плохо

Цитата:
Сообщение от Человек_Борща Посмотреть сообщение
открываете картинку TFileStream и режете на куски и кусками передаёте воттакой рекорд:
Код:
type
  TDataRec=record
  Buff:array[0..32000] of Byte; //кусок картинки
  BuffOffset:Cardinal; //Место, куда его приклеить
и передаёте через указатели.

На сервере, ловите, создаёте файл стрим, и пишете буфер в указанный офсет.
Ну вот хорошо написал почти как вы сказали, но вылетают ошибки на сервер вроде out of memory, не подскажете в чем косяк?

Код сервера:
Код:
type
TDataRec=record
Buff: array[0..2048] of byte;
BuffOffset: Int64;
BuffSize: Int64;
end;
var
size: Int64;
sl,s: widestring;
DataRec: TDataRec;
begin
  if not Reciving then begin
    s:= Socket.ReceiveText;
    SetLength(sl, StrLen(PChar(s))+1); // +1 for the null terminator
    StrLCopy(@sl[1], PChar(s), Length(sl)-1);
    DataSize:= StrToInt(sl);
    MS:= TMemoryStream.Create;
    MS.SetSize(DataSize);
    Delete(s, 1, Length(sl));
    Reciving:= true;
    AlrDown:= 0;
  end else begin
    Socket.ReceiveBuf(DataRec,sizeof(DataRec));
    MS.Seek(DataRec.BuffOffset,soFromEnd);
    Form1.Caption:= 'Принято '+IntToStr(AlrDown)+' из '+IntToStr(DataSize);
    MS.Write(DataRec.Buff,DataRec.BuffSize);
    AlrDown:= AlrDown+DataRec.BuffSize;
    if AlrDown = DataSize then begin
      MS.Position:= 0;
      MS.SaveToFile('n.bmp');
      Form2.Image1.Picture.Bitmap.LoadFromStream(MS);
      Reciving:= false;
      GetScr:= false;
      Form2.ShowModal;
    end;
    end;
Код клиента:
Код:
  Bitmap:= TBitmap.Create;
  MS:= TMemoryStream.Create;
  Bitmap.Width:= Screen.Width;
  Bitmap.Height:= Screen.Height;
  BitBlt(Bitmap.Canvas.Handle,0,0, Screen.Width, Screen.Height,
  GetDC(GetDesktopWindow),0,0,SRCCOPY);
  Bitmap.SaveToStream(MS);
  MS.Position:= 0;
  n:= MS.Size div 2048;
  if MS.Size mod 2048<>0 then
  inc(n);
  ClientSocket1.Socket.SendText(IntToStr(MS.Size)+#0);
  for k:= 1 to n do begin
  DataRec.BuffOffset:= MS.Position;
  if i<>n then begin
  DataRec.BuffSize:= 2048;
  MS.Read(DataRec.Buff,2048);
  MS.Seek(MS.Size-2048*k,soFromEnd);
  end else begin
  MS.Read(DataRec.Buff,MS.Size-MS.Position);
  DataRec.BuffSize:= MS.Size-MS.Position;
  end;
  PDataRec:= @DataRec;
  ClientSocket1.Socket.Sendbuf(PDataRec^,sizeof(DataRec));
Stepan_AVR вне форума Ответить с цитированием
Старый 20.10.2011, 22:56   #4
Человек_Борща
Старожил
 
Аватар для Человек_Борща
 
Регистрация: 30.12.2009
Сообщений: 11,426
По умолчанию

Думаете, разумно передовать по 2048 байт(2 кб)?

Ваша логика кода, меня просто убивает...
Код:
begin
  if not Reciving then begin
    Reciving:= true;
  end else begin
      Reciving:= false;
    end;
    end;
Зачем так извращяться?

Думаю разумно забить на передачу текста, и использовать только буфер.
Ставя в начало какой-то знак(Идентификатор), того что после него...
Код:
const
  meString=#255#255+'Str'+#255#255;
  meBin=#255#255+'Bin'+#255#255;
var
 Buff:Char;
begin
 Buff:=meString+PChar('THIS IS A STRING!!111111111111');
 Client.Socket.SendFuf(@Buff,SizeOf(Buff));
 
 Buff:=meInt+PChar(IntToStr('1234567890'));
 Client.Socket.SendFuf(@Buff,SizeOf(Buff));
end;
На сервере, принимайте читайте первых 7 байт, и там смотрите что после этого будет..
Это как вариант..


А память теряете потому, что насоздавали MemorySteam'ов ...но нигде нет ни слова об MS.FREE; ни в клиенте ни в сервере...

Последний раз редактировалось Человек_Борща; 20.10.2011 в 23:04.
Человек_Борща вне форума Ответить с цитированием
Старый 28.10.2011, 16:58   #5
Stepan_AVR
 
Регистрация: 17.10.2011
Сообщений: 3
По умолчанию

Короче после долгих экспериментов изменил код и все даже заработало! Но только на локальной машине! При передачи данных по сети все равно вы летают ошибки! Гляньте по коду может чего подскажете..

Код сервера:

Код клиента:
Код:
Bitmap:= TBitmap.Create;
  MS:= TMemoryStream.Create;
  Bitmap.Width:= Screen.Width;
  Bitmap.Height:= Screen.Height;
  BitBlt(Bitmap.Canvas.Handle,0,0, Screen.Width, Screen.Height,
  GetDC(GetDesktopWindow),0,0,SRCCOPY);
  Bitmap.SaveToStream(MS);
  Bitmap.Free;
  MS.Position:= 0;
  n:= MS.Size div 4095; //определяем количество кусков
  if MS.Size mod 4095<>0 then //если будет кусок <2048 то + 1 пакет
  inc(n);
  ClientSocket1.Socket.SendText(IntToStr(MS.Size)+#0);
  for k:= 0 to n do begin
  DataRec.BuffOffset:= MS.Position;
  if k<>n then begin //для целых пакетов
  DataRec.BuffSize:= 4095;
  MS.Read(DataRec.Buff[0],4095);
  MS.Seek(4095*k,soFromBeginning);
  end else begin
  DataRec.BuffSize:= MS.Size-MS.Position;
  MS.Read(DataRec.Buff[0],MS.Size-MS.Position);
  end;
  PDataRec:= @DataRec;
  ClientSocket1.Socket.Sendbuf(PDataRec^,sizeof(DataRec));
  sleep(1);
  end;
MS.Free;
end;
Код сервера:
Код:
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
type
TDataRec=record
Buff: array[0..4095] of byte;
BuffOffset: Int64;
BuffSize: Int64;
end;
var
size: Int64;
sl,s: widestring;
DataRec: TDataRec;
begin
  if not Reciving then begin
    s:= Socket.ReceiveText;
    SetLength(sl, StrLen(PChar(s))+1); // +1 for the null terminator
    StrLCopy(@sl[1], PChar(s), Length(sl)-1);
    DataSize:= StrToInt(sl);
    MS:= TMemoryStream.Create;
    MS.SetSize(DataSize);
    Delete(s, 1, Length(sl));
    Reciving:= true;
    AlrDown:= 0;
    exit;
  end else begin
    Socket.ReceiveBuf(DataRec,sizeof(DataRec));
    MS.Seek(DataRec.BuffOffset,soFromBeginning);
    MS.Write(DataRec.Buff[0],DataRec.BuffSize);
    AlrDown:= AlrDown+DataRec.BuffSize;
    Form1.Caption:= 'Принято '+IntToStr(AlrDown)+' из '+IntToStr(DataSize);
    if AlrDown = DataSize then begin
      MS.Position:= 0;
      Form2.Image1.Picture.Bitmap.LoadFromStream(MS);
      MS.Free;
      Reciving:= false;
      GetScr:= false;
      Form2.ShowModal;
    end;
    end;
end;
PS При увеличении размера буфера вылетают ошибки, так же в коде клиента есть задержка в одну мс так вот если ее убрать то тоже вылетают ошибки! Про то что вы писали, да это конечно красиво было бы если все данные отправлять массивами с идентификаторами вначале но мне тогда переделывать все логику серверной части, что я делать не очень хочу! И кстати ошибка была не в том что я потоки не очищал а в том что в процедуре write указывал на сам буффер а не на первый элемент как правильно!!
Stepan_AVR вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Передача переменной через сокет Yura_S Общие вопросы Delphi 3 25.03.2010 09:53
Передавать файл через сокет частями NieL Работа с сетью в Delphi 0 08.12.2009 16:42
Данные через сокет по tcp/ip протоколу a.haener JavaScript, Ajax 1 04.07.2009 08:14
Не работает отправка файла через сокет SARGE Работа с сетью в Delphi 0 29.01.2009 13:29
Передача файла через сокет Delphi Unconnected Работа с сетью в Delphi 4 23.12.2008 16:16