網絡編程中I/O複用select的用法


注:本文對select函數、相關參數及結構體不做解釋

select使用流程圖

Created with Raphaël 2.2.0定義select相關變量定義select相關變量的相關數據初始化調用select 監視可讀事件是否有新的鏈接新的鏈接描述符放入數組和描述符集批處理可讀鏈接描述符yesno

注:沒有寫socket流程

在網絡編程中需要添加的代碼行以及意義

1.定義select相關變量

	int listenfd,connfd,i;
	/*套接字描述符/鏈接描述符(錄入環節)/變量*/	

	int nread,maxfd,maxi,sockfd;
	/*就緒數/最大鏈接描述符/用於記錄鏈接描述符數組當前最大使用的下標,減少無用的遍歷/
		鏈接描述符(區別在於此處用於I/O複用的遍歷環節)*/
	
	int client[FD_SETSIZE];
	/*存放錄入的鏈接描述符*/
	
	fd_set allest,rset;
	/*文件集合描述符:所有集合/就緒可讀集合*/

2.select相關變量的相關數據初始化

	maxfd = listenfd; 
	/*初始化最大鏈接描述符,*/
	maxi = -1;
	for (i = 0; i < FD_SETSIZE; i++) client[i] = -1;
	FD_ZERO(&allest);
	/*清空allset描述符集*/
	FD_SET(listenfd, &allest);
	 /*將監聽描述符加到allset中*/

3.select 監視可讀事件

	rset = allest;
	nread = select(maxfd+1, &rset, NULL, NULL, NULL);
		/*調用select 監視可讀事件*/

4.新的鏈接描述符放入數組和描述符集

	if (FD_ISSET(listenfd, &rset))
	/*判斷服務器端套接字中是否有變化(是否有新的鏈接)*/
	{
		cliaddr_lin = sizeof(cliaddr);
		connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_lin);
		for (i = 0; i < FD_SETSIZE; i++)
		{
			if (client[i] < 0)
			{
				client[i] = connfd;
/				/*新的鏈接描述符放入數組*/
				break;
			}
		}
		FD_SET(connfd, &allest);
		 /* 將來自客戶的連接connfd加入描述符集 */
		if (connfd > maxfd) maxfd = connfd;
		/*新的鏈接描述符*/
		if (i > maxi) maxi = i;
		if (--nread == 0) continue;
	}

5.批處理可讀鏈接描述符

for (i = 0; i <= maxi; i++)
{
	if ((sockfd = client[i]) < 0) continue;
	if (FD_ISSET(sockfd, &rset))
	{
		if ((n = read (sockfd, buf, MAXLIN)) == 0)
		{
			close(sockfd);
			FD_CLR(sockfd, &allest);
			client[i] = -1;
		}else
		{
			if (n < 0) perror("read:");
			if (n > 0) write(sockfd, buf, n);
		}
	if (--nread == 0) break;
	}
}

例程

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define LINK_NUMBER 20
#define SERV_PORT 8000
#define MAXLIN 100

int main(int argc, char const *argv[])
{
	int listenfd,connfd,i;
	int nread,maxfd,maxi,sockfd;
	int client[FD_SETSIZE];
	ssize_t n;
	fd_set allest,rset;
	socklen_t cliaddr_lin;
	struct sockaddr_in servaddr,cliaddr;
	char buf[MAXLIN],str[INET_ADDRSTRLEN];

	listenfd = socket(AF_INET,SOCK_STREAM,0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(SERV_PORT);

	bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	listen(listenfd,LINK_NUMBER);

	maxfd = listenfd;
	maxi = -1;
	for (i = 0; i < FD_SETSIZE; i++) client[i] = -1;
	FD_ZERO(&allest);
	FD_SET(listenfd, &allest);

	while(1)
	{
		rset = allest;
		nread = select(maxfd+1, &rset, NULL, NULL, NULL);
		if (nread < 0)
		{
			perror("select error:");
			break;
		}

		if (FD_ISSET(listenfd, &rset))
		{
			cliaddr_lin = sizeof(cliaddr);
			connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_lin);

			printf("received from %s at PORT %d\n",
			       inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
			       ntohs(cliaddr.sin_port));

			for (i = 0; i < FD_SETSIZE; i++)
			{
				if (client[i] < 0)
				{
					client[i] = connfd;
					break;
				}
			}
			if (i == FD_SETSIZE)
			{
				perror("too many clients");
				exit(1);
			}
			FD_SET(connfd, &allest);
			if (connfd > maxfd) maxfd = connfd;
			if (i > maxi) maxi = i;
			if (--nread == 0) continue;	
		}

		for (i = 0; i <= maxi; i++)
		{
			if ((sockfd = client[i]) < 0) continue;

			if (FD_ISSET(sockfd, &rset))
			{
				if ((n = read (sockfd, buf, MAXLIN)) == 0)
				{
					close(sockfd);
					FD_CLR(sockfd, &allest);
					client[i] = -1;
				}else
				{
					if (n < 0)
					{
						perror("read:");
					}
					if (n > 0)
					{
						write(sockfd, buf, n);
					}
	
				}
			
				if (--nread == 0) break;
			}
		}
		/*socket*/
		/*while(1)
		{
			if((n = read(connfd,buf,MAXLIN)) <= 0) 
			{
				printf("err" );
				break;
			}

			printf("received from %s at PORT %d::%s\n",
			       inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
			       ntohs(cliaddr.sin_port),buf);

			write(connfd,buf,n);
		}
		close(connfd);*/
	}

	return 0;
}

參考文獻及博客

Linux聊天室項目 – ChatRome(select實現)
Linux I/O複用之select函數詳解
unix網絡編程第二版

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章