Synapse. UDP client and server sample

Модератор: Модераторы

Synapse. UDP client and server sample

Сообщение B4rr4cuda » 07.05.2011 23:13:09

Сам долго мучился, пока нашел работоспособный пример, поэтому выкладываю для страждущих.
Код: Выделить всё
program synudp;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes,
  blcksock;

const
  CONST_PORT = '32000';

type
  TUDPThread = class( TThread )
  private
    function  GetLog: String;
    procedure OnStatus(Sender: TObject; Reason: THookSocketReason; const Value: string);
  protected
    m_Log : TStringList;
    m_pSocket : TUDPBlockSocket;
  public
    constructor Create; virtual;
    destructor  Destroy; override;
    property Log : String read GetLog;
  end;

  TUDPServer = class( TUDPThread )
  protected
    procedure Execute; override;
  end;

  TUDPClient = class( TUDPThread )
  protected
    procedure Execute; override;
  end;

{ TUDPThread }

constructor TUDPThread.Create;
begin
  inherited Create( false );
  FreeOnTerminate := false;
  m_Log := TStringList.Create;
  m_pSocket := TUDPBlockSocket.Create;
  m_pSocket.OnStatus := @OnStatus;
end;

destructor TUDPThread.Destroy;
begin
  FreeAndNil( m_pSocket );
  FreeAndNil( m_Log );
  inherited;
end;

function TUDPThread.GetLog: String;
begin
  Result := m_Log.Text;
end;

procedure TUDPThread.OnStatus(Sender: TObject; Reason: THookSocketReason;
  const Value: string);
var
  sReason : String;
begin
  case Reason of
    HR_ResolvingBegin : sReason := 'HR_ResolvingBegin';
    HR_ResolvingEnd : sReason := 'HR_ResolvingEnd';
    HR_SocketCreate : sReason := 'HR_SocketCreate';
    HR_SocketClose : sReason := 'HR_SocketClose';
    HR_Bind : sReason := 'HR_Bind';
    HR_Connect : sReason := 'HR_Connect';
    HR_CanRead : sReason := 'HR_CanRead';
    HR_CanWrite : sReason := 'HR_CanWrite';
    HR_Listen : sReason := 'HR_Listen';
    HR_Accept : sReason := 'HR_Accept';
    HR_ReadCount : sReason := 'HR_ReadCount';
    HR_WriteCount : sReason := 'HR_WriteCount';
    HR_Wait : sReason := 'HR_Wait';
    HR_Error : sReason := 'HR_Error';
  end;
  m_Log.Add( sReason + ': ' + Value );
end;

{ TUDPServer }

procedure TUDPServer.Execute;
var
  sResult : String;
begin
  m_pSocket.Bind( cAnyHost, CONST_PORT );
  if ( m_pSocket.LastError = 0 ) then
  repeat
    sResult := m_pSocket.RecvPacket( -1 );
    m_Log.Add( sResult );
  until terminated or ( sResult = 'exit' );
end;

{ TUDPClient }

procedure TUDPClient.Execute;
var
  ii : Integer;
begin
  m_pSocket.Connect( cLocalhost, CONST_PORT );
  for ii := 0 to 3 do
  begin
    m_pSocket.SendString( IntToStr( ii ) );
    Sleep( 100 );
  end;
  m_pSocket.SendString( 'exit' );
end;

var
  pClient : TUDPClient;
  pServer : TUDPServer;

begin
  pServer := TUDPServer.Create;
  try
    Sleep( 100 );
    pClient := TUDPClient.Create;
    try
      pServer.WaitFor;
      WriteLn( '*************** Client ***************' );
      WriteLn( pClient.Log );
      WriteLn( '*************** Server ***************' );
      WriteLn( pServer.Log );
      ReadLn;
    finally
      FreeAndNil( pClient );
    end;
  finally
    FreeAndNil( pServer );
  end;
end.
Аватара пользователя
B4rr4cuda
энтузиаст
 
Сообщения: 693
Зарегистрирован: 28.12.2007 07:48:35

Re: Synapse. UDP client and server sample

Сообщение Polinom2686 » 30.06.2011 08:16:29

Спасибо тебе, хороший человек. :D
Polinom2686
незнакомец
 
Сообщения: 8
Зарегистрирован: 02.09.2009 02:01:29

Re: Synapse. UDP client and server sample

Сообщение VirtUX » 20.03.2013 15:12:50

Спасибо :)
Аватара пользователя
VirtUX
энтузиаст
 
Сообщения: 880
Зарегистрирован: 05.02.2008 10:52:19
Откуда: Крым, Алушта

Re: Synapse. UDP client and server sample

Сообщение VirtUX » 22.03.2013 01:33:00

Что-то не могу найти в модуле blcksock как узнать адрес отправителя пакета?
Т.е.: я ожидаю пакет:
Код: Выделить всё
...
FSocket.Bind( cAnyHost, FPort );
...
sResult := FSocket.RecvTerminated( -1, #254#255 );
...

Теперь мне нужно ответить отправителю, что я все получил. Но как узнать адрес приславшего дейтаграмму?
===========
и параллельный вопрос:
сколько раз, и как правильно я могу связывать UDP-сокет с различными адресатами?
Т.е.: создан сокет, связываем его:
Код: Выделить всё
FSocket.Connect( '10.254.7.12', '10201' );

отправляем дейтаграмму.
Теперь нужно отправить ДГ другому адресату. Могу-ли я использовать тот же сокет? Если да, то как правильно связать с другим адресатом? Нужны-ли еще какие действия между одним и следующим FSocket.Connect?
Аватара пользователя
VirtUX
энтузиаст
 
Сообщения: 880
Зарегистрирован: 05.02.2008 10:52:19
Откуда: Крым, Алушта

Re: Synapse. UDP client and server sample

Сообщение B4rr4cuda » 23.03.2013 06:35:16

VirtUX писал(а):Но как узнать адрес приславшего дейтаграмму?

Если память не подводит, то так :
Код: Выделить всё
FSocket.RemoteSin.sin_addr


VirtUX писал(а):Теперь нужно отправить ДГ другому адресату. Могу-ли я использовать тот же сокет? Если да, то как правильно связать с другим адресатом? Нужны-ли еще какие действия между одним и следующим FSocket.Connect?

Отключиться разве что.. я бы, на всякий случай, все же освобождал бы и создавал заново. Но это так.. дую на воду.

Да, кстати.. а зачем адрес, чтоб отправить ответ? Что мешает отослать отправителю инфу тем же сокетом с того же соединения?
Что-то типа такого:
Код: Выделить всё
          vRecvSockStr:='';
          //если есть инфа в буфере, то читаем
          if FBlockSock.WaitingData>0 then begin //пришла инфа по сокету
            vRecvSockStr := FBlockSock.RecvPacket(1000);
            CatchDisconnect(vRecvSockStr);
            SendInfoToIPC(vRecvSockStr);
          end;
         //Обработка ошибок сокета
          if FBlockSock.lastError<>0 then begin
            AddLog(inttostr(FBlockSock.LastError)+':'+FBlockSock.GetErrorDescEx,LogError);
            break;
            //если не connection timeout
            //вообще, вопрос, надо ли отваливаться?
            //по идее сервер должен пахать как проклятый.
            //if LastError<>10060 then break;
          end
          else begin
             //Обработка пришедшей команды сервером IPC
             vRecvIPCStr:=GetIPCCommand;
             if vRecvIPCStr<>'' then
               FBlockSock.SendString(vRecvIPCStr);
          end;
          sleep(200);

          end;
Аватара пользователя
B4rr4cuda
энтузиаст
 
Сообщения: 693
Зарегистрирован: 28.12.2007 07:48:35

Re: Synapse. UDP client and server sample

Сообщение VirtUX » 24.03.2013 00:53:05

А перед FSocket.RemoteSin.sin_addr вызвать FSocket.GetSinRemote; нужно, или это не обязательно?
Аватара пользователя
VirtUX
энтузиаст
 
Сообщения: 880
Зарегистрирован: 05.02.2008 10:52:19
Откуда: Крым, Алушта

Re: Synapse. UDP client and server sample

Сообщение B4rr4cuda » 24.03.2013 05:07:25

Не в курсе, надо проверять..
Аватара пользователя
B4rr4cuda
энтузиаст
 
Сообщения: 693
Зарегистрирован: 28.12.2007 07:48:35

Re: Synapse. UDP client and server sample

Сообщение VirtUX » 26.03.2013 13:24:12

B4rr4cuda писал(а):Что мешает отослать отправителю инфу тем же сокетом с того же соединения?

Сообщить об успешном получении дейтаграммы таким образом можно. Но тут ситуация в следующем:
- пришел запрос на сервер от клиента;
- сервер ставит его в очередь инструкций на обработку, и продолжает ожидать запросы от различных клиентов;
- поток обработки запросов выполняет инструкцию из очереди, и, возможно, отправляет результат выполнения клиенту;
- в момент отправки результата, принимающий запросы сокет может уже иметь в поле RemoteSin адрес и порт другого клиента.
Т.е. сервер не прекращает принимать запросы от одних клиентов в тот момент, пока формируются ответы для других клиентов. Поэтому я и спрашиваю про возможность использования одного сокета, для отправки дейтаграмм различным клиентам.
Аватара пользователя
VirtUX
энтузиаст
 
Сообщения: 880
Зарегистрирован: 05.02.2008 10:52:19
Откуда: Крым, Алушта

Re: Synapse. UDP client and server sample

Сообщение Ichthyander » 26.03.2013 13:50:49

VirtUX А если передать этому же "потоку обработки запросов" объект-сокет? То есть сервер получил от клиента пакет, создает новый поток куда помещает данные сокета и "забывает" о нем..? Этот поток и отвечает клиенту. Я так делал, правда с TCP-сервером.
На UDP сервере я все делал в одном потоке, то есть поток обрабатывал запрос клиента, отвечал ему и после этого сервер готов принимать новый пакет.
Аватара пользователя
Ichthyander
энтузиаст
 
Сообщения: 668
Зарегистрирован: 04.04.2007 08:32:43
Откуда: Астрахань

Re: Synapse. UDP client and server sample

Сообщение VirtUX » 26.03.2013 14:45:30

Ichthyander писал(а):А если передать этому же "потоку обработки запросов" объект-сокет?

Дело в том, что в поле удаленного адреса этого объекта значение будет меняться при поступлении новых пакетов от других клиентов. И функция Send может отправить пакет клиенту, который на данный момент прислал запрос последним.
В TCP другая история в отличии от UDP. Там для каждого клиента плодятся отдельные сокеты.
Аватара пользователя
VirtUX
энтузиаст
 
Сообщения: 880
Зарегистрирован: 05.02.2008 10:52:19
Откуда: Крым, Алушта

Re: Synapse. UDP client and server sample

Сообщение VirtUX » 11.04.2013 02:15:43

странное дело. В локалке пакеты путешествуют от клиента к серверу и обратно отлично. Но через интернет не работает.
Сервер находится за роутером. Порт по которому клиент шлет запросы на роутере проброшен.
Соединяюсь с сервером так:
Код: Выделить всё
FSocket.Connect( FIPServer, FPortServer );
; где FIPServer - WAN-адрес роутера смотрящий в интернет (с рабочего места клиента пингуется), FPortServer - порт сервера (порт проброшен на локальный адрес сервера). IP и порт для клиентского сокета не указываю (оставлено на милость synapse). Хотя пробовал жесткую привязку к локальному IP и такому же порту как на сервере - результат тот же - негативный.
Сервер привязан так:
Код: Выделить всё
FSocket.Bind( FIP, FPort );
; где FIP - локальный адрес сервера, FPort - порт сервера.
Пакеты не приходят на сервер. Что не так?
Аватара пользователя
VirtUX
энтузиаст
 
Сообщения: 880
Зарегистрирован: 05.02.2008 10:52:19
Откуда: Крым, Алушта

Re: Synapse. UDP client and server sample

Сообщение Mr.Smart » 11.04.2013 09:53:00

Смотрите сетевые настройки. Проброску портов, FireWall и т.д.
Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: Synapse. UDP client and server sample

Сообщение VirtUX » 11.04.2013 11:42:18

Mr.Smart писал(а):FireWall

Забыл открыть порт в брандмауре Windows Server :oops: (До этого тестил на рабочих станциях)
Спасибо за намек :)
Аватара пользователя
VirtUX
энтузиаст
 
Сообщения: 880
Зарегистрирован: 05.02.2008 10:52:19
Откуда: Крым, Алушта

Re: Synapse. UDP client and server sample

Сообщение VirtUX » 11.06.2013 11:51:09

B4rr4cuda писал(а):Не в курсе, надо проверять..

После считывания данных из сокета FSocket.Recv.... становятся доступны IP и Port:
Код: Выделить всё
nowIP:= FSocket.GetRemoteSinIP;
nowPort:= IntToStr( FSocket.GetRemoteSinPort );

P.S.
Я столкнулся с проблемой, что разные ОС по умолчанию выделяют под сокет буфер разного размера. Поэтому не забываем про:
Код: Выделить всё
FSocket.SizeSendBuffer := 65000;
FSocket.SizeRecvBuffer := 65000;
Аватара пользователя
VirtUX
энтузиаст
 
Сообщения: 880
Зарегистрирован: 05.02.2008 10:52:19
Откуда: Крым, Алушта

Re: Synapse. UDP client and server sample

Сообщение VirtUX » 14.06.2013 16:55:29

Странности продолжаются. Мало того, что размер пакета зависит от ОС, так еще и от типа соединения. В локалке и внутренней сети провайдера пакеты гуляют нормально. Но через интернет не более ~1400 байт. Поэтому при попытке отправить пакет 1500 байт - он не доходит :(
Интересует вопрос: это не лечится? пакеты по 1400 байт оооочень маленькие. И если я разделяю сообщение на части по 1000 байт, то по очень долгу загружается ответ от сервера, что мне никак нет приемлемо! Есть-ли возможность отправлять пакеты по 65000 байт?
Использую blcksock из Synapse.

Добавлено спустя 8 минут 3 секунды:
Разница теста: 1 минута 21 секунда (если делить по 1000 байт), против 7 секунд (если делить по 65000).
Не полный код теста:
Код: Выделить всё
{ Приращиваемый блок }
sAppend:='7777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777';
{ Размер блока }
i := 100;
  while i < 65001 do begin

      sSend += sAppend;
      fTimeStartBlock:= GetTickCount64;
      sAns := Cln.Send(sSend);       // Отправка сообщения на сервер. Возвращает то же самое сообщение (если все ОК :))
      lenAns:= Length( sAns );
      if lenAns <> i then sAns:= CodeChars(sAns)    // Если пришло не то, что нужно, то показываю коды символов пришедшего ответа
      else sAns:= IntToStr(lenAns);                        // иначе длину ответа (остатки от прошлого кода)
      fTimeStopBlock:= GetTickCount64;
      { Показываем время ожидания ответа }
      ListBox1.Items.Append(IntToStr(i) + ':' + sAns + ' >>> ' + IntToStr(fTimeStopBlock - fTimeStartBlock));
      ListBox1.Repaint;
      i += 100;

  end;
Аватара пользователя
VirtUX
энтузиаст
 
Сообщения: 880
Зарегистрирован: 05.02.2008 10:52:19
Откуда: Крым, Алушта

След.

Вернуться в Сеть

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4

Рейтинг@Mail.ru