端口复用以及完成端口DEMO(200大洋)(200分)

  • 主题发起人 主题发起人 阿伟~
  • 开始时间 开始时间

阿伟~

Unregistered / Unconfirmed
GUEST, unregistred user!
征求端口复用以及完成端口的小DEMO,并能提供一些入门级的说明文档,

在此先谢过了

我先把我关于这2个技术的了解罗列如下:
关于端口复用:字面意思是否就是端口的重复使用?但又听说好象135端口是无法复用的?

完成端口:一种模型或说一种机制?主要用于高性能服务器上?常与异步重叠I/O技术相结合?
呵呵,只是胡乱说了几点,其实在我脑中对他们还是有斗大的疑问的,因为不懂嘛
 
能复用吗
 
呵呵,当然是可以的,如果想学的话帮忙一起顶,别让帖子沉了
 
难道现在物价上涨了?200大洋都不够大虾们出来活动下?
 
http://www.tomore.com/1/22032.html
http://www.tomore.com/1/6272.html
 
谢谢KGM,我看了一个封装完成端口的类,我现在要求的是入门级的就是一个简单的完成端口以及端口复用的小DEMO,并带有解释就OK的
 
呵呵,完成端口模型其实比较简单了。记得在CSDN的blog中有一篇文章说的就是将VC的代码转换成delphi的代码的。你可以用google搜一下。我自己写的IOCP不能给你呵呵!
 
我有个例子还没来得及看,你的email是??,我发给你。
 
heihei19811222@163.com
 
我还没收到啊,QQ:19810230
 
我收藏的代码,是别人的,全部给你了:
基于Delphi的Socket I/O模型全接触

http://tech.163.com/school • 2005-03-31 09:53:14 • 来源: Delphi先锋网 第1页:基于Delphi的Socket I/O模型全接触 第2页:基于Delphi的Socket I/O模型全接触 第3页:基于Delphi的Socket I/O模型全接触

  老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。

   这和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;


老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。

   这和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;

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

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

   Overlapped I/O 完成例程要求用户提供一个回调函数,发生新的网络事件的时候系统将执行这个函数:



procedure WorkerRoutine( const dwError, cbTransferred : DWORD;
const
lpOverlapped : LPWSAOVERLAPPED; const dwFlags : DWORD ); stdcall;

   然后告诉系统用WorkerRoutine函数处理接收到的数据:


WSARecv( m_socket, @FBuf, 1, dwTemp, dwFlag, @m_overlap, WorkerRoutine );

   然后......没有什么然后了,系统什么都给你做了!微软真实体贴!


while ( not Terminated ) do//这就是一个Recv/Send线程要做的事情......什么都不用做啊!!!
begin
  if SleepEx( RECV_TIME_OUT, True ) = WAIT_IO_COMPLETION then //
  begin
   ;
  end else
  begin
   continue;
  end;
end;

   六:IOCP模型

   微软信箱似乎很完美,老陈也很满意。但是在一些大公司情况却完全不同!这些大公司有数以万计的信箱,每秒钟都有数以百计的信件需要处理,以至于微软信箱经常因超负荷运转而崩溃!需要重新启动!微软不得不使出杀手锏......

   微软给每个大公司派了一名名叫“Completion Port”的超级机器人,让这个机器人去处理那些信件!

   “Windows NT小组注意到这些应用程序的性能没有预料的那么高。特别的,处理很多同时的客户请求意味着很多线程并发地运行在系统中。因为所有这些线程都是可运行的[没有被挂起和等待发生什么事],Microsoft意识到NT内核花费了太多的时间来转换运行线程的上下文[Context],线程就没有得到很多CPU时间来做它们的工作。大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比起创建进程开销要小,但也远不是没有开销的。我们不妨设想一下:如果事先开好N个线程,让它们在那hold[堵塞],然后可以将所有用户的请求都投递到一个消息队列中去。然后那N个线程逐一从消息队列中去取出消息并加以处理。就可以避免针对每一个用户请求都开线程。不仅减少了线程的资源,也提高了线程的利用率。理论上很不错,你想我等泛泛之辈都能想出来的问题,Microsoft又怎会没有考虑到呢?”-----摘自nonocast的《理解I/O Completion Port》

   先看一下IOCP模型的实现:


//创建一个完成端口
FCompletPort := CreateIoCompletionPort( INVALID_HANDLE_VALUE, 0,0,0 );

//接受远程连接,并把这个连接的socket句柄绑定到刚才创建的IOCP上
AConnect := accept( FListenSock, addr, len);
CreateIoCompletionPort( AConnect, FCompletPort, nil, 0 );

//创建CPU数*2 + 2个线程
for i:=1 to si.dwNumberOfProcessors*2+2 do
begin
  AThread := TRecvSendThread.Create( false );
  AThread.CompletPort := FCompletPort;//告诉这个线程,你要去这个IOCP去访问数据
end;

   就这么简单,我们要做的就是建立一个IOCP,把远程连接的socket句柄绑定到刚才创建的IOCP上,最后创建n个线程,并告诉这n个线程到这个IOCP上去访问数据就可以了。

   再看一下TRecvSendThread线程都干些什么:


procedure TRecvSendThread.Execute;
var
  ......
begin
  while (not self.Terminated) do
  begin
   //查询IOCP状态(数据读写操作是否完成)
   GetQueuedCompletionStatus( CompletPort, BytesTransd, CompletKey, POVERLAPPED(pPerIoDat), TIME_OUT );

   if BytesTransd <> 0 then
    ....;//数据读写操作完成
  
    //再投递一个读数据请求
    WSARecv( CompletKey, @(pPerIoDat^.BufData), 1, BytesRecv, Flags, @(pPerIoDat^.Overlap), nil );
   end;
end;

   读写线程只是简单地检查IOCP是否完成了我们投递的读写操作,如果完成了则再投递一个新的读写请求。

   应该注意到,我们创建的所有TRecvSendThread都在访问同一个IOCP(因为我们只创建了一个IOCP),并且我们没有使用临界区!难道不会产生冲突吗?不用考虑同步问题吗?

   这正是IOCP的奥妙所在。IOCP不是一个普通的对象,不需要考虑线程安全问题。它会自动调配访问它的线程:如果某个socket上有一个线程A正在访问,那么线程B的访问请求会被分配到另外一个socket。这一切都是由系统自动调配的,我们无需过问。
 
to chnplzh
太谢谢你了,这段例子通俗且使用,非常适合我,谢谢,等结帖时给重分

我还需要些资料,资料不怕多,越多越好
 
to fxh7622:

你的意思我明白,呵呵,我希望你能稍微莳萝的把你的IOCP写成个入门的小DEMO并配备解释

帮助他人成长自己也快乐嘛
 
可能是公司的邮箱不行,我在这儿只能上公司邮箱,对不起了。
 
。。。。我QQ已经放出来了的啊~~~~~`
 
qq早就被封杀了,我这里技术不能聊天,就连bbs也是勉强能上,没办法啊。
 
呵呵,那不勉强你了,我现在在看chnplzh的例子,不错
 
我又发了一遍,据同事说这里对163的邮箱时不时行,你收收看吧,没有也没办法了,不过chnplzh已经给了例子,希望你能提高技术。
 
呵呵,没收到,不过没关系,有人愿意帮助进步是好事,可惜我是个菜鸟,分不多,家底快般光了:)
 
后退
顶部