winsock编程select模型

  网络服务端连接数量过多时,为每一个连接申请一个线程会让机器性能急剧下降(大多说是因为线程在用户态和内核态之间切换会占用大量的CPU时间片)。为了解决多线程带来的性能下降问题,windows提供了5种网络编程模型。这其中,最简单的就是select模型。

  select模型的基本思想是,同时管理一组socket。每次调用select,系统会检查这一组socket的状态,并返回可读、可写、异常socket的信息。有了socket信息,便可进行recv、send操作。为了不阻塞主线程,对一组socket的轮询操作仅需要开辟一个线程即可。当然,这一组socket的大小不能无限制增长,windows定义了select调用参数socket数组的最大长度为64。如果当前socket数组的长度大于64,需要将该数组划分成长度小于等于64的小数组,分别进行轮询。

  select模型主要用到以下函数和宏:

1:select函数

Description:The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O。

 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
);

Parameters

nfds

Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.该参数会被忽略掉

readfds

Optional pointer to a set of sockets to be checked for readability.

writefds

Optional pointer to a set of sockets to be checked for writability.

exceptfds

Optional pointer to a set of sockets to be checked for errors.

timeout

Maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations.

Return Value

  The select function returns the total number of socket handles that are ready and contained in the fd_set structures, zero if the time limit expired, or SOCKET_ERROR if an error occurred. If the return value is SOCKET_ERROR, WSAGetLastError can be used to retrieve a specific error code.

  select返回值0,时间超时。返回值大于0,表示传入参数fd_set中中已就绪的socket数量。

2:ioctlsocket

Descriptio:The ioctlsocket function controls the I/O mode of a socket.

 int ioctlsocket(
__in SOCKET s,
__in long cmd,
__in_out u_long* argp
);

Parameters

s

Descriptor identifying a socket.

cmd

Command to perform on the socket s.

argp

Pointer to a parameter for cmd.

Return Value

  Upon successful completion, the ioctlsocket returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

  ioctlsocket用来设置socket的IO传输模式,返回值0,表示设置成功。cmd参数和argp参数需要配合使用,常用的参数值组合如下:

  A:cmd:FIONBIO    argp为0表示设置soclet为阻塞模式,非0表示设置socket为非阻塞模式。

  B:cmd:FIONREAD   用了确定socket的输入缓冲区内剩余的可读字节数,用argp接收该数值。

  C:cmd:SIOCATMARK  用来确定out of band (OOB)数据是否已读。

  ioctlsocket 设置socket味非阻塞模式的代码如下:

 u_long iMode = ;
ioctlsocket(m_socket, FIONBIO, &iMode);

3:fd_set结构体和相关宏

  select传入的参数为fd_set结构体,fd_set定义如下:

 typedef struct fd_set {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE];
} fd_set;

  FD_SETSIZE的值为64,也就是说windows已经把数组fd_array的长度定义好,不要超过该长度。下面是操作fd_set结构体的几个宏:

  FD_ISSET宏:用于判断socket是否在fd_set中,是返回true,否则返回false。

#define FD_ISSET(fd, set) __WSAFDIsSet((SOCKET)(fd), (fd_set FAR *)(set))

  FD_ZERO宏:把fd_set内部的count赋值为0.

#define FD_ZERO(set) (((fd_set FAR *)(set))->fd_count=0)

  FD_CLR宏:在fd_set数组中清除一个socket

 #define FD_CLR(fd, set) do { \
u_int __i; \
for (__i = ; __i < ((fd_set FAR *)(set))->fd_count ; __i++) { \
if (((fd_set FAR *)(set))->fd_array[__i] == fd) { \
while (__i < ((fd_set FAR *)(set))->fd_count-) { \
((fd_set FAR *)(set))->fd_array[__i] = \
((fd_set FAR *)(set))->fd_array[__i+]; \
__i++; \
} \
((fd_set FAR *)(set))->fd_count--; \
break; \
} \
} \
} while()

  FD_SET宏:在fd_set数组中添加一个socket

 #define FD_SET(fd, set) do { \
u_int __i; \
for (__i = ; __i < ((fd_set FAR *)(set))->fd_count; __i++) { \
if (((fd_set FAR *)(set))->fd_array[__i] == (fd)) { \
break; \
} \
} \
if (__i == ((fd_set FAR *)(set))->fd_count) { \
if (((fd_set FAR *)(set))->fd_count < FD_SETSIZE) { \
((fd_set FAR *)(set))->fd_array[__i] = (fd); \
((fd_set FAR *)(set))->fd_count++; \
} \
} \
} while()

  timeval结构体,select超时参数

 /* Structure used in select() call, taken from the BSD file sys/time.h.*/
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};

4:例子程序

  select模式主要用到的就是select和ioctlsocket函数,下面是例子程序

 void main()
{
WSAData wsa;
if (WSAStartup(MAKEWORD(,),&wsa) != )
{
WSACleanup();
return ;
}
SOCKET listensocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
do
{
if (listensocket == INVALID_SOCKET)
break;
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_port = htons();
service.sin_addr.s_addr = INADDR_ANY;
if (bind(listensocket,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR)
{
cout<<"bind error,error code "<<WSAGetLastError()<<endl;
break;
}
if (listen(listensocket,SOMAXCONN) == SOCKET_ERROR)
{
cout<<"listen error,error code "<<WSAGetLastError()<<endl;
break;
}
//以下是select相关代码
//此处把listensocket设置为非阻塞模式
u_long unblock = ; //非0为非阻塞模式
if (ioctlsocket(listensocket,FIONBIO,&unblock) == SOCKET_ERROR)
{
cout<<"ioctlsocket(listensocket) error,error code "<<WSAGetLastError()<<endl;
break;
}
fd_set m_readset;
fd_set m_writeset;
FD_SET(listensocket,&m_readset);
SOCKET socks[];
socks[] = listensocket;
int TotalSockets = ;
while ()
{
fd_set readset = m_readset;
fd_set writeset = m_writeset;
timeval tv;
tv.tv_sec = ;
tv.tv_usec = ;
int res = select(,&readset,&writeset,NULL,&tv);
if (res == SOCKET_ERROR)
{
cout<<"select error,error code "<<WSAGetLastError()<<endl;
break;
}
if (res == )
{
continue;//表示当前无状态可控的socket
}
int tempTotalSockets = TotalSockets;
for (int i=;i<TotalSockets;i++)
{
if (FD_ISSET(socks[i],&readset))
{
if (socks[i] == listensocket)
{
//listensocket可读,表示连接到达
SOCKET acp = accept(listensocket,NULL,NULL);
if (acp == INVALID_SOCKET)
{
cout<<"accept error,error code "<<WSAGetLastError()<<endl;
break;
}
//设置新到达socket为非阻塞模式,并加入socks以及fdset
if (ioctlsocket(acp,FIONBIO,&unblock) == SOCKET_ERROR)
{
cout<<"ioctlsocket(acp) error,error code "<<WSAGetLastError()<<endl;
break;
}
FD_SET(acp,m_readset);
FD_SET(acp,m_writeset);
socks[tempTotalSockets++] = acp;
}
else
{
char buf[];
int len = recv(socks[i],buf,,);
if (len == )
{
cout<<"connection has been closed "<<endl;
break;
}
else if (len == SOCKET_ERROR)
{
cout<<"recv error,error code "<<WSAGetLastError()<<endl;
break;
}
else
{
char* outbuf = new char[len+];
memcpy(outbuf,buf,len);
outbuf[len] = ;
cout<<"recv data,"<<outbuf<<endl;
delete outbuf;
} }
}
else if (FD_ISSET(socks[i],&writeset))
{
std::string str = "send data!";
if (send(socks[i],str.c_str(),str.length(),) == SOCKET_ERROR)
{
cout<<"send error,error code "<<WSAGetLastError()<<endl;
break;
}
}
}
TotalSockets = tempTotalSockets;
} } while ();
closesocket(listensocket);
WSACleanup();
}

  

winsock编程select模型的更多相关文章

  1. winsock编程IOCP模型实现代码

    winsock编程IOCP模型实现代码 话不多说,上代码.借鉴<windows核心编程>部分源码和CSDN小猪部分代码. stdafx.h依赖头文件: #include <iostr ...

  2. winsock编程WSAEventSelect模型

    winsock编程WSAEventSelect模型 WSAEventSelect模型和WSAAsyncSelec模型类似,都是用调用WSAXXXXXSelec函数将socket和事件关联并注册到系统, ...

  3. winsock编程WSAAsyncSelect模型

    winsock编程WSAAsyncSelect模型 WSAAsyncSelect模型也称异步选择模型,其核心函数是WSAAsyncSelect.它可以用来在一个socket上接收以windows消息为 ...

  4. windows socket编程select模型使用

    int select(         int nfds,            //忽略         fd_ser* readfds,    //指向一个套接字集合,用来检测其可读性       ...

  5. UNIX网络编程-Select模型学习

    1.相关接口介绍 1.1 select ---------------------------------------------------------------------- #include ...

  6. socket编程的select模型

    在掌握了socket相关的一些函数后,套接字编程还是比较简单的,日常工作中碰到很多的问题就是客户端/服务器模型中,如何让服务端在同一时间高效的处理多个客户端的连接,我们的处理办法可能会是在服务端不停的 ...

  7. Winsock IO模型之select模型

    之所以称其为select模型是因为它主要是使用select函数来管理I/O的.这个模型的设计源于UNIX系统,目的是允许那些想要避免在套接字调用上阻塞的应用程序有能力管理多个套接字. int sele ...

  8. 网络编程第六讲Select模型

    网络模型第六讲Select模型 一丶Select模型是什么 以前我们讲过一个迭代模型.就是只服务一个客户端连接.但是实际网络编程中.复杂的很多. 比如一个 C/S架构程序 (客户端/服务端) 客户端很 ...

  9. Windows网络编程系列教程之四:Select模型

    讲一下套接字模式和套接字I/O模型的区别.先说明一下,只针对Winsock,如果你要骨头里挑鸡蛋把UNIX下的套接字概念来往这里套,那就不关我的事. 套接字模式:阻塞套接字和非阻塞套接字.或者叫同步套 ...

随机推荐

  1. asp.net mvc 上传下载文件的几种方式

    view: <!DOCTYPE html> <html> <head> <meta name="viewport" content=&qu ...

  2. 洛谷-求同构数的个数-NOIP2013提高组复赛

    题目描述 Description 所谓同构数是指这样的数,即它出现在它的平方数的右端.例如,5的平方是25 (即5×5=25),5是25右端的数,那么5就是同构数.又如,25的平方是625(即25×2 ...

  3. [Linux]当一个棘手问题需要即可定位,如何协助开发,缩小定位范围

    写在前面:前段时间,朋友给我说了一个她亲身经历的某知名企业面试故事,面试结束感觉自己已脱了一层皮...面试官的问题并不刁钻,但是却是步步紧逼,而且有点类似拜占庭将军问题,只是拜占庭将军问题是所有的假设 ...

  4. LoadRunner日志(归档记录,以便自己查阅)

    1.当设置迭代次数大于1时,回放从第二次迭代开始发生错误 这种现象多是由于在"Run-time Setting"的"Browse Emulation"的设置中, ...

  5. linux 通过pid寻找程序路径的最简单命令

    在linux实际操作命令中,查看pid的方式有很多种,通过pid找程序路径的方式也有好几个,但是可能大家都忽略的一个很简单也是很实用的命令:pwdx. 比如要查找某个java编写的程序运行情况可通过j ...

  6. Linux nfs+autofs 环境搭建

    两台服务器环境为centos 6.6 1.安装配置nfs 安装portmap 和  nfs [root@node0 ~]# yum install portmap [root@node0 ~]# yu ...

  7. was性能调优前期准备

    http://www.ibm.com/developerworks/cn/websphere/library/techarticles/0707_wudan/#resources was应用服务器环境 ...

  8. iOS蓝牙开发

    蓝牙常见名称和缩写 MFI ======= make for ipad ,iphone, itouch 专们为苹果设备制作的设备 BLE ==== buletouch low energy,蓝牙4.0 ...

  9. javaWEB总结(16):jsp错误页面的处理

    前言 网站上线后,jsp页面上有时会出现不友好的错误信息,我们需要展示给用户更加友好的页面.这时候要用到page标签的errorPage和isErrorPage. errorPage 指定当前页面出现 ...

  10. 计算机网络课程优秀备考PPT之第三章数据链路层(三)

    为了记录自己从2016.9~2017.1的<计算机网络>助教生涯,也为了及时梳理和整写笔记! 前期博客是, 计算机网络课程优秀备考PPT之第一章概述(一) 计算机网络课程优秀备考PPT之第 ...