Конференция "Сети" » Изображение по UDP
 
  • Troop (27.03.11 16:29) [0]
    Доброго времени суток.
    И снова я задам вопрос относительно UDP.

    Вот набросал Клиент и сервер на основе IdUDP.

    Клиент:


    unit Unit1;

    interface

    uses
     Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     Dialogs, IdUDPServer, XPMan, IdBaseComponent, IdComponent, IdUDPBase,
     IdUDPClient, StdCtrls, IdSocketHandle,Jpeg, ExtCtrls;

    type
     TForm1 = class(TForm)
       IdUDPClient1: TIdUDPClient;
       Button1: TButton;
       Image1: TImage;
       Memo: TMemo;
       procedure Button1Click(Sender: TObject);
       procedure GrabScreen();
       procedure FormCreate(Sender: TObject);
     private
       { Private declarations }
     public
       { Public declarations }
     end;

    var
     Form1: TForm1;
        Bitmap:TBitmap;
        Jpeg:TJpegImage;
        packNUM:integer;
    implementation

    {$R *.dfm}

    procedure TForm1.GrabScreen();
    var
    DC : HDC;
    bmp : TBitmap;
    jpgImg: TJPEGImage;
    Stream:TMemoryStream;
    size:integer;
    Buf:array[0..1024] of byte;
    begin
    Stream:=TMemoryStream.Create;
    bmp := TBitmap.Create;
    jpgImg := TJPEGImage.Create;
    bmp.Height := Screen.Height;
    bmp.Width := Screen.Width;
    DC := GetDC(0);
    bitblt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height, DC, 0, 0, SRCCOPY);
    ReleaseDC(0, DC);
    jpgImg.Assign(bmp);
    jpgImg.CompressionQuality:=100;

    jpgImg.SaveToStream(Stream);

        Stream.Position:=0;
        size:=Stream.Size;
        IdUDPClient1.Send('text'+IntToStr(Size));
        packNUM:=0;
        while Stream.Position<Stream.Size do
       begin
          packNUM:=packNUM+1;
          size:=Stream.Read(Buf,SizeOf(Buf));
          IdUDPClient1.SendBuffer(Buf,size);
          Memo.Lines.Add(IntToStr(Stream.Position)+' < '+IntToStr(Stream.Size)+'  пакет №  '+IntToStr(packNUM));
      end;

    bmp.Assign(jpgImg);
    jpgImg.Free;
     Form1.Image1.Picture.Bitmap.Assign(bmp);
     bmp.Free;
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    begin
    GrabScreen();
    end;

    procedure TForm1.FormCreate(Sender: TObject);
    begin
     Bitmap:=TBitmap.Create;
      Jpeg:=TJpegImage.Create;
    end;

    end.



    Сервер


    unit Unit1;

    interface

    uses
    Windows, Messages, SysUtils, Classes, Graphics, Forms,
    ExtCtrls, StdCtrls, ScktComp, Controls,dialogs, ComCtrls,
    IdUDPServer, XPMan, IdBaseComponent, IdComponent, IdUDPBase,
     IdUDPClient, IdSocketHandle, math, Jpeg;

    type
     TForm1 = class(TForm)
       IdUDPServer1: TIdUDPServer;
       Memo: TMemo;
       Image1: TImage;
       Button1: TButton;
       procedure IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
         ABinding: TIdSocketHandle);
       procedure Button1Click(Sender: TObject);
     private
       { Private declarations }
     public
       { Public declarations }
     end;

    var
     Form1: TForm1;
     toread:integer;
     MainSize:integer;
       Size:integer;
       proverka:integer;
         StrStream  : TStringStream;
     MemStream  : TMemoryStream;
     packNUM:integer;
    implementation

    {$R *.dfm}

    procedure TForm1.IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
     ABinding: TIdSocketHandle);

    var
      Buf:array[0..1023] of byte;
       Jpeg:TJpegImage;
          Bitmap:TBitmap;

    begin
     // здесь происходит обработка данных пришедших от клиента
    //все что длинной до 10  я считаю текстом, а что
     // больше 10, то это уже изображение

     if AData.Size<=10 then
     begin
     packNUM:=0;
       // итак к нам пришел текст (размер изображения), работаем с потоком строк
       // создаем строковый поток
       StrStream:=TStringStream.Create('');
       // читаем данные в этот поток из пришедшего потока
       StrStream.CopyFrom(AData, AData.Size);
       // добавляем данные в приемник, указывая перед этим от кого этот текст
       Memo.Lines.Add(ABinding.PeerIP+': '+copy(StrStream.DataString,5,length(StrStream.DataString)));

       //Переменная, хранящая размер изображения
       MainSize:=StrToInt(copy(StrStream.DataString,5,length(StrStream.DataString)));

       // освобождаем память
       StrStream.Free;

       // Поток для изображения
       MemStream:= TMemoryStream.Create;
        MemStream.Size:=MainSize;
        MemStream.Position:=0;
          ToRead:=MemStream.Size-MemStream.Position;
          ToRead:=Min(ToRead,AData.size);

     end else

    // Прием изображения

     begin
    // Memo.Lines.Add(IntToStr(Adata.Size));
          if (ToRead>0) then
          begin
            packNUM:=packNUM+1;
            Size:=Adata.Size;
            if Size<0 then exit;
            MemStream.WriteBuffer(Adata,Size);
            ToRead:=MemStream.Size-MemStream.Position;
            ToRead:=Min(ToRead,Adata.Size);
            Memo.Lines.Add(IntToStr(Adata.Size) + '  пакет №  '+IntToStr(packNUM));
          end;
     end;
    end;

    //Кнопка, которая должна поместить изображение в image1

    procedure TForm1.Button1Click(Sender: TObject);
    var
          Jpeg:TJpegImage;
          Bitmap:TBitmap;
    begin
    MemStream.Position:=0;
          Jpeg:=TJpegImage.Create;
          Bitmap:=TBitmap.Create;
          try
            Jpeg.LoadFromStream(MemStream);
            Jpeg.SaveToFile('C:\Screenshot.jpg');
            Bitmap.Assign(Jpeg);
          finally
          Form1.Image1.Picture.Bitmap.Assign(Bitmap);
            Jpeg.Free;
            Bitmap.Free;
          end;
    end;

    end.



    Клиент отсылает изображение на сервер, послав предварительно размер изображения. Компоненты Memo я добавил просто для отслеживания отосланных и принятых пакетов и их размеров.

    Все отсылается и вроде даже доходит, при нажатии на кнопку принятое изображение должно поместиться в Jpeg, но  выдает ошибку Jpeg 53 (неверный формат).
    возможно я не вижу банальной ошибки...
  • CrytoGen (27.03.11 17:26) [1]
    зачем это
        MemStream.Size:=MainSize;?

    а вот это хрень полная
    Memo.Lines.Add(ABinding.PeerIP+': '+copy(StrStream.DataString,5,length(StrStream.DataString)));

    и Memo нельзя использовать в IdUDPServer1UDPRead
  • Troop (27.03.11 17:30) [2]
    MemStream.Size:=MainSize; - лишняя строчка, забыл удалить.
    Memo это так для себя, его отсутствие не уберет проблему.
  • Troop (27.03.11 18:31) [3]
    Большая часть взята из программы господина Slym'а только переделана под UDP, возможно я где то накосячил
  • Anatoly Podgoretsky © (27.03.11 18:42) [4]
    > Troop  (27.03.2011 17:30:02)  [2]

    Сколько еще строчек, которые ты забыл и мы про это даже не догадываемся?
  • Anatoly Podgoretsky © (27.03.11 18:43) [5]
    Не надо нам гнать пургу, неизвестно, что в ответ тоже получишь пургу, ты публикуй только тот код, что у тебя есть, без какой либо отсебятины.
  • Troop (27.03.11 18:51) [6]
    Я и опубликовал. Размер отсылаемого потока, совпадает с размером полученного, т.е. все доходит, но в Jpeg вогнать не получается
  • Anatoly Podgoretsky © (27.03.11 19:02) [7]
    > Troop  (27.03.2011 18:51:06)  [6]

    Ты опубликовал совсем другой код, сам же подтвердил это. Так ты каши не
    сваришь.
  • Troop (27.03.11 19:09) [8]
    http://pda.delphimaster.net/?id=1227604925&n=4&p=1
    Тут изначальный код, используются компоненты ClientSocket и ServerSocket, я написал программу с нуля, просто принцип разбиения и передачи от туда (хотя он везде по существу одинаковый). Там онлайн передача, я же пока пытаюсь передать только единичное изображение рабочего стола. Вроде как все передает, но не тут то было.
  • sniknik © (27.03.11 19:15) [9]
    > т.е. все доходит
    UDP это такая странная штука, что может не только не доходить, но и доходить не по порядку. и это вполне нормально. т.е. пропуски/порядок это все ложится на "совесть" программиста.

    и кстати
    +
    > if AData.Size<=10 then
    а где гарантии, что размер данных всегда больше 10? особенно последний кусок.

    ++
    размер буфера в UDP по умолчанию 8кб (в компоненте стоит), и насколько помню есть условия когда в принципе меньше не посылается, даже если посыл в 1 байт... а у тебя выбран буфер = 1кб + 1б.
    странно. (ну хотя бы круглую цифру... а то часть системного всегда будет пустой, хорошо если он всего 16 байт... но все же)
  • Troop (27.03.11 19:18) [10]
    MemStream.Size:=MainSize; В этой строчки я задаю размер потока на прием (Предварительно на клиенте перед посылкой изображения отсылается размер потока)
  • Troop (27.03.11 19:24) [11]
    if AData.Size<=10 да насчет этого я знаю, но обычно последний покет не меньше 500, хотя это легко исправить просто поставить boolean выражение, например if first_=true then , я пока рассматриваю отсыл только одного изображения.

    Просто понять не могу в чем ошибка, знаю что UDP не надежен. Но как то писал отсыл файла через udp использую TFileStream, там кол-во пакетов доходило до несколько десятков тысяч, и в конечном счете файл доходил и открывался, а тут тоже вроде изображение доходит, но нечетабельно о_О
  • sniknik © (27.03.11 19:59) [12]
    > Просто понять не могу в чем ошибка
    это то как раз элементарно... в разных данных там и здесь. очень просто можно убедится...
    вот вместо этого
    Jpeg.SaveToFile('C:\Screenshot.jpg');
    сделать
    MemStream.Position:= 0;
    MemStream.SaveToFile('C:\ScreenServ.jpg');

    и в клиенте
    jpgImg.SaveToStream(Stream);
    Stream.SaveToFile('C:\ScreenClient.jpg');
    а после сравнить, можно побайтно... (есть проги)

    т.е. то что передаешь, в самом начале, с тем что принято, в самом конце. уверен будет разница.

    > а тут тоже вроде изображение доходит
    комп не терпит слов "вроде". должно быть "точно" вплоть до последнего байта.
  • CrytoGen (27.03.11 20:02) [13]
    так делать не надо
    MemStream.Size:=MainSize;
  • Troop (27.03.11 20:11) [14]
    Без MemStream.Size:=MainSize; тоже самое
  • Troop (27.03.11 20:14) [15]
    Вот переписал немного, теперь на клиенте и на сервере стоит компонент TidUDPServer, и шлется уведомление о том, что пакет доставлен, пока пакет не дойдет, другие отсылаться не будут, т.е. все пакеты доставляются по порядку, НО ошибка 53 все равно осталась =(

    клиент


    unit Unit1;

    interface

    uses
     Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     Dialogs, IdUDPServer, XPMan, IdBaseComponent, IdComponent, IdUDPBase,
     IdUDPClient, StdCtrls, IdSocketHandle,Jpeg, ExtCtrls;

    type
     TForm1 = class(TForm)
       Button1: TButton;
       Image1: TImage;
       Memo: TMemo;
       IdUDPClient1: TIdUDPServer;
       procedure Button1Click(Sender: TObject);
       procedure GrabScreen();
       procedure FormCreate(Sender: TObject);
       procedure IdUDPClient1UDPRead(Sender: TObject; AData: TStream;
         ABinding: TIdSocketHandle);
     private
       { Private declarations }
     public
       { Public declarations }
     end;

    var
     Form1: TForm1;

       get_accept: boolean = false;

        Bitmap:TBitmap;
        Jpeg:TJpegImage;
        packNUM:integer;
    implementation

    {$R *.dfm}

    procedure TForm1.GrabScreen();
    var
    DC : HDC;
    bmp : TBitmap;
    jpgImg: TJPEGImage;
    Stream:TMemoryStream;
    size:integer;
    Buf:array[0..1024] of byte;
    begin
    Stream:=TMemoryStream.Create;
    bmp := TBitmap.Create;
    jpgImg := TJPEGImage.Create;
    bmp.Height := Screen.Height;
    bmp.Width := Screen.Width;
    DC := GetDC(0);
    bitblt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height, DC, 0, 0, SRCCOPY);
    ReleaseDC(0, DC);
    jpgImg.Assign(bmp);
    jpgImg.CompressionQuality:=100;

    jpgImg.SaveToStream(Stream);

        Stream.Position:=0;
        size:=Stream.Size;
            IdUDPClient1.Send('localhost',1151,'text'+IntToStr(Size));
        packNUM:=0;
        while Stream.Position<Stream.Size do
       begin
          packNUM:=packNUM+1;
          size:=Stream.Read(Buf,SizeOf(Buf));
          IdUDPClient1.SendBuffer('localhost',1151,Buf,size);
              while get_accept=false do Application.ProcessMessages;
          get_accept:=false;
          Memo.Lines.Add(IntToStr(Stream.Position)+' < '+IntToStr(Stream.Size)+'  пакет №  '+IntToStr(packNUM));
      end;

    bmp.Assign(jpgImg);
    jpgImg.Free;
     Form1.Image1.Picture.Bitmap.Assign(bmp);
     bmp.Free;
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    begin
    GrabScreen();
    end;

    procedure TForm1.FormCreate(Sender: TObject);
    begin
     Bitmap:=TBitmap.Create;
      Jpeg:=TJpegImage.Create;
    end;

    procedure TForm1.IdUDPClient1UDPRead(Sender: TObject; AData: TStream;
     ABinding: TIdSocketHandle);
    begin
      get_accept:=true;
    end;

    end.




    Сервер


    unit Unit1;

    interface

    uses
    Windows, Messages, SysUtils, Classes, Graphics, Forms,
    ExtCtrls, StdCtrls, ScktComp, Controls,dialogs, ComCtrls,
    IdUDPServer, XPMan, IdBaseComponent, IdComponent, IdUDPBase,
     IdUDPClient, IdSocketHandle, math, Jpeg;

    type
     TForm1 = class(TForm)
       IdUDPServer1: TIdUDPServer;
       Memo: TMemo;
       Image1: TImage;
       Button1: TButton;
       Label1: TLabel;
       procedure IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
         ABinding: TIdSocketHandle);
       procedure Button1Click(Sender: TObject);
     private
       { Private declarations }
     public
       { Public declarations }
     end;

    var
     Form1: TForm1;
     toread:integer;
     MainSize:integer;
       Size:integer;
       proverka:integer;
         StrStream  : TStringStream;
     MemStream  : TMemoryStream;
     packNUM:integer;
     SStream:TStream;
    implementation

    {$R *.dfm}

    procedure TForm1.IdUDPServer1UDPRead(Sender: TObject; AData: TStream;
     ABinding: TIdSocketHandle);

    var
      Buf:array[0..1023] of byte;
       Jpeg:TJpegImage;
          Bitmap:TBitmap;

    begin
     // здесь происходит обработка данных пришедших от клиента
    //все что длинной до 10  я считаю текстом, а что
     // больше 10, то это уже изображение

     if AData.Size<=10 then
     begin
     packNUM:=0;
       // итак к нам пришел текст (размер изображения), работаем с потоком строк
       // создаем строковый поток
       StrStream:=TStringStream.Create('');
       // читаем данные в этот поток из пришедшего потока
       StrStream.CopyFrom(AData, AData.Size);
       // добавляем данные в приемник, указывая перед этим от кого этот текст
       //Memo.Lines.Add(ABinding.PeerIP+': '+copy(StrStream.DataString,5,length(StrStream.DataString)));

       //Переменная, хранящая размер изображения
       MainSize:=StrToInt(copy(StrStream.DataString,5,length(StrStream.DataString)));

       // освобождаем память
       StrStream.Free;

       // Поток для изображения
       MemStream:= TMemoryStream.Create;
        MemStream.Size:=MainSize;
        MemStream.Position:=0;
          ToRead:=MemStream.Size-MemStream.Position;
          ToRead:=Min(ToRead,AData.size);

     end else

    // Прием изображения

     begin
    // Memo.Lines.Add(IntToStr(Adata.Size));
         // if (ToRead>0) then
         // begin
            packNUM:=packNUM+1;
            Size:=Adata.Size;
            if Size<0 then exit;
            MemStream.WriteBuffer(Adata,Size);
            ToRead:=MemStream.Size-MemStream.Position;
            ToRead:=Min(ToRead,Adata.Size);
            //Memo.Lines.Add(IntToStr(Adata.Size) + '  пакет №  '+IntToStr(packNUM));
            Memo.Lines.Add(IntToStr(MemStream.Position));
            idUDPServer1.Send('localhost',1152,'accept');
         // end;
     end;
    end;

    //Кнопка, которая должна поместить изображение в image1

    procedure TForm1.Button1Click(Sender: TObject);
    var
          Jpeg:TJpegImage;
          Bitmap:TBitmap;
    begin
    MemStream.Position:=0;

          Jpeg:=TJpegImage.Create;
          Bitmap:=TBitmap.Create;
          SStream:=TStream.Create;
          SStream:=MemStream;
          try
          Jpeg.CompressionQuality:=100;
            label1.Caption:=IntToStr(MemStream.Position);
            Jpeg.LoadFromStream(SStream);
            Jpeg.SaveToFile('C:\Screenshot.jpg');
            Bitmap.Assign(Jpeg);
          finally
          Form1.Image1.Picture.Bitmap.Assign(Bitmap);
            Jpeg.Free;
            Bitmap.Free;
          end;
    end;

    end.


  • sniknik © (27.03.11 20:44) [16]
    > НО ошибка 53 все равно осталась =(
    сравни данные "до" и "после", как в [12], они все еще НЕ совпадают...

    p.s. на "кошках", что ли, бы потренировался... ну там текст из мемо в мемо пересылать... для потоков все одно, а намного виднее.
  • CrytoGen (27.03.11 20:47) [17]
    SStream:=TStream.Create;
     
    не нужно

    Вообще с таким подходом к написанию кода странно, что у вас вообще хоть что-то работает. Очень сложно понять порядок передачи. Почему вы размер передаёте строкой? При чтении строки вполне может попасть пакет с изображением.
    Сохраните полученные данные в файл, чтобы сравнить с отправленными данными, строить здесь предположения очень сложно - ошибок много.
  • Troop (27.03.11 21:00) [18]
    Это тоже для проверки создавалось =) можно убрать.

    P.S. Ошибка решена, и она была очень глупой =). Нельзя было считывать напрямую из AData в поток, необходимо было с начало из AData считать в буффер (переменная Buf (размер на сервере надо изменить 0..1024)), а потом уже записать буффер в поток MemStream.
  • sniknik © (27.03.11 22:17) [19]
    > Нельзя было считывать напрямую из AData в поток
    чушь. всегда так делал, ошибок, чтобы чего то неверно/криво передавалось не было.

    > размер на сервере надо изменить 0..1024
    а вот это верно.
    еще в [9] писал про странный "не круглый" размер буфера, на клиенте, вот ты там где то и путал, "откусывал" лишний байт видать.
    опять выбрал "не круглый"... но хоть одинаковый с клиентом.
  • Troop (28.03.11 01:20) [20]
    Да нет, не из за размера, заметь, у меня переменная Buf висела просто, она не использовалась при чтении на сервере, а переделал я вот эту часть



            packNUM:=packNUM+1;
            Adata.Read(Buf,sizeof(Buf));
            if Size<0 then exit;
            MemStream.WriteBuffer(Buf,sizeof(Buf));
            Memo.Lines.Add(IntToStr(MemStream.Position));
            idUDPServer1.Send('localhost',1152,'accept');


  • sniknik © (28.03.11 07:47) [21]
    т.е. выкинул странные расчеты размера для следующего чтения, но оставил условие выхода по нему? т.е. от не определенной переменной... ну тогда чудо, что работает.
  • CrytoGen (28.03.11 08:31) [22]
    А вы товарищ, Warnings читаете? :)
  • Troop (28.03.11 11:53) [23]
    условие ToRead понадобится когда будет отсылаться не одно изображение, а множество, т.е. онлайн просмотр рабочего стола.
  • Leonw (01.12.11 10:41) [24]
    Смотри:
    if AData.Size<=10 then

    я делал для того чтобы IP автоматом грузился на сервер
    if AData.Size<=15 then

    почему 15 потому что = 192,168,123,123. ну или 16 там указал. даже при сравнении размера, УДП всеровно выдает ошибку.
    Если кто знает, как картинку кусками передавать? Спасибо!
  • Chaser (11.01.12 20:52) [25]
    Вот, есть готовый пример: http://www.majento.ru/_hidadmin/
 
Конференция "Сети" » Изображение по UDP
Есть новые Нет новых   [134435   +15][b:0][p:0.006]