很幽默的讲解六种Socket IO模型.doc_第1页
很幽默的讲解六种Socket IO模型.doc_第2页
很幽默的讲解六种Socket IO模型.doc_第3页
很幽默的讲解六种Socket IO模型.doc_第4页
很幽默的讲解六种Socket IO模型.doc_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教。一:select模型二:WSAAsyncSelect模型三:WSAEventSelect模型四:Overlapped I/O 事件通知模型五:Overlapped I/O 完成例程模型六:IOCP模型老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。这和Socket模型非常类似。下面我就以老陈接收信件为例讲解 Socket I/O模型一:select模型老陈非常想看到女儿的信。以至于他每隔10分钟就下楼检查信箱,看是否有女儿的信 在这种情况下,下楼检查信箱 然后回到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作。select模型和老陈的这种情况非常相似:周而复始地去检查. 如果有数据.接收/发送 .使用线程来select应该是通用的做法:procedureTListenThread.Execute;varaddr:TSockAddrIn;fd_read:TFDSet;timeout:TTimeVal;ASock,MainSock:TSocket;len,i:Integer;beginMainSock:=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(notTerminated)dobeginFD_ZERO(fd_read);FD_SET(MainSock,fd_read);timeout.tv_sec:=0;timeout.tv_usec:=500;ifselect(0,fd_read,nil,nil,timeout)0then/至少有1个等待Accept的connectionbeginifFD_ISSET(MainSock,fd_read)thenbeginfori:=0tofd_read.fd_count-1do/注意,fd_count=64,也就是说select只能同时管理最多64个连接beginlen:=sizeof(addr);ASock:=accept(MainSock,addr,len);ifASockINVALID_SOCKETthen./为ASock创建一个新的线程,在新的线程中再不停地selectend;end;end;end;/while(notself.Terminated)shutdown(MainSock,SD_BOTH);closesocket(MainSock);end;二:WSAAsyncSelect模型后来,老陈使用了微软公司的新式信箱。这种信箱非常先进,一旦信箱里有新的信件,盖茨就会给老陈打电话:喂,大爷,你有新的信件了!从此,老陈再也不必频繁上下楼检查信箱了,牙也不疼了,你瞅准了,蓝天 .不是,微软微软提供的WSAAsyncSelect模型就是这个意思。WSAAsyncSelect模型是Windows 下最简单易用的一种Socket I/O模型。使用这种模型时,Windows会把网络事件以消息的形势通知应用程序。首先定义一个消息标示常量:const WM_SOCKET = WM_USER + 55;再在主Form的private 域添加一个处理此消息的函数声明:privateprocedure WMSocket(var Msg: TMessage); message WM_SOCKET;然后就可以使用WSAAsyncSelect了:varaddr : 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);varsock : TSocket;addr : TSockAddrIn;addrlen : Integer;buf : Array 0.4095 of Char;begin/Msg的WParam是产生了网络事件的 socket句柄,LParam则包含了事件类型case WSAGetSelectEvent( Msg.LParam ) ofFD_ACCEPT :beginaddrlen := sizeof(addr);sock := accept( Msg.WParam, addr, addrlen );if sock INVALID_SOCKET thenWSAAsyncSelect( sock, Handle, WM_SOCKET, FD_READ or FD_WRITE or FD_CLOSE );end;FD_CLOSE : closesocket( Msg.WParam );FD_READ : recv( Msg.WParam, buf0, 4096, 0 );FD_WRITE : ;end;end;三:WSAEventSelect模型后来,微软的信箱非常畅销,购买微软信箱的人以百万计数.以至于盖茨每天 24小时给客户打电话,累得腰酸背痛,喝蚁力神都不好使微软改进了他们的信箱:在客户的家中添加一个附加装置,这个装置会监视客户的信箱,每当新的信件来临,此装置会发出 新信件到达声,提醒老陈去收信。盖茨终于可以睡觉了。同样要使用线程:procedure TListenThread.Execute;varhEvent : 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 ) dobeginIndex := WSAWaitForMultipleEvents( EventTotal, EventArray0, FALSE, WSA_INFINITE, FALSE );FillChar( ne, sizeof(ne), 0 );WSAEnumNetworkEvents( SockArrayIndex-WSA_WAIT_EVENT_0, EventArrayIndex-WSA_WAIT_EVENT_0, ne );if ( ne.lNetworkEvents and FD_ACCEPT ) 0 thenbeginif ne.iErrorCodeFD_ACCEPT_BIT 0 thencontinue;ret := sizeof(adr);sock := accept( SockArrayIndex-WSA_WAIT_EVENT_0, adr, ret );if EventTotal WSA_MAXIMUM_WAIT_EVENTS-1 then/这里WSA_MAXIMUM_WAIT_EVENTS 同样是64beginclosesocket( sock );continue;end;hEvent := WSACreateEvent();WSAEventSelect( sock, hEvent, FD_READ or FD_WRITE or FD_CLOSE );SockArrayEventTotal := sock;EventArrayEventTotal := hEvent;Inc( EventTotal );end;if ( ne.lNetworkEvents and FD_READ ) 0 thenbeginif ne.iErrorCodeFD_READ_BIT 0 thencontinue;FillChar( RecvBuf0, PACK_SIZE_RECEIVE, 0 );ret := recv( SockArrayIndex-WSA_WAIT_EVENT_0, RecvBuf0, 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;vardwTemp : DWORD;ret : Integer;Index : DWORD;begin.while ( not Terminated ) dobeginIndex := WSAWaitForMultipleEvents( FLinks.Count, FLinks.Events0, FALSE, RECV_TIME_OUT, FALSE );Dec( Index, WSA_WAIT_EVENT_0 );if Index WSA_MAXIMUM_WAIT_EVENTS-1 then /超时或者其他错误continue;WSAResetEvent( FLinks.EventsIndex );WSAGetOverlappedResult( FLinks.SocketsIndex, FLinks.pOverlapsIndex, dwTemp, FALSE, FLinks.pdwFlagsIndex );if dwTemp = 0 then /连接已经关闭begin.continue;end elsebeginfmMain.ListBox1.Items.Add( FLinks.pBufsIndex.buf );end;/初始化缓冲区FLinks.pdwFlagsIndex := 0;FillChar( FLinks.pOverlapsIndex, sizeof(WSAOVERLAPPED), 0 );FLinks.pOverlapsIndex.hEvent := FLinks.EventsIndex;FillChar( FLinks.pBufsIndex.buf, BUFFER_SIZE, 0 );/递一个接收数据请求WSARecv( FLinks.SocketsIndex, FLinks.pBufsIndex, 1, FLinks.pdwRecvdIndex, FLinks.pdwFlagsIndex, FLinks.pOverlapsIndex, nil );end;end;五:Overlapped I/O 完成例程模型老陈接收到新的信件后,一般的程序是:打开信封-掏出信纸 -阅读信件-回复信件 .为了进一步减轻用户负担,微软又开发了一种新的技术:用户只要告诉微软对信件的操作步骤,微软信箱将按照这些步骤去处理信件,不再需要用户亲自拆信 /阅读/回复了!老陈终于过上了小资生活!Overlapped I/O 完成例程要求用户提供一个回调函数,发生新的网络事件的时候系统将执行这个函数:procedure WorkerRoutine( const dwError, cbTransferred : DWORD; constlpOverlapped : LPWSAOVERLAPPED; const dwFlags : DWORD ); stdcall;然后告诉系统用WorkerRoutine函数处理接收到的数据:WSARecv( m_socket, FBuf, 1, dwTemp, dwFlag, m_overlap, WorkerRoutine );然后.没有什么然后了,系统什么都给你做了!微软真实体贴!while ( not Terminated ) do/这就是一个Recv/Send线程要做的事情 .什么都不用做啊!beginif SleepEx( RECV_TIME_OUT, True ) = WAIT_IO_COMPLETION then /begin;end elsebegincontinue;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 dobeginAThread := TRecvSendThread.Create( false );AThread.CompletPort := FComp

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论