Конференция "Сети" » Как правильно написать socks-сервер? [D7, WinXP]
 
  • Сергей М. © (15.01.08 14:18) [20]

    > где можно про это почитать


    Дык в справке стандартной и почитай
  • DVM © (15.01.08 17:33) [21]

    > zSergey

    Delphi автоматически позволяет создавать ActiveX только на основе потомков TWinControl кажется.
  • zsergey (16.01.08 19:53) [22]
    Привет знатокам!
    Написал сокет-сервер как сом-объект на WinApi (*.dll). Все хорошо, сокет открывается, слушается, данные из него читаются, но само приложение, использующее библиотеку, тормозит и дальше программа не может выполнить остальные свои функции, что не есть хорошо. Используются не блокирующие сокеты. Выкладываю весь исходник:

    unit Utestlib;

    {$WARN SYMBOL_PLATFORM OFF}

    interface

    uses
     ComObj, ActiveX, testlib_TLB, StdVcl, Dialogs, WinSock, SysUtils, Windows;

    type
     TApp = class(TAutoObject, IApp)
     protected
       procedure OpenSocket; safecall;
    end;

    type
    ThreadData = record
      Socket: TSocket;
    end;
    PThreadData = ^ThreadData;

    var
    WSAData: TWSAData;
    ListenSocket, ClientSocket: TSocket;
    Info: PThreadData;
    SockAddr: TSockAddr;
    ThreadId: THandle;
    hClientThread: Thandle;

    Arg : u_long;
    s: TSocket;
    const
    Port = word(5555);

    implementation

    uses ComServ;
    //////////////////////////////////////////
    procedure SocketThread(Info: PThreadData);
    var
    SockName: TSockAddr;
    NameLen, OptLen: Integer;
    buf : array[0..35] of byte;
    RecvSize: integer;

    BuffSize,k: integer;
    str : string;
    error : integer;

    begin
    s := Info^.Socket;
    try
      NameLen := SizeOf(TSockAddr);
      if GetPeerName(s, SockName, NameLen) <> 0 then exit;
      ShowMessage('Client accepted');
      OptLen := SizeOf(BuffSize);
      if GetSockOpt(s, SOL_SOCKET, SO_RCVBUF, pointer(@BuffSize), OptLen) <> 0 then exit;
      try
        repeat
          RecvSize := recv(s, Buf[0], Length(Buf), 0);
          if  RecvSize = SOCKET_ERROR then
             Begin
               error := WSAGetLastError();
               if (error<>WSAEWOULDBLOCK) then
                   continue
               else RecvSize := 1
             end
          else
          sleep(5);
          if RecvSize > 1 then
             ShowMessage('Received from client ...');
        until RecvSize <= 0;
      finally
    end;
      ShowMessage('Client disconnected ...');
    finally
      CloseSocket(s);
      Dispose(Info);
    end;
    end;
    //////////////////////////////////////////

    procedure TApp.OpenSocket;
    begin
    ShowMessage('WSA Initialize ...');
    if WSAStartup($101, WSAData) <> 0 then exit;
    try
      ListenSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
      // non bloking
       Arg:=1;
       IOCtlSocket(ListenSocket,FIONBIO,Arg);
      //
      if ListenSocket = INVALID_SOCKET then exit;
      try
        FillChar(SockAddr, SizeOf(TSockAddr), 0);
        SockAddr.sin_family := AF_INET;
        SockAddr.sin_port := htons(Port);
        SockAddr.sin_addr.S_addr := INADDR_ANY;
        if Bind(ListenSocket, SockAddr, SizeOf(TSockAddr)) <> 0 then exit;
          if listen(ListenSocket, SOMAXCONN) <> 0 then exit;
            repeat
               
              ClientSocket := accept(ListenSocket, nil, nil);
              if ClientSocket <> INVALID_SOCKET then
                begin
                  New(Info);
                  Info^.Socket := ClientSocket;
                  hClientThread := BeginThread(nil, 0, @SocketThread, Info, 0, ThreadId);
                  if hClientThread <> 0 then CloseHandle(hClientThread);
                end;
            until false;
      finally
      Arg:=0;
      IOCtlSocket(ListenSocket,FIONBIO,Arg);
      CloseSocket(ListenSocket);
      end;
    finally
      WSACleanup;
    end;
    end;

    initialization
     TAutoObjectFactory.Create(ComServer, TApp, Class_App,
       ciMultiInstance, tmApartment);
    end.



    что я не так делал?
  • Slym © (17.01.08 04:28) [23]
    zsergey   (16.01.08 19:53) [22]
    не блокирующие сокеты

    1. и нафега BeginThreadв твоем не блокирующем сокете?
    2. чтобы он стал полноценно неблокирующим нужно вызвать Select функцию и в нее передать дескриптор окна в которое будут приходить сообщения об событиях сокета
    3. неблокирующий у тебя только слушающий сокет (listen), остальные (accept) блокирующие т.к. становится им неблокирующими ты им не сказал
  • Slym © (17.01.08 04:32) [24]
    ты понацеплял
    ComObj, ActiveX, testlib_TLB, StdVcl, Dialogs

    и тебе жалко 10кб на нормальный сокет? ты еще сом-объект руками сваяй...
    Slym ©   (17.01.08 4:28) [23]
    4. в com объекте с потоками огребеш не зная предметной области
  • Сергей М. © (17.01.08 10:11) [25]

    > ShowMessage


    Что еще за VCL-выкрутасы в дополнительном потоке ?
  • Сергей М. © (17.01.08 10:21) [26]

    > 3. неблокирующий у тебя только слушающий сокет (listen),
    >  остальные (accept) блокирующие т.к. становится им неблокирующими
    > ты им не сказал
    >


    Угу.
    Но при этом в слушающем потоке, работающем с неблокирующим ListenSocket, почему-то нет ни намека на WSAEWOULDBLOCK, зато в клиентских потоках, работающих с блокирующими ClientSocket, WSAEWOULDBLOCK с какого-то перепугу фигурирует.
  • DVM © (17.01.08 10:34) [27]

    > zsergey   (16.01.08 19:53) [22]

    Лучше бы ты на асинхронных неблокирующих сокетах сделал. На сообщениях. Ей богу проще для твоего случая. И доп потоки не нужны.
  • Сергей М. © (17.01.08 10:42) [28]

    > if GetSockOpt(s, SOL_SOCKET, SO_RCVBUF, pointer(@BuffSize),
    >  OptLen) <> 0 then exit;


    Совершенно непонятно, в каких случаях GetSockOpt может вернуть ошибку и для чего вообще запрашивается размер буфера приема, если этот полученный размер далее по ходу дела нигде не используется
  • Сергей М. © (17.01.08 10:56) [29]

    > Slym ©   (17.01.08 04:28) [23]


    Впрочем ни ты ни я не правы  - ClientSocket у автора работает именно в неблок.режиме.

    Цитата из справки по accept():

    newly created socket has the same properties as s including asynchronous events registered with WSAAsyncSelect or with WSAEventSelect
  • Сергей М. © (17.01.08 10:59) [30]
    Но этот факт не меняет сути - логика repeat-цикла в кл.потоке с учетом неблок.режима не верна в принципе
  • Slym © (17.01.08 11:08) [31]
    Сергей М. ©   (17.01.08 10:56) [29]
    Select

    я то как раз прав...
    ниодного select я не увидел...
    если бы и увидел то было выглядело бы так:
    WSAAsyncSelect(ListenSocket,hWnd,FD_ACCEPT,WM_USER);
    этого для слушателя достаточно, а для ацепнутого сокета никаких Эвентов не прописано...
  • zSergey (17.01.08 11:38) [32]

    > 2. чтобы он стал полноценно неблокирующим нужно вызвать
    > Select функцию


    > ниодного select я не увидел...
    > если бы и увидел то было выглядело бы так:
    > WSAAsyncSelect(ListenSocket,hWnd,FD_ACCEPT,WM_USER);

    Хорошо, вот переделанный сервер с селектом (не СОМ)

    program Project1;
    uses
     SysUtils,
     WinSock;

    {$APPTYPE CONSOLE}
    var Sockets:array of TSocket;
       Addr:TSockAddr;
       Data:TWSAData;
       Len,I,J:Integer;
       FDSet:TFDSet;
       arrBuf : array [0..35] of byte;
       Arg:u_long;
       tv : Ttimeval;

    begin
     WSAStartup($101,Data);
     SetLength(Sockets,1);
     Sockets[0]:=Socket(AF_Inet,Sock_Stream,0);
     Addr.sin_family:=AF_Inet;
     Addr.sin_port:=HToNS(21001);
     Addr.sin_addr.S_addr:=InAddr_Any;
     FillChar(Addr.Sin_Zero,SizeOf(Addr.Sin_Zero),0);
     Bind(Sockets[0],Addr,SizeOf(TSockAddr));
     Listen(Sockets[0],SoMaxConn);
     while True do
      begin
       FD_Zero(FDSet);
       for I:=0 to High(Sockets) do
       Arg:=1;
       IOCtlSocket(Sockets[I],FIONBIO,Arg);
       FD_Set(Sockets[I],FDSet);

       tv.tv_sec := 5;
       tv.tv_usec := 0;

       Select(0,@FDSet,nil,nil,@tv);

       if FDSet.fd_count=0 then Continue;
       sleep(100);
       writeln('wait');
       
       I:=1;
       while I<=High(Sockets) do
        begin
         if FD_IsSet(Sockets[I],FDSet) then
          if Recv(Sockets[I],arrBuf, sizeOf(arrBuf),0) <=0 then
           begin
           
            writeln('disconnect, socket close');
            CloseSocket(Sockets[I]);
            for J:=I to High(Sockets)-1 do
             Sockets[J]:=Sockets[J+1];
            Dec(I);
            SetLength(Sockets,Length(Sockets)-1)
           end
          else
           begin
            writeln('Receive data');

           end;
         Inc(I)
        end;
       

       if FD_IsSet(Sockets[0],FDSet) then
        begin
       
         writeln('new client connected');
         SetLength(Sockets,Length(Sockets)+1);
         Len:=SizeOf(TSockAddr);
         Sockets[High(Sockets)]:=Accept(Sockets[0],@Addr,@Len)
        end
      end;
    end.



    Но почему-то при тестировании select не возвращает сокеты для чтения, чего не должно быть. Где ошибка?
  • Сергей М. © (17.01.08 11:41) [33]

    > Slym ©   (17.01.08 11:08) [31]


    > ниодного select я не увидел


    Причем здесь select ?
    Ну нет его и нет - и фиг с ним, я о другом сейчас.

    Я о твоей ремарке

    > остальные (accept) блокирующие т.к. становится им неблокирующими
    > ты им не сказал


    и о своей


    > в клиентских потоках, работающих с блокирующими ClientSocket


    А это неверно, поскольку слушающий сокет перед вызовом accept() переведен в неблок.режим (т.е. для него явно установлено опция-"свойство" nonblocking), что, с учетом цитаты в [29], при успешном вызове accept()  порождает новое неблокирующее гнездо.
  • Сергей М. © (17.01.08 11:45) [34]

    > select не возвращает сокеты для чтения


    И не возвратит.

    Куда у тебя accept()-то пропал ? Без него select как мертвому припарка)
  • zSergey (17.01.08 14:43) [35]

    > И не возвратит.

    нашел в чем ошибка:
    FD_Set(Sockets[I],FDSet);

    в цикле не в операторских скобках;
    для чего
    Arg:=1;
      IOCtlSocket(Sockets[I],FIONBIO,Arg);

    перенес под Accept(); теперь заработало, НО само приложение в "замороженном" состоянии, будь там блокирующие сокеты или не блокирующие, будут там нити или нет. Я почти все перепробовал. Повторюсь, сервер должен не только обрабатывать подключения, но и одновременно сохранять другие свои ф-ции, короче, чтобы все контролы на форме были доступными. Вроде понятно объяснил.
    > Лучше бы ты на асинхронных неблокирующих сокетах сделал.
    >  На сообщениях. Ей богу проще для твоего случая. И доп потоки
    > не нужны.

    Господа, дайте пож-ста простой примерчик, на который мне можно было опереться в данном случае.
  • Сергей М. © (17.01.08 15:13) [36]
    ты все-таки определись, тебя на WSAPI заклинило окончательно и бесповоротно или только блажь ?
  • zSergey (17.01.08 15:32) [37]

    > ты все-таки определись, тебя на WSAPI заклинило окончательно
    > и бесповоротно или только блажь ?

    ага мне вот делать нехер, низнаю чем заняться, WSAPI или еще чем. Уважаемый Сергей М, напоминаю, для чего мне все это нужно:

    Поясняю задачу: сам сервер пишется для связки с 1с v.8. Т.к. у 1с нет стандартных средств для работы с сокетами, приходиться прибегнуть с помошью внешних компонент (dll), где собственно будет находится сам функционал сервера. Далее, приняв данные, сервер должен в реальном времени передать их приложению-хозяину, приложение в это время не находиться в режиме ожидания, а продолжает выполнять весь свой функционал. Я понимаю, что это форум отношения к 1с не имеет, потому я пока рассматриваю вариант  взаимодействия программы и сервера на delphi. Буду благодарен любым идеям.

    ни внешних компонент, ни  бесплатных ActiveX контролов у меня нет, чтоб решить эту проблему, потому я вынужден ковырять wsapi ибо другого выхода я не вижу :)
  • Сергей М. © (17.01.08 15:34) [38]

    > мне вот делать нехер, низнаю чем заняться


    Я вот тоже так думаю - тебе нечем более заняться)

    ЧЕМ не устроили TServerSocket, TTCPServer, ICS-компоненты ?
    Вразумительно ты так и не объяснил)
  • zSergey (17.01.08 15:41) [39]

    > ЧЕМ не устроили TServerSocket, TTCPServer, ICS-компоненты
    > ?
    > Вразумительно ты так и не объяснил)

    я не знаю как эти компоненты засунуть в dll :)
 
Конференция "Сети" » Как правильно написать socks-сервер? [D7, WinXP]
Есть новые Нет новых   [134435   +33][b:0][p:0.004]