Socket I/O模型全接触(转)
2011-02-16 11:21
363 查看
声明:除CSDN外的任何媒体转载必须注明作者以及“转载自CSDN”。
本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教。
一:select模型
二:WSAAsyncSelect模型
三:WSAEventSelect模型
四:Overlapped I/O 事件通知模型
五:Overlapped I/O 完成例程模型
六:IOCP模型
老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。
这和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域添加一个处理此消息的函数声明:
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
socketbind
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线程则完全不同:
Code
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
不过代码写的很简陋,请多包涵!
转自:http://blog.csdn.net/flyinwuhan/
本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教。
一:select模型
二:WSAAsyncSelect模型
三:WSAEventSelect模型
四:Overlapped I/O 事件通知模型
五:Overlapped I/O 完成例程模型
六:IOCP模型
老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。
这和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域添加一个处理此消息的函数声明:
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
socketbind
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线程则完全不同:
Code
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
不过代码写的很简陋,请多包涵!
转自:http://blog.csdn.net/flyinwuhan/
相关文章推荐
- 基于Delphi的Socket I/O模型全接触 good
- Socket I/O模型全接触
- Socket I/O模型全接触
- Socket I/O模型全接触
- 基于Delphi的Socket I/O模型全接触
- Socket I/O模型全接触
- 基于Delphi的Socket I/O模型全接触
- Socket I/O模型全接触
- 基于Delphi的Socket I/O模型全接触(总结) [转]
- Socket I/O模型全接触
- Socket I/O模型全接触
- Socket I/O模型全接触
- 《Socket I/O模型全接触》Delphi版代码
- 基于Delphi的Socket I/O模型全接触
- Socket I/O模型全接触
- Socket I/O模型全接触
- 基于Delphi的Socket I/O模型全接触
- Socket I/O模型全接触
- Socket I/O模型全接触
- 基于Delphi的Socket I/O模型全接触