由于使用传统的socket模型在处理大量的客户端时需要不断的开辟多余的线程,这样无非会很大程度上浪费系统资源,因此,我们需要一种能够管理套接字的模型。最简单的select选择模型可以采用一种有序的方式,轮询fd_set集合,从而同时进行对多个套接字的管理。
所用结构体:
typedef struct fd_set {
u_int fd_count; /* how many are SET? */
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
1.主要用到的几个函数
int select(
int nfds,
fd_set* readfds,
fd_set* writefds,
fd_set* exceptfds,
const struct timeval* timeout
);
nfds:本参数忽略,仅起到兼容作用。
readfds:(可选)指针,指向一组等待可读性检查的套接口。
writefds:(可选)指针,指向一组等待可写性检查的套接口。
exceptfds:(可选)指针,指向一组等待错误检查的套接口。
timeout:select()最多等待时间,对阻塞操作则为NULL。
FD_CLR(s,*set):从集合set中删除描述字s。
FD_ISSET(s,*set):若s为集合中一员,非零;否则为零。
FD_SET(s,*set):向集合添加描述字s。
FD_ZERO(*set):将set初始化为空集NULL。
2.实现原理
只需创建两个线程,一个主要负责监听客户端的连接,并把套接字加入到fd_set集合中,另一个线程主要负责轮询管理fd_set集合中的套接字进行通讯。
服务器代码:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <WinSock2.h>
#include <Windows.h>
#pragma comment(lib,"ws2_32")
SOCKET serverSock;
int clientnum = 0;
fd_set socket_fd1, fd_read;
int maxfd;
void t_accept()
{
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);
serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKADDR_IN sAddr;
sAddr.sin_family = PF_INET;
sAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
sAddr.sin_port = htons(10086);
bind(serverSock, (SOCKADDR*)&sAddr, sizeof sAddr);
listen(serverSock, 10);
//SOCKET clientSock = NULL;
SOCKADDR_IN cAddr = {};
int nSize = sizeof cAddr;
FD_ZERO(&socket_fd1);
while (1)
{
SOCKET clientSock = accept(serverSock, (SOCKADDR*)&cAddr, &nSize);
printf("there is a client ip : %s, port: %d\n", inet_ntoa(cAddr.sin_addr), ntohs(cAddr.sin_port));
FD_SET(clientSock, &socket_fd1);
maxfd = clientSock;
printf("fd1: %d maxfd: %d\n", socket_fd1.fd_count,maxfd);
clientnum += 1;
}
}
void sendmsg()//轮询进行通讯
{
char sendbuff[1024];
while (1)
{
printf("input: ");
gets_s(sendbuff);
for (int i = 0; i < socket_fd1.fd_count; i++)
{
send(socket_fd1.fd_array[i], sendbuff, strlen(sendbuff), 0);
}
}
}
void main()
{
char recvbuff[1024];
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)t_accept, NULL, 0, 0);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sendmsg, NULL, 0, 0);
FD_ZERO(&fd_read);
timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
while (1)
{
fd_read = socket_fd1;
int n = select(0,&fd_read,NULL,NULL,&tv);
if(n!=-1)
//printf("n: %d fd_read: %d\n", n,fd_read.fd_count);
if (n !=SOCKET_ERROR)
{
for (int i = 0; i < socket_fd1.fd_count; i++)
{
if (FD_ISSET(socket_fd1.fd_array[i], &fd_read))
{
memset(recvbuff, 0, 1024);
int nr = recv(socket_fd1.fd_array[i], recvbuff, 1024, 0);
if (nr > 0)printf("\t\tclient%d: %s\n",i+1, recvbuff);
}
}
}
}
closesocket(serverSock);
closesocket(clientSock);
WSACleanup();
system("pause");
}