一个服务端,被多个用户连接,为了节约环境资源,减少线程 。
轮询
缺点:客户端多的情况下,询问时间大大大于接受时间浪费资源,一般为64位以下用户体验不到卡顿的效果
//设置异步套接字,使可以接受数据不等待 设置套接字阻塞和非阻塞
u_long block = 1;
ioctlsocket(s, FIONBIO, &block);
//接受了一个客户端就将它加入队列
int cs = accept(s, (sockaddr*)&caddr, &len);
clients[count++] = cs;
//轮询 轮流问一下每个客户端是否有消息接受
for (int i = 0; i < count; i++)
{
char msg[260] = { 0 };
int bytes = recv(clients[i], msg, sizeof(msg), 0);
printf("socket:%d bytes:%d msg:%s ", clients[i], bytes, msg);
}
用API实现轮询,操作系统内部实现,比手工实现速度更优
主线程 记录多少个线程 和多少个客户端 一般64个客户端一个线程
客户端 Cline[64]; nCline;
线程 Thread[65535]; nThread;
每产生一个线程都需要new一个客户端 每来一个客户端 客户端计数加一socket放进数组
产生支线程需要将客户端信息另存进 fd_set 结构体中,避免和主线程同步。
fd_set { u_int fd_nCount, Socket fd_Array[FD_SETSIZE]};
FD_ZERO(&set); /*将set清零使集合中不含任何fd*/
FD_SET(fd, &set); /*将fd加入set集合*/
FD_CLR(fd, &set); /*将fd从set集合中清除*/
FD_ISSET(fd, &set); /*在调用select()函数后,用FD_ISSET来检测fd是否在set集合中,当检测到fd在set中则返回真,否则,返回假(0)*/
fd_set set;
FD_ZERO(&set); 初始化
for (int i = 0; i < info->count; i++) 添加socket加入set集合
{
FD_SET(info->clients[i], &set);
}
//轮询
timeval tv = { 2 };
int recv_count = select(0, &set, NULL, NULL, &tv);
for (int i = 0; i < set.fd_count; i++)
{
If(FD_ISSET(set.fd_array[i], &set))
{
recv(set.fd_array[i], msg, sizeof(msg), 0);
printf("socket:%d bytes:%d msg:%s ", set.fd_array[i], bytes, msg);
}
}
因为在接收数据方面花费时间太多,所以希望有消息就通知
通知套接字端口有请求事件发生.
#include <winsock.h>
intPASCAL FAR WSAAsyncSelect (SOCKET s,HWND hWnd,
unsigned int wMsg,long lEvent);
s 标识一个需要事件通知的套接口的描述符.
hWnd 标识一个在网络事件发生时需要接收消息的窗口句柄.
wMsg 在网络事件发生时要接收的消息.
lEvent位屏蔽码,用于指明应用程序感兴趣的网络事件集合.
但是这个api必须要窗口过程才能使用。
产生客户端就产生消息请求,通过参数知道对应的socket和消息参数
lParam的高字段则含有一个错误码,事件和错误码可以通过下面的宏从lParam中取出:
#define WSAGETSELECTEVENT(lParam) LOWORD(lParam) //获取事件
#define WSAGETSELECTERROR(lParam) HIWORD(lParam) //获取错误
创建一个初始状态为失信的匿名的需要手动重置的事件对象。
#include <winsock2.h>
WSAEVENT WSAAPI WSACreateEvent( VOID );
返回值:
如果函数成功,则返回值即是事件对象的句柄。
如果函数失败,返回WSA_INVALID_EVENT。应用程序可通过调用WSAGetLastError()函数获取进一步的错误信息。
错误代码:
WSANOTINITIALISED 在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN 网络子系统失效。
WSA_NOT_ENOUGH_MEMORY 无足够内存创建事件对象。
WSAEventSelect模型是WindowsSockets提供的一个有用异步I/O模型。该模型允许在一个或者多个套接字上接收以事件为基础的网络事件通知。Windows Sockets应用程序在创建套接字后,调用WSAEventSelect()函数,将一个事件对象与网络事件集合关联在一起。当网络事件发生时,应用程序以事件的形式接收网络事件通知。
使用这个模型的基本思路是为感兴趣的一组网络事件创建一个事件对象,再调用WSAEventSelect()函数将网络事件和事件对象关联起来。当网络事件发生时,Winsock使相应的事件对象受信,在事件对象上的等待函数就会返回。
Winsock中创建事件对象的函数是WSACreateEvent,定义如下:
WSAEVENT WSACreateEvent(void); //返回一个手工重置的事件对象句柄
创建事件对象之后,必须调用WSAEventSelect函数将指定的一组网络事件与它关联在一起,函数用法如下。
WSAEventSelect模型简单易用,也不需要窗口环境。该模型唯一的缺点是有最多等待64个事件对象的限制,当套接字连接数量增加时,就必须创建多个线程来处理I/O,也就是所谓的线程池。