注册 | 登录
收藏 | 帮助
热门文章
编辑推荐
相关文章  
信息安全的隐患-GoogleHacking原
Microsoft AntiSpyware微软出品的
SOHO网络解决方案
宽带来临 封杀FSO躲避FSO木马的侵
Htran把内网肉鸡做成SOCKS5代理
DNS 系统设定例--7.[Fwd] 特殊网
DNS 记录项目 (Resource Records
qmail在Linux,Solaris系统安装详
qmail在Linux,Solaris系统安装详
IMail:Win32.Sobig垃圾邮件解决全
您现在的位置: 顶尖设计 >> IT学院 >> 编程开发 >> Delphi >> 文章正文
Socket I/O模型全接触
作者:flyinwuhan  来源:转载自CSDN  点击:  更新:2006-12-19
简介:

本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教。

    一:select模型
    二:WSAAsyncSelect模型
    三:WSAEventSelect模型
    四:Overlapped I/O 事件通知模型
    五:Overlapped I/O 完成例程模型
    六:IOCP模型

    老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。
    这和Socket模型非常类似。下面我就以老陈接收信件为例讲解Socket I/O模型~~~

一:select模型

老陈非常想看到女儿的信。以至于他每隔10分钟就下楼检查信箱,看是否有女儿的信~~~~~
在这种情况下,“下楼检查信箱”然后回到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作。
select模型和老陈的这种情况非常相似:周而复始地去检查......如果有数据......接收/发送.......

使用线程来select应该是通用的做法:
procedure TListenThread.Execute;
var
  addr     : TSockAddrIn;
  fd_read  : TFDSet;
  timeout  : TTimeVal;
  ASock,
  MainSock : TSocket;
  len, i   : Integer;
begin
  MainSock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
  addr.sin_family := AF_INET;
  addr.sin_port := htons(5678);
  addr.sin_addr.S_addr := htonl(INADDR_ANY);
  bind( MainSock, @addr, sizeof(addr) );
  listen( MainSock, 5 );

  while (not Terminated) do
  begin
    FD_ZERO( fd_read );
    FD_SET( MainSock, fd_read );
    timeout.tv_sec  := 0;
    timeout.tv_usec := 500;
    if select( 0, @fd_read, nil, nil, @timeout ) > 0 then //至少有1个等待Accept的connection
    begin
      if FD_ISSET( MainSock, fd_read ) then
      begin
        for i:=0 to fd_read.fd_count-1 do //注意,fd_count <= 64,也就是说select只能同时管理最多64个连接
        begin
          len := sizeof(addr);
          ASock := accept( MainSock, addr, len );
          if ASock <> INVALID_SOCKET then
             ....//为ASock创建一个新的线程,在新的线程中再不停地select
        end;
      end;
    end;
  end; //while (not self.Terminated)

  shutdown( MainSock, SD_BOTH );
  closesocket( MainSock );
end;

二:WSAAsyncSelect模型

后来,老陈使用了微软公司的新式信箱。这种信箱非常先进,一旦信箱里有新的信件,盖茨就会给老陈打电话:喂,大爷,你有新的信件了!从此,老陈再也不必频繁上下楼检查信箱了,牙也不疼了,你瞅准了,蓝天......不是,微软~~~~~~~~
微软提供的WSAAsyncSelect模型就是这个意思。

WSAAsyncSelect模型是Windows下最简单易用的一种Socket I/O模型。使用这种模型时,Windows会把网络事件以消息的形势通知应用程序。
首先定义一个消息标示常量:
const WM_SOCKET = WM_USER + 55;
再在主Form的private域添加一个处理此消息的函数声明:
private
  procedure WMSocket(var Msg: TMessage); message WM_SOCKET;
然后就可以使用WSAAsyncSelect了:
var
  addr  : TSockAddr;
  sock  : TSocket;

  sock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
  addr.sin_family := AF_INET;
  addr.sin_port := htons(5678);
  addr.sin_addr.S_addr := htonl(INADDR_ANY);
  bind( m_sock, @addr, sizeof(SOCKADDR) );

  WSAAsyncSelect( m_sock, Handle, WM_SOCKET, FD_ACCEPT or FD_CLOSE );

  listen( m_sock, 5 );
  ....

应用程序可以对收到WM_SOCKET消息进行分析,判断是哪一个socket产生了网络事件以及事件类型:

procedure TfmMain.WMSocket(var Msg: TMessage);
var
  sock    : TSocket;
  addr    : TSockAddrIn;
  addrlen : Integer;
  buf     : Array [0..4095] of Char;
begin
  //Msg的WParam是产生了网络事件的socket句柄,LParam则包含了事件类型
  case WSAGetSelectEvent( Msg.LParam ) of
    FD_ACCEPT :
    begin
      addrlen := sizeof(addr);
      sock := accept( Msg.WParam, addr, addrlen );
      if sock <> INVALID_SOCKET then
         WSAAsyncSelect( sock, Handle, WM_SOCKET, FD_READ or FD_WRITE or FD_CLOSE );
    end;

    FD_CLOSE : closesocket( Msg.WParam );
    FD_READ  : recv( Msg.WParam, buf[0], 4096, 0 );
    FD_WRITE : ;
  end; 
end;

三:WSAEventSelect模型

后来,微软的信箱非常畅销,购买微软信箱的人以百万计数......以至于盖茨每天24小时给客户打电话,累得腰酸背痛,喝蚁力神都不好使~~~~~~
微软改进了他们的信箱:在客户的家中添加一个附加装置,这个装置会监视客户的信箱,每当新的信件来临,此装置会发出“新信件到达”声,提醒老陈去收信。盖茨终于可以睡觉了。

同样要使用线程:
procedure TListenThread.Execute;
var
  hEvent : WSAEvent;
  ret    : Integer;
  ne     : TWSANetworkEvents;
  sock   : TSocket;
  adr    : TSockAddrIn;
  sMsg   : String;
  Index,
  EventTotal : DWORD;
  EventArray : Array [0..WSA_MAXIMUM_WAIT_EVENTS-1] of WSAEVENT;
begin
  ...socket...bind...
  hEvent := WSACreateEvent();
  WSAEventSelect( ListenSock, hEvent, FD_ACCEPT or FD_CLOSE );
  ...listen...

  while ( not Terminated ) do
  begin
    Index := WSAWaitForMultipleEvents( EventTotal, @EventArray[0], FALSE, WSA_INFINITE, FALSE );
    FillChar( ne, sizeof(ne), 0 );
    WSAEnumNetworkEvents( SockArray[Index-WSA_WAIT_EVENT_0],  EventArray[Index-WSA_WAIT_EVENT_0], @ne );

    if ( ne.lNetworkEvents and FD_ACCEPT ) > 0 then
    begin
      if ne.iErrorCode[FD_ACCEPT_BIT] <> 0 then
         continue;

      ret := sizeof(adr);
      sock := accept( SockArray[Index-WSA_WAIT_EVENT_0], adr, ret );
      if EventTotal > WSA_MAXIMUM_WAIT_EVENTS-1 then//这里WSA_MAXIMUM_WAIT_EVENTS同样是64
      begin
        closesocket( sock );
        continue;
      end;

      hEvent := WSACreateEvent();
      WSAEventSelect( sock, hEvent, FD_READ or FD_WRITE or FD_CLOSE );
      SockArray[EventTotal] := sock;
      EventArray[EventTotal] := hEvent;
      Inc( EventTotal );
    end;

    if ( ne.lNetworkEvents and FD_READ ) > 0 then
    begin
      if ne.iErrorCode[FD_READ_BIT] <> 0 then
         continue;
      FillChar( RecvBuf[0], PACK_SIZE_RECEIVE, 0 );
      ret := recv( SockArray[Index-WSA_WAIT_EVENT_0], RecvBuf[0], PACK_SIZE_RECEIVE, 0 );
      ......
    end;
  end;
end;

四:Overlapped I/O 事件通知模型

后来,微软通过调查发现,老陈不喜欢上下楼收发信件,因为上下楼其实很浪费时间。于是微软再次改进他们的信箱。新式的信箱采用了更为先进的技术,只要用户告诉微软自己的家在几楼几号,新式信箱会把信件直接传送到用户的家中,然后告诉用户,你的信件已经放到你的家中了!老陈很高兴,因为他不必再亲自收发信件了!

Overlapped I/O 事件通知模型和WSAEventSelect模型在实现上非常相似,主要区别在“Overlapped”,Overlapped模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。这些提交的请求完成后,应用程序会收到通知。什么意思呢?就是说,如果你想从socket上接收数据,只需要告诉系统,由系统为你接收数据,而你需要做的只是为系统提供一个缓冲区~~~~~
Listen线程和WSAEventSelect模型一模一样,Recv/Send线程则完全不同:
procedure TOverlapThread.Execute;
var
  dwTemp : DWORD;
  ret    : Integer;
  Index  : DWORD;
begin
  ......

  while ( not Terminated ) do
  begin
    Index := WSAWaitForMultipleEvents( FLinks.Count, @FLinks.Events[0], FALSE, RECV_TIME_OUT, FALSE );
    Dec( Index, WSA_WAIT_EVENT_0 );
    if Index > WSA_MAXIMUM_WAIT_EVENTS-1 then //超时或者其他错误
       continue;

    WSAResetEvent( FLinks.Events[Index] );
    WSAGetOverlappedResult( FLinks.Sockets[Index], FLinks.pOverlaps[Index], @dwTemp, FALSE, FLinks.pdwFlags[Index]^ );

    if dwTemp = 0 then //连接已经关闭
    begin
      ......
      continue;
    end else
    begin
      fmMain.ListBox1.Items.Add( FLinks.pBufs[Index]^.buf );
    end;

    //初始化缓冲区
    FLinks.pdwFlags[Index]^ := 0;
    FillChar( FLinks.pOverlaps[Index]^, sizeof(WSAOVERLAPPED), 0 );
    FLinks.pOverlaps[Index]^.hEvent := FLinks.Events[Index];
    FillChar( FLinks.pBufs[Index]^.buf^, BUFFER_SIZE, 0 );

    //递一个接收数据请求
    WSARecv( FLinks.Sockets[Index], FLinks.pBufs[Index], 1, FLinks.pdwRecvd[Index]^, FLinks.pdwFlags[Index]^, FLinks.pOverlaps[Index], nil );
  end;
end;

五:Overlapped I/O 完成例程模型

老陈接收到新的信件后,一般的程序是:打开信封----掏出信纸----阅读信件----回复信件......为了进一步减轻用户负担,微软又开发了一种新的技术:用户只要告诉微软对信件的操作步骤,微软信箱将按照这些步骤去处理信件,不再需要用户亲自拆信/阅读/回复了!老陈终于过上了小资生活!

Overlapped I/O 完成例程要求用户提供一个回调函数,发生新的网络事件的时候系统将执行这个函数:
procedure WorkerRoutine( const dwError, cbTransferred : DWORD; const
          lpOverlapped : LPWSAOVERLAPPED; const dwFlags : DWORD ); stdc

[1] [2] 下一页






  • 上一篇文章:
  • 下一篇文章:
  • 分享此文:该页面添加到 Mister Wong 添加到雅虎Yahoo!收藏 Add to:Del.icio.us Post to Furl Digg this 添加到Google书签 reddit spurl blogmarks 365Key 评论  收藏  分享  打印
     我来说两句
    姓名:       验证码:   
    主页: 
    评分: 1分 2分 3分 4分 5分
    本频道近期热评文章:
      关于我们 | 联系我们 | 站点地图 | 广告投放 | 友情链接 | 在线留言 | 版权申明
    版权所有 © 2004-2007 顶尖设计(bobd.cn)
    未经授权禁止转载,摘编,复制本站内容或建立镜像. 沪ICP备07504942号 
    网络110
    报警服务