31网络通信之Select模型
多路复用并发模型 -- select

#include<sys/select.h>
#include<sys/time.h>
int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout)
maxfd 监控的套接字最大值 + 1
readset 集合中任意描述字准备好读,则返回
writeset 集合中任意描述字准备好写,则返回
exceptset 集合中任意描述字有异常等待处理,则返回
timeout 超时则返回(NULL 则一直等待,0 则立即返回)
返回值 =0 超时, 返回值<0 错误,返回值>0正常
多路复用并发模型 -- select
FD_ZERO(fd_set *fdset)
清空描述符集合
FD_SET(int fd, fd_set *fdset)
增加fd 到集合中, 事实上就是把某个bit位置位
FD_CLR(int fd, fd_set *fdset)
从集合中清除fd, 事实上就是把某个bit位清除置位
int FD_ISSET(int fd, fd_set *fdset)
描述字是否准备好
多路复用并发模型 -- select
优点:
通过IO复用,支持交互式输入
通过IO复用,可以同时监听 UDP 和 TCP
相比比多线程, 系统开销大大减少,
缺点:
每次调用 select 都需要把fd集合从用户态拷贝到内核态,fd很多时开销很大
调用 select 需要内核遍历 fd, fd 很多时开销很大
select 支持文件描述符监视有数量限制,默认 1024
服务器端代码:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<sys/time.h>
#define SRV_PORT 0xabcd
#define MAX_CONN 3
void RcvMsg(int fds[], int index, int *pnConn)
{
	char szBuf[1024] = {0};
	int iRet;
	iRet = read(fds[index], szBuf, 1024);
	if (iRet < 0)
	{
		perror("Fail to read!");
	}
	else if (iRet == 0)
	{
		//disconnect.  remove fd from fds
		printf("[%d]Disconnect...\n", fds[index]);
		close(fds[index]);
		int j;
		for (j=index; j < *pnConn - 1; j++)
		{
			fds[j] = fds[j+1];
		}
		(*pnConn)--;
	}
	else
	{
		printf("[%d]Recv:%s\n", fds[index], szBuf);
	}
	return;
}
void TcpServer()
{
	int fd;
	int iRet;
	struct sockaddr_in addr;
	socklen_t addrlen = sizeof(addr);
	fd = socket(PF_INET, SOCK_STREAM, 0);
	if (fd < 0)
	{
		perror("Fail to socket!");
		return;
	}
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(SRV_PORT);
	iRet = bind(fd, (struct sockaddr*)&addr, addrlen);
	if (iRet)
	{
		perror("Fail to bind!");
		close(fd);
		return;
	}
	iRet = listen(fd, 100);
	if (iRet)
	{
		perror("Fail to listen!");
		close(fd);
		return;
	}
	fd_set fdset;
	int maxfd = fd;
	int fds[MAX_CONN]; //client fd;
	int nConn = 0;         //client fd num.
	int i;
	int clientfd;
	struct sockaddr_in srcaddr;
	char szMsg[100];
	while(1)
	{
		FD_ZERO(&fdset);
		FD_SET(fd, &fdset);
		
		for (i=0; i<nConn; i++)
		{
			FD_SET(fds[i], &fdset);//add client fd to fdset for monitor
		}
		fprintf(stderr, "Send:");
		scanf("%s", szMsg);
		for (i=0; i<nConn; i++)
		{
			write(fds[i], szMsg, strlen(szMsg));
		}
		iRet = select(maxfd+1, &fdset, NULL, NULL, NULL);	
		if (iRet < 0)
		{
			perror("Fail to select!");
			break;
		}
		else if (iRet == 0)
		{
			//timeout
		}
		else
		{
			if (FD_ISSET(fd, &fdset))
			{
				clientfd = accept(fd, (struct sockaddr*)&srcaddr, &addrlen);
				if (clientfd < 0)
				{
					perror("Fail to accept!");
					break;
				}
				if (nConn == MAX_CONN)
				{
					char szTip[] = "Over connect, please wait...";
					write(clientfd, szTip, sizeof(szTip));
					printf("Connect over!\n");
					close(clientfd);
				}
				else
				{
					char szTip[] = "Welcome!";
					write(clientfd, szTip, sizeof(szTip));
					printf("[%d]New connection form %s:%d\n", clientfd, 
						inet_ntoa(srcaddr.sin_addr), ntohs(srcaddr.sin_port));
					
					fds[nConn] = clientfd;
					nConn++;
				
					if (clientfd > maxfd)
					{
						maxfd = clientfd;
					}
				}
			}
			
			for (i=0; i<nConn; i++)
			{
				if (FD_ISSET(fds[i], &fdset))
				{
					RcvMsg(fds, i, &nConn);			
				}
			}
				
				
		}
	}
	
	close(fd);
}
int main()
{
	TcpServer();
	
	return 0;
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号