winsock之select选择模型

        由于使用传统的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");
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值