基于Delphi的Socket I/O模型全接触

页面导航:首页 > 软件编程 > Delphi > 基于Delphi的Socket I/O模型全接触

基于Delphi的Socket I/O模型全接触

来源: 作者: 时间:2016-02-05 11:47 【

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

   这和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的aram是产生了网络事件的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;
Tags:

文章评论

最 近 更 新
热 点 排 行
Js与CSS工具
代码转换工具

<