声明:除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域添加一个处理此消息的函数声明:
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;
三:wsaeventselect模型
后来,微软的信箱非常畅销,购买微软信箱的人以百万计数......以至于盖茨每天24小时给客户打电话,累得腰酸背痛,喝蚁力神都不好使~~~~~~
微软改进了他们的信箱:在客户的家中添加一个附加装置,这个装置会监视客户的信箱,每当新的信件来临,此装置会发出“新信件到达”声,提醒老陈去收信。盖茨终于可以睡觉了。