《Socket I/O模型全接触》

flyinwuhan 2005-03-11 11:26:20
准备贴出的东西其实是个人学习socket时写的一些小程序没有仔细斟酌没有严格测试如果你发现其中存在什么错误请务必赐教。。。。。。。先谢了!!!

1。WSAAsyncSelect模型
2。select模型
3。Overlapped I/O 完成例程
4。WSAEventSelect模型
5。Overlapped I/O 事件通知
6。完成端口
...全文
1629 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
adingzhang 2005-03-31
  • 打赏
  • 举报
回复
11
jlbnet 2005-03-16
  • 打赏
  • 举报
回复
不错,收藏
flyinwuhan 2005-03-16
  • 打赏
  • 举报
回复
以后使用签名了 否则找帖子太麻烦

--------------------------------------------------------
三思而后行
lw549 2005-03-15
  • 打赏
  • 举报
回复
8错!
jim138 2005-03-15
  • 打赏
  • 举报
回复
条理清晰,也很幽默.
谢谢楼主
mingyue99 2005-03-15
  • 打赏
  • 举报
回复
好东西,收藏先,再慢慢学习!~~~~
楼主好样的~~`
僵哥 2005-03-15
  • 打赏
  • 举报
回复
撑!!!
szjay 2005-03-15
  • 打赏
  • 举报
回复
楼主是强人,谢谢了,回去慢慢看。
herman~~ 2005-03-15
  • 打赏
  • 举报
回复
学习
aiirii 2005-03-14
  • 打赏
  • 举报
回复
能否整理下, 加多些文字說明, 發到 eMag - delphi 電子雜誌上??
jtg98g3 2005-03-14
  • 打赏
  • 举报
回复
楼主加油!

我在看!
caiso 2005-03-14
  • 打赏
  • 举报
回复
顶一下
flyinwuhan 2005-03-14
  • 打赏
  • 举报
回复
四: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 完成例程模型

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

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;

OK,就这么简单,我们要做的就是建立一个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。这一切都是由系统自动调配的,我们无需过问。

呵呵,终于写完了,好累......以上所有的源代码可以从这里看到:
http://community.csdn.net/Expert/topic/3844/3844679.xml?temp=5.836123E-02
不过代码写的很简陋,请多包涵!
onemonth 2005-03-14
  • 打赏
  • 举报
回复
完成端口,类似于OnXXX事件,或者说是回调函数。也就是windows内核通常采取的方式。只是不需要你注册。然后对你关心的几个事件进行处理。而且它可以在每个cpu上产生一个线程,能够有效的使用多cpu。它工作在windows的内核模式中,少了很多场景切换,性能算最好的。具体看看windows网络设计,将得很多。这个书的第二版写得不好,不清晰。
flyinwuhan 2005-03-14
  • 打赏
  • 举报
回复
>>能否整理下, 加多些文字說明, 發到 eMag - delphi 電子雜誌上??
如果你认为可以,俺就整理一下~~~~荣幸~~~~~~~~~
pilicat 2005-03-13
  • 打赏
  • 举报
回复
good
aa_33 2005-03-13
  • 打赏
  • 举报
回复
谢谢
quansui 2005-03-13
  • 打赏
  • 举报
回复
up,狂学中....
lovefox_zoe 2005-03-13
  • 打赏
  • 举报
回复
楼主,有没有IOCP的例子啊。代码有没有下载啊。
halfdream 2005-03-13
  • 打赏
  • 举报
回复
楼主还有IOCP的没有贴啊?
加载更多回复(15)

1,593

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 网络通信/分布式开发
社区管理员
  • 网络通信/分布式开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧