首页 | 互联网 | IT动态 | IT培训 | Cisco | Windows | Linux | Java | .Net | Oracle | 软件测试 | C/C++ | 嵌入式开发 | 存储世界 | 服务器
网络设备 | IDC | 安全 | 求职招聘 | 数字网校 | 网页设计 | 平面设计 | 技术专题 | 电子书下载 | 教学视频 | 源码下载 | 搜索 | 博客 | 论坛
首页 | JAVA | C# | VB | VB.NET | C/C++ | delphi | 工程管理 | 其他语言 | 论坛
各大城市软件开发培训、软件人才免费咨询热线:400-700-5807
 您现在的位置: 中国IT实验室 >> 桌面开发 >> Delphi >> 正文
简单介绍各种Socket I/O模型全接触
来源:ChinaItLab 作者:佚名 时间:2007-4-5

  一: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 完成例程模型
  
  老陈接收到新的信件后,一般的程序是:打开信封--
【责编:Lili】
中国IT教育热线咨询
相关文章
用多线程又有几种常用的编程模型
用UML模型实现大型实时监控应用软件
软件开发质量管理层次模型
OpenGL实现3DS文件中的模型自由旋转
推荐文章

 精彩友情推荐
·Asp源码 PHP源码
·CGI源码 JSP源码
·建站书籍教程
·服务器软件 .net源码
·建站工具软件
·IDC资讯大全
·机房品质万里行
·IDC托管必备知识
·全国IDC报价
·网站推广优化
最新更新 推荐文章
·框架:J2EE WEB应用架构分析…03-13
·几种VC++数据库开发技术的相对比…03-13
·利用C#实现标注式消息提示窗口03-13
·用C#创建COM对象03-13
·Visual C#多线程参数传递浅析…03-13
·Visual C#多线程参数传递浅析…03-13
·基于HOOK和MMF的Win密码渗透技术11-15
·Visual C++设计超强仿QQ自动伸缩…11-15
·Java SE 6.0实现高质量桌面集成开…11-15
·史玉柱东山再起幕后高人11-15
·用C#创建COM对象09-06
·IT管理十大失误及其对策08-30
·VC中利用MFC设计绘图程序初步08-23
·JAVA中对象创建和初始化过程08-23
·C语言中的位域的使用08-09
·浅谈Java桌面应用程序开发08-09
·C#的前途如何?08-02
·几种VC++数据库开发技术的相对比较07-12
·用Visual C#实现网络封包监视…07-12
·VB.NET中的TextBox控件详解07-12
·VB.NET实现PC与掌上电脑PPC的双向通信07-05