select选择模型是套接字进行异步IO的基本模式,非常方便。
在C++中,select()函数的原型如下:
int select( __in int nfds, __in_out fd_set* readfds, __in_out fd_set* writefds, __in_out fd_set* exceptfds, __in const struct timeval* timeout );
即,使用fd_set结构体来存放读、写、异常状况的套接字;
使用宏FD_ZEOR来清空fd_set集、FD_SET来增加套接字到某个fd_set集中、FD_CLR来删除某个fd_set集中的套接字、FD_ISSET来判断套接字是否属于某个fd_set集;
timeout参数使用INFINITE可一直等待,直到函数放回(有套接字满足条件)
返回值是原fd_set集经过select()函数筛选之后的满足条件(即有读、写或异常状态)套接字的数量,然后依次进行I/O操作;
在C#中,select方法内化为Socket类的静态方法之一,fd_set也用更为方便的ArrayList类来代替(该类实现了IList接口)
select方法的原型如下:
public static void Select( IList checkRead, IList checkWrite, IList checkError, int microSeconds )
当microSeconds=-1时,表示一直等待,直到套接字满足条件后返回
可以看到,该函数没有返回值。因为满足需要的套接字都留在了ArrayList类的对象中,用ArrayList对象的count属性判断个数,以数组下标形式逐个访问套接字以进行I/O操作即可。
下面是MSDN中给出的一个示例(有改动):
//获取主机IP
IPHostEntry ipHostEntry = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostEntry.AddressList[0];
Socket socket0 = null;
Socket socket1 = null;
Socket socket2 = null;
Socket remoteSock = null;
//存放监听套接字
ArrayList listenList = new ArrayList();
listenList.Add(socket0);
listenList.Add(socket1);
listenList.Add(socket2);
//创建3个TCP监听套接字
for( int i = 0; i < 3; i++ )
{
listenList[i] = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
//注意类型转换
((Socket)listenList[i]).Bind(new IPEndPoint(ipAddress, 11000 + i));
((Socket)listenList[i]).Listen(10);
}
// Only the sockets that contain a connection request
// will remain in listenList after Select returns.
Socket.Select(listenList, null, null, 1000);
for( int i = 0; i < listenList.Count; i++ )
{
acceptList[i] = ((Socket)listenList[i]).Accept();
}