Socket I/O模型全接触[1]

[入库:2005年8月18日] [更新:2007年3月24日]

本文简介:选择自 flyinwuhan 的 blog

声明:除csdn外的任何媒体转载必须注明作者以及“转载自csdn”。

本文简单介绍了当前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小时给客户打电话,累得腰酸背痛,喝蚁力神都不好使~~~~~~
微软改进了他们的信箱:在客户的家中添加一个附加装置,这个装置会监视客户的信箱,每当新的信件来临,此装置会发出“新信件到达”声,提醒老陈去收信。盖茨终于可以睡觉了。

本文关键:Socket I/O模型全接触
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top