zoukankan      html  css  js  c++  java
  • 网络学习之WSAEventSelect模型

    WSAEventSelect模型是一个异步事件通知模型。允许应用程序在一个或多个套接字上接收收基于事件的网络通知。它与WSAAsyncSelect模型类似,但不是依靠windows的

    消息驱动机制,而是经由事件对象句柄通知。

    主要函数:

    WSACreateEvent 创建一个事件对象

    WSAEventSelect  网络事件与事件对象进行关联。

    具体参数:

    socket s 套接字句柄

    WSAEvent  h 事件对象句柄

    long NetWorkEvents  FD_xxx网络事件组合。FD_ACCEPT,FD_READ,FD_WRITE,FD_CLOSE

    WSAWaitForMultipleEvents  在一个或多个事件对象上等待网络事件的发生,如果指定时间已过,返回WSA_WAIT_TIMEOUT,如果调用失败则返回WSA_WAIT_FAILED

    如果成功,则返回受信的事件对象。

    具体参数:

    DWORD cEvents 指定事件数组中句柄的个数

    WSAEVENT* lphEvents 指向一个事件对象句柄数组

    BOOL fWaitAll    指定是否等待所有事件对象都变成受信状态

    DWORD dwtimeout

    BOOL falertable  在使用WSAEventSelect模型时可以忽略,应设为FALSE

    WSAEnumNetworkEvents 找到受信事件对像与之对应的套接字。

    具体参数:

    SOCKET S  //套接字句柄。

    WSAEVENT hEventObject 对应的事件对象句柄。如果提供了此参数,本函数会重置这个事件对象的状态

    LPWSANETWORKEVENTS lpNetworkEvents //指向一个WSANETWORKEVENTS结构。

    最后这个WSANETWORKEVENTS结构是最重要的,因为它取得了套接字上发生的网络事件和相关的出错代码。

    定义如下:

    LPWSANETWORKEVENTS=^WSANETWORKEVENTS

    WSANETWORKEVENTS=RECORD

        INetworkEvents : longint;

       iErrorCode         : array[0..FD_MAX_EVENTS] OF INT;

    end;

    iErrorCode  是一个数组,数组的每个成员对应着一个网络事件的出错代码。可以用预定义标识FD_READ_BIT,FD_WRITE_BIT,FD_ACCEPT_BIT,FD_CLOSE_BIT来索引FD_READ等事件发生的出错代码。

    具体的DELPHI代码如下:

    program Project1;
    {$APPTYPE CONSOLE}
    uses
      Windows,SysUtils,JwaWinsock2;

    var
      eventarray  :array[0..WSA_MAXIMUM_WAIT_EVENTS-1] OF WSAEVENT; //事件数组
      sockarray   :array[0..WSA_MAXIMUM_WAIT_EVENTS-1] of TSocket;  //套接字数组
      nEventTotal :Integer;    //当前总事件数,不能大于64
      nport       :Integer;    //端口
      sListen     :TSocket;    //主套接字
      v_sin       :sockaddr_in;
      event       :WSAEVENT;   //事件对象变量,用于创建新的事件
      sEvent      :_WSANETWORKEVENTS;
      nIndex      :Integer;    //接收函数WSAWaitForMultipleEvents返回的值。
      i,j         :Integer;    //循环变量
      sNew        :TSocket;    //客户端的SOCKET
      sztext      :array[0..255] of AnsiChar;  //接收客户端的信息。
      vwsadata    :WSAData;   //初始化socket的变量;
    begin
       WSAStartup($0202,vwsadata);  //初始化socket,此处用socket2.0。
       try
         slisten:=INVALID_SOCKET;
         nport:=4567;
         nEventTotal:=0;
         try
           sListen:=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
           v_sin.sin_family:=AF_INET;
           v_sin.sin_port  :=htons(nport);
           v_sin.sin_addr.S_addr:=INADDR_ANY;

           if bind(sListen,@v_sin,SizeOf(v_sin))=SOCKET_ERROR then
             begin
                Writeln('绑定端口发生错误。');
             end;
           listen(sListen,200); //开始监听。
           Writeln('网络开始进行监听中......');
           //创建事件对象,并关联到新的套接字上。
           event:=WSACreateEvent();
           WSAEventSelect(sListen,event,FD_ACCEPT OR FD_CLOSE);
          //将事件和套接字存入相应的数组中。
          eventarray[nEventTotal]:=event;
          sockarray [nEventTotal]:=sListen;
          inc(nEventTotal);
          while True do
            begin
              {若收到一个事件对象的网络事件通知后,便会返回一个值指出造成函数返回的事件对象,
              这样应用程序便可引用事件数组中已传信的事件,并检索与事件对应的 SOCKET ,
              方法是用 WSAWaitForMultipleEvents 的返回值减去相应的预定义值 WSA_WAIT_EVENT_0, 得到具体的引用值
              将fAlertable设为False后,如果同时有几个事件对象受信,WSAWaitForMultipleEvents函数的返回值仅能指明
              一个,就是句柄数组中最前面的那个。如果指明的事件对象总有网络时间发生,那么后面的其他事件对象所关联的
              网络事件就得不到处理。解决的方法:当WSAWaitForMultipleEvents函数返回后,对每个事件都再次调用这个函数,
              以便确定其状态。
              }
              nindex:=WSAWaitForMultipleEvents(nEventTotal,@eventarray,False,WSA_INFINITE,False);
              nindex:=nIndex-WSA_WAIT_EVENT_0;
              for i:=nIndex to nEventTotal-1 do
                begin
                  nIndex:=WSAWaitForMultipleEvents(1,@eventarray[i],True,1000,False);
                  if (nIndex=WSA_WAIT_FAILED) or (nIndex=WSA_WAIT_TIMEOUT) then
                    begin
                      Continue;
                    end
                  else
                    begin
                       WSAEnumNetworkEvents(sockarray[i],eventarray[i],@sEvent);
                       if sEvent.lNetworkEvents=FD_ACCEPT then
                        begin
                          if sEvent.iErrorCode[FD_ACCEPT_BIT]=0 then
                            begin
                              if nEventTotal>WSA_MAXIMUM_WAIT_EVENTS-1 then
                                begin
                                  Writeln('有太多的连接!已经大于64了。');
                                  Continue;
                                end;
                              snew:=accept(sockarray[i],nil,nil);
                              writeln('有新的连接进入。');
                              event:=WSACreateEvent();
                              WSAEventSelect(sNew,event,FD_READ OR FD_CLOSE OR FD_WRITE);
                              eventarray[nEventTotal]:=event;
                              sockarray [nEventTotal]:=sNew;
                              inc(nEventTotal);
                            end;
                        end
                       else if sEvent.lNetworkEvents=FD_READ then
                         begin
                            if recv(sockarray[i],szText,SizeOf(szText),0)>0 then
                             begin
                               Writeln('接收到的信息:'+strpas(sztext));
                             end;
                         end
                       else if sEvent.lNetworkEvents=FD_CLOSE then
                         begin
                            if sEvent.iErrorCode[FD_CLOSE_BIT]=0 then
                              begin
                                closesocket(sockarray[i]);
                                for j:=i to nEventTotal-1 do
                                 begin
                                   sockarray[j]:=sockarray[j+1];
                                   eventarray[j]:=eventarray[j+1];
                                 end;
                                nEventTotal:=nEventTotal-1;
                              end;
                         end
                       else if sEvent.lNetworkEvents=FD_WRITE then
                         begin

                         end;

                    end;
                end;
            end;
         finally
           closesocket(sListen); //关闭套接字
         end;
       finally
         WSACleanup; //清除socket。
       end;
    end.

    WSAEVENTSELECT模型最多等待64个事件对象的限制,当套接字连接数量增加时,必须创建多个线程来处理I/O,也就是使用线程池。

    在我写的WSAEVENTSELECT模型实例中,我做了测试发现,当我连接的数量越来越多时(当然少于64个连接),服务端的反应就越来越慢。

  • 相关阅读:
    【Python学习笔记之三】lambda表达式用法小结
    Cisco Packet Tracer 6.0 实验笔记
    Kali Linux 下安装中文版输入法
    kali 2018.1安装教程
    Kali Linux菜单中各工具功能大全
    互联网电商购物车架构演变案例
    互联网业务场景下消息队列架构
    物流系统高可用架构案例
    高并发下海量容器案例一
    客服系统微服务架构的演化
  • 原文地址:https://www.cnblogs.com/wangorg/p/2038399.html
Copyright © 2011-2022 走看看