Асинхронный режим работы сокета.

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by Joker-jar, 19 Jun 2007.

  1. Joker-jar

    Joker-jar Elder - Старейшина

    Joined:
    11 Mar 2007
    Messages:
    581
    Likes Received:
    205
    Reputations:
    37
    Хочу показать довольно полезный пример работы сокета, основанный на сообщениях. Приемущество его в том, что не нужно по циклу опрашивать сокет на предмет принятия данных и т.д., сокет сам сообщит об этом. Для реализации требуется winsock2, обертку под Делфи можно легко найти в рунете.
    Первым делом объявляется константа

    Code:
    WM_SocketEvent = WM_User+112;
    которая будет выполнять функции идентификатора сообщения от сокета (присваивается любое значение, не занятое стандартными сообщениями Windows).
    Далее идет стандартная последовательность - подготовка сокета к прослушиванию

    Code:
    if WSAStartup(MAKEWORD(2,0),WSA)=0 then
      begin
        Sock:=socket(AF_INET,SOCK_STREAM,0);
        if Sock=INVALID_SOCKET then
          //error and exit
        address.sin_addr.s_addr := htonl(INADDR_ANY);
        address.sin_family:=AF_INET;
        address.sin_port:=htons(portnum);
        FillChar(Address.sin_zero,SizeOf(Address.sin_zero),0);
        if bind(Sock, @address, sizeof(address))=SOCKET_ERROR then
          //error and exit
      end;
    А вот и самое главное: перед непосредственным началом слушания порта (командой listen) сокет переводится в неблокирующий режим и привязывается к хэндлу, куда впоследствии будут отправляться сообщения.

    Code:
    WSAAsyncSelect(Handle,WM_SocketEvent,FD_Read or FD_Accept or FD_Close);
    Таким образом, при поступлении данных от клиентов (FD_Read), коннекте и дисконнекте (FD_Accept и FD_Close соответственно) будут генерироваться сообщения с идентификатором WM_SocketEvent, отправленные на хэндл Handle (лучше всего использовать хэндл формы, если это GUI-приложение).
    Остается только правильно принять эти сообщения и обработать. Создается обработчик сообщений WM_SocketEvent, Wparam сообщения будет содержать хэндл на клиентский сокет (еще один плюс - не надо хранить информацию о клиентских сокетах, при возникновении события мы сразу можем узнать, какой из клиентов его вызвал). LParam будет указывать на тип события:

    Code:
    WSAGetSelectEvent(lParam)
    возвратит один из типов события, в данном случае это либо FD_Read, либо FD_Accept, либо FD_Close. Таким образом, примерный вид обработчика сообщений будет таким:

    Code:
    private
      procedure SocketProc(var Msg: TMessage); message WM_SocketEvent;
      ...
      procedure SocketProc(var Msg: TMessage);
      var
        sClient:TSocket;
      begin
        inherited ;
        if Msg.Msg = WM_SocketEvent then
          begin
            Sclient := Msg.Wparam;
            case WSAGetSelectEvent(Msg.lParam) of
              FD_Read:
                begin 
                  recv(Sclient,буфер,размер_буфера,0);
                end;
              FD_Accept:
                begin
                  Accept(...);
                  MessageBox(Клиент такой-то подключился);
                end;
              FD_Close:
                begin
                  MessageBox(Клиент такой-то отключился);
                end;
            end;           
          end;
    
    Рульная вещь, всем советую попробовать, кто не пользовался.
     
    1 person likes this.