Конференция "Сети" » клиент-сервер через UDP [D7]
 
  • Troop (22.03.11 19:07) [0]
    Здравствуйте.
    Хочу задать вопрос насчет управления рабочим столом http://pda.delphimaster.net/?id=1227604925&n=4&p=1
    Невозможно заставить TClientSocket и TServerSocket осуществлять передачи через UDP. Я пытался переделать программу на  TIdUDPClient и TIdUDPServer, но ничего не вышло (видно я слишком глуп). Не могли бы вы подсказать способ передавать данные в этой программе через UDP протокол.

    Клиент:



    unit ClientUnit;

    interface

    uses
    Windows, Messages, SysUtils, Classes, Graphics, Forms,
    ExtCtrls, StdCtrls, ScktComp, Controls,dialogs;

    type
    TClientThread=class(TThread)
    private
      ClientSocket: TClientSocket;
      LastUpdate:DWORD;
      Bitmap:TBitmap;
      Traffic:integer;
    protected
      procedure UpdateScreen;
      procedure Execute;override;
    public
      constructor Create(CreateSuspended: Boolean;const Address:string);
      destructor Destroy;override;
    end;

    type
    TMouseEvent=(meNone,meMove,meClick,meDblClick,meDown,meUp);
    TRes=packed record
      X,Y:word;
    end;
    TCmd=packed record
      CmdType:word;
      Res:TRes;
      Param:DWORD;
    end;

    TForm1 = class(TForm)
      Panel1: TPanel;
      AddressEd: TEdit;
      Button1: TButton;
      Image: TImage;
      Button2: TButton;
      procedure Button1Click(Sender: TObject);
      procedure ImageMouseMove(Sender: TObject; Shift: TShiftState; X,
        Y: Integer);
      procedure ImageClick(Sender: TObject);
      procedure Button2Click(Sender: TObject);
      procedure FormCreate(Sender: TObject);
      procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    private
      ClientThread:TClientThread;
    public
      MousePos:TPoint;
      MouseEvent:TMouseEvent;
      MouseMoved:boolean;
      Msg:string;
      MsgReady:boolean;
    end;

    var
    Form1: TForm1;

    implementation
    uses math,Jpeg,JPEG_IO;
    {$R *.dfm}

    { TClientThread }

    constructor TClientThread.Create(CreateSuspended: Boolean;const Address:string);
    var i:integer;
    begin
    Bitmap:=TBitmap.Create;
    ClientSocket:=TClientSocket.Create(nil);
    ClientSocket.ClientType:=ctBlocking;
    i:=pos(':',Address);
    ClientSocket.Address:=copy(Address,1,i-1);
    ClientSocket.Port:=StrToInt(copy(Address,i+1,MaxInt));
    ClientSocket.Active:=true;
    inherited Create(CreateSuspended);
    end;

    destructor TClientThread.Destroy;
    begin
    ClientSocket.Free;
    Bitmap.Free;
    inherited;
    end;

    procedure TClientThread.Execute;
    var
    Socket:TClientWinSocket;
    ScreenStream:TMemoryStream;
    Cmd:word;
    ImgSize:TPoint;
    Buf:array[0..1023] of byte;
    size,ToRead:integer;
    begin
    Socket:=ClientSocket.Socket;
    try
      ScreenStream:=TMemoryStream.Create;
      try
        while Socket.Connected and not Terminated do
        begin
          ImgSize.X:=Form1.Image.Width;
          ImgSize.Y:=Form1.Image.Height;
          if Form1.MouseEvent=meMove then
          begin
            Cmd:=2;
            if Socket.SendBuf(Cmd,SizeOf(Cmd))<>SizeOf(Cmd) then exit;
            if Socket.SendBuf(ImgSize,SizeOf(ImgSize))<>SizeOf(ImgSize) then exit;
            if Socket.SendBuf(Form1.MousePos,SizeOf(Form1.MousePos))<>SizeOf(Form1.MousePos) then exit;
            Form1.MouseMoved:=false;
          end;
          if Form1.MsgReady then
          begin
            Cmd:=3;
            if Socket.SendBuf(Cmd,SizeOf(Cmd))<>SizeOf(Cmd) then exit;
            size:=Length(Form1.Msg);
            if Socket.SendBuf(size,SizeOf(size))<>SizeOf(size) then exit;
            if Socket.SendBuf(PChar(Form1.Msg)^,size)<>size then exit;

            if Socket.ReceiveBuf(size,SizeOf(size))<>SizeOf(size) then exit;
            if size>0 then
              if Socket.ReceiveBuf(PChar(Form1.Msg)^,size)<>size then exit;
                MessageBox(0, PChar(Form1.Msg),'Отвед',MB_OK);
            Form1.MsgReady:=false;
          end;

          Cmd:=1;
          if Socket.SendBuf(Cmd,SizeOf(Cmd))<>SizeOf(Cmd) then exit;
          if Socket.SendBuf(ImgSize,SizeOf(ImgSize))<>SizeOf(ImgSize) then exit;
          if Socket.ReceiveBuf(size,SizeOf(size))<>4 then exit;
          if (Size<0) or (Size>maxInt-1) then exit;
          ScreenStream.Size:=Size;
          ScreenStream.Position:=0;
          ToRead:=ScreenStream.Size-ScreenStream.Position;
          ToRead:=Min(ToRead,Length(Buf));
          while (ToRead>0) and Socket.Connected do
          begin
            Size:=Socket.ReceiveBuf(Buf,ToRead);
            if Size<0 then exit;
            ScreenStream.WriteBuffer(Buf,Size);
            ToRead:=ScreenStream.Size-ScreenStream.Position;
            ToRead:=Min(ToRead,Length(Buf));
          end;
          ScreenStream.Position:=0;
          Traffic:=ScreenStream.Size;
          LoadBmpFromJpegStream(Bitmap,ScreenStream);
          Synchronize(UpdateScreen);
        end;
      finally
        ScreenStream.Free;
      end;
    finally
      Socket.Close;
    end;
    end;

    procedure TClientThread.UpdateScreen;
    var tick:DWORD;
    begin
    Form1.Image.Picture.Bitmap.Assign(Bitmap);
    tick:=GetTickCount;
    Form1.Image.Canvas.Brush.Style:=bsClear;
    Form1.Image.Canvas.TextOut(1,1,Format('Разрешение: %d:%d, cкорость: %f кадр/сек, трафик %d байт/кадр',
      [Bitmap.Width,Bitmap.Height,1000/(tick-LastUpdate),Traffic]));
    LastUpdate:=tick;
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    begin
    ClientThread:=TClientThread.Create(true,AddressEd.Text);
    ClientThread.FreeOnTerminate:=true;
    ClientThread.Priority:=tpLower;
    ClientThread.Resume;
    end;

    procedure TForm1.ImageMouseMove(Sender: TObject; Shift: TShiftState; X,
    Y: Integer);
    begin
    MousePos.X:=x;
    MousePos.Y:=y;
    MouseMoved:=true;
    end;

    procedure TForm1.ImageClick(Sender: TObject);
    begin
    MouseEvent:=meClick;
    end;

    procedure TForm1.Button2Click(Sender: TObject);
    begin
    MsgReady:=InputQuery('Input Box', 'Prompt', Msg);
    end;

    procedure TForm1.FormCreate(Sender: TObject);
    begin
    ClientThread:=nil;
    end;

    procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    begin
    if assigned(ClientThread) then
    begin
      ClientThread.Terminate;
      ClientThread:=nil;
    end;
    end;

    end.


  • Troop (22.03.11 19:08) [1]
    Сервер



    program NetScreenServerCon;
    {$apptype console}

    uses
    Windows,
    SysUtils,
    Classes,
    ScktComp,
    Graphics,
    Dialogs,
    JPEG_IO in 'JPEG_IO\JPEG_IO.pas',
    IJL in 'JPEG_IO\ijl.pas';

    type
    TSCThread=class(TServerClientThread)
    private
      Bitmap:TBitmap;
    protected
      procedure GrabScreen(Stream:TMemoryStream;const ImgSize:TPoint);
      procedure SetMouse(const ImgSize,MousePoint:TPoint);
      procedure ClientExecute; override;
    public
      constructor Create(CreateSuspended: Boolean; ASocket: TServerClientWinSocket);
      destructor Destroy; override;
    end;

    constructor TSCThread.Create(CreateSuspended: Boolean;
    ASocket: TServerClientWinSocket);
    begin
    Bitmap:=TBitmap.Create;
    bitmap.PixelFormat:=pf24bit;
    inherited;
    end;

    destructor TSCThread.Destroy;
    begin
    Bitmap.Free;
    inherited;
    end;

    procedure TSCThread.GrabScreen(Stream:TMemoryStream;const ImgSize:TPoint);
    var
    Desktop:HWnd;
    DesktopRect: TRect;
    DesktopDC:HDc;
    begin
    Desktop:=GetDesktopWindow;
    GetWindowRect(Desktop,DesktopRect);
    Bitmap.Height:=ImgSize.Y;
    Bitmap.Width:=ImgSize.X;
    DesktopDC:=GetWindowDC(Desktop);
    try
      Win32Check(StretchBlt(Bitmap.Canvas.Handle,0,0,Bitmap.Width,Bitmap.Height,
                DesktopDC,DesktopRect.Left,DesktopRect.Top,DesktopRect.Right,DesktopRect.Bottom,  SRCCOPY));
    finally
      ReleaseDC(Desktop,DesktopDC);
    end;
    SaveBmpToJpegStream(Bitmap,Stream,30);
    end;

    procedure TSCThread.SetMouse(const ImgSize, MousePoint: TPoint);
    var
    Desktop:HWnd;
    DesktopRect: TRect;
    begin
    Desktop:=GetDesktopWindow;
    GetWindowRect(Desktop,DesktopRect);
    SetCursorPos(round(DesktopRect.BottomRight.X/ImgSize.X*MousePoint.X),round(DesktopRect.BottomRight.Y/ImgSize.Y*MousePoint.Y));
    end;

    procedure TSCThread.ClientExecute;
    var
    ScreenStream:TMemoryStream;
    Buf:array[0..1023] of byte;
    Cmd:word;
    ImgSize,Mouse:TPoint;
    size:integer;
    s:string;
    begin
    while (not Terminated) and ClientSocket.Connected do
    try
      ScreenStream:=TMemoryStream.Create;
      try
        if ClientSocket.ReceiveBuf(Cmd,SizeOf(Cmd))<>SizeOf(Cmd) then exit;
        if Cmd=1 then
        begin
          if ClientSocket.ReceiveBuf(ImgSize,SizeOf(ImgSize))<>SizeOf(ImgSize) then exit;
          GrabScreen(ScreenStream,ImgSize);
          ScreenStream.Position:=0;
          size:=ScreenStream.Size;
          ClientSocket.SendBuf(size,SizeOf(size));
          while ScreenStream.Position<ScreenStream.Size do
          begin
            size:=ScreenStream.Read(Buf,SizeOf(Buf));
            if ClientSocket.SendBuf(Buf,size)<>size then exit;
          end;
        end;
        if Cmd=2 then
        begin
          if ClientSocket.ReceiveBuf(ImgSize,SizeOf(ImgSize))<>SizeOf(ImgSize) then exit;
          if ClientSocket.ReceiveBuf(Mouse,SizeOf(Mouse))<>SizeOf(Mouse) then exit;
          SetMouse(ImgSize,Mouse);
        end;
        if Cmd=3 then
        begin
          if ClientSocket.ReceiveBuf(size,SizeOf(size))<>SizeOf(size) then exit;
          SetLength(s,size);
          if ClientSocket.ReceiveBuf(PChar(s)^,size)<>size then exit;
          if MessageBox(0,PChar(s),'Ответить?',MB_YESNO)=ID_YES then
            InputQuery('Ответ','Ну давай',s) else s:='';
          size:=Length(s);
          if ClientSocket.SendBuf(size,SizeOf(size))<>SizeOf(size) then exit;
          if size>0 then
            if ClientSocket.SendBuf(PChar(s)^,size)<>size then exit;
        end;
      finally
        ScreenStream.Free;
      end;
    except
      Terminate;
      HandleException;
    end;
    end;

    function Proc2Method(Code,Data:pointer):TMethod;
    begin
    result.Code:=Code;
    result.Data:=Data;
    end;

    procedure GetThread(self,Sender: TObject; ClientSocket: TServerClientWinSocket;
      var SocketThread: TServerClientThread);
    begin
    SocketThread:=TSCThread.Create(true,ClientSocket);
    SocketThread.Priority:=tpLower;
    SocketThread.Resume;
    end;

    var Server:TServerSocket;

    begin
    Server:=TServerSocket.Create(nil);
    try
      Server.ServerType:=stThreadBlocking;
      Server.OnGetThread:=TGetThreadEvent(Proc2Method(@GetThread,Server));
      Server.Port:=1234;
      Server.Active:=true;
      while Server.Active do Sleep(100);
    finally
      Server.Free;
    end;
    end.


  • Troop (22.03.11 20:40) [2]
    Не обязательно по этим исходникам смотреть, было бы отлично если бы даже первоначальную версию из этой темы http://pda.delphimaster.net/?id=1227604925&n=4&p=1
    переделать на UDP.
  • Slym © (23.03.11 06:31) [3]
    UDP в отличие от TCP с подтверждением доставки, т.е. потеря пакета восстанавливается автоматически... в UDP потерял - забыл и никто тебе не скажет что что-то не так. а пакетики маленькие - 1 жипег в 10-50 пакетов, потеря 1 пакета = потере всех 50 т.к. картинка нечитаема.
    но выход есть!
    написать систему обнаружения потери и восстановления пакетов, последовательно нумеруя каждый пакетик
  • Slym © (23.03.11 06:34) [4]
    даже если забить на восстановление пакетов, нужен протокол уведомления того что клиент жив... а то получится что сервер флудит в сторону мертвого клиента
  • brother © (23.03.11 06:57) [5]
    почему именно UDP?
  • Сергей М. © (23.03.11 09:13) [6]

    > Troop


    В боевых условиях никакой UDP не справится с нагрузкой, которую ты возлагаешь на свою "систему". Гонишь по сети целый скрин, пусть даже и сжатый слегка, вместо того чтобы гнать сжатые опорные кадры и дельты.

    Не изобретай велосипед и востользуйся любым доступным зеркальным драйвером дисплея.
  • han_malign (23.03.11 11:15) [7]

    > написать систему обнаружения потери и восстановления пакетов

    http://ru.wikipedia.org/wiki/RUDP
    - имеет смысл только если не реализован TCP стек(например - если не хочется в датчик сухого контакта полноценную OS запихивать)...
    Но поскольку на прикладном уровне доступном автору - наличие TCP-стека гарантировано - иначе как мазохизмом это не назовешь...
  • Troop (23.03.11 18:37) [8]
    Спасибо всем, забью на UDP, просто руководитель проекта хочет, что бы шло через UDP, буду доказывать ему, что это совершенно невыгодно =)
  • brother © (24.03.11 05:29) [9]
    может наоборот, он хочет, что бы ты по возможности, как можно больше все ручками организовывал?
  • Anatoly Podgoretsky © (24.03.11 09:33) [10]

    > Troop   (23.03.11 18:37) [8]

    Глупо доказывать, это вполне выгодно. Будешь выглядеть смешно, как ламер.
  • han_malign (24.03.11 10:35) [11]

    > нужен протокол уведомления того что клиент жив... а то получится что сервер флудит в сторону мертвого клиента

    - в TCP - оно тоже надо, либо SO_KEEPALIVE(в windows сетях поддерживается, на некоторых древних *nix-ах может положить сеть), либо своими сообщениями...

    Когда одна из сторон молчит как рыба об лед - некоторые L3 маршрутизаторы(замечено за каталистами) - считают односторонний трафик мертвым(или злонамеренным) и банят его. (почему не учитываются ack-пакеты - непонятно - вероятно глюк)

    В случае пассивного TCP сервера(вполне нормальный профиль - Запрос/Ответ) без механизма тайм-аутов/рукопожатий, все вообще очень плохо:
    1. Если соединение в тихую рвется где-то за маршрутизатором(NIC не детектирует обрыв сети) - пассивное TCP-подключение молча ждет привета до скончания веков...
    т.к.:
    1.1. Восстановление подключения TCP подразумевает, что - пока не пришел FIN, RST, или артефактные SYN или ack/seq указатели - подключение живо и просто спит.
    1.2. Сброс по тайм-ауту TCP(зависит от провайдера/настроек; в windows - порядка 5-ти минут) произойдет только если послать данные и не получить подтверждение - а сервер у нас молчит...

    2. Пул серверных TCP-подключений обычно ограничен - в один прекрасный момент они все становятся мертвыми - и приходит полный DoS...

    З.Ы. До введения ограничений на полу-подключения - все было еще хуже - злонамеренная сторона могла выбрать лимит listen еще до запуска программных механизмов жизни...
  • han_malign (24.03.11 10:59) [12]

    > Глупо доказывать, это вполне выгодно. Будешь выглядеть смешно,
    >  как ламер.

    - ну в общем есть правда в этих словах... - мультимедийные потоки реального времени испокон веков(таки второй календарный пошел) гонялись поверх UDP:
    http://ru.wikipedia.org/wiki/RTP
    - потери кадров и соответственно артефакты считаются малой расплатой за низкую загрузку и латентность сети...

    Хотя RDP, Citrix, и разные клоны xVNC - таки работают по TCP, но это по большей части связано с безопасностью(сеансовые поточные шифры)...
  • Troop (24.03.11 20:55) [13]
    В общем есть еще компоненты на подобии TIdUDPClient?
  • Troop (24.03.11 21:12) [14]
    Херню сморозил
  • Troop (24.03.11 21:22) [15]
    У кого нибудь есть пример работы TServerClientThread с TIdUDPServer?
  • Troop (24.03.11 21:38) [16]
    Пытаюсь переделать под TidUDP но ничего не получается, сам понимаю что делаю ерись =(
  • Troop (25.03.11 14:26) [17]
    Парне если кто знает, дайте нормальный пример работы с компонентами TidUDP
  • Anatoly Podgoretsky © (25.03.11 15:08) [18]
    > Troop  (25.03.2011 14:26:17)  [17]

    Я как то возился с TidUDP, выкинул и применил ICS - вот это работает
 
Конференция "Сети" » клиент-сервер через UDP [D7]
Есть новые Нет новых   [134437   +27][b:0][p:0.007]