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

  

 

posted @ 2019-02-13 19:38  gd_沐辰  阅读(494)  评论(0编辑  收藏  举报