Linux編程I/O複用select用法備忘


它允許進程指示內核阻塞在等待多個事件中的任一個發生,並僅在一個或多個事件發生或經過某指定的時間後才喚醒進程。
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
struct  timeval{
 long tv_sec; / * 秒數 * /
long tv_usec; / * 微秒(百萬分之一秒) * /
 };

int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, timeval *tv);


參數從前至後:最大文件描述符+1, 讀文件描述符集合,寫文件描述符集合,異常文件描述符集合,等待超時時間


fd_set set;   

FD_ZERO(&set); /*將set清零使集合中不含任何fd*/ 

FD_SET(fd, &set); /*將fd加入set集合*/   

FD_CLR(fd, &set); /*將fd從set集合中清除*/

FD_ISSET(fd, &set); /*測試fd是否在set集合中*/


當使用select時,1個最常見的編程錯誤是:忘了對最大描述字加1
忘了描述字集是值-結果參數,select返回時會將那些沒準備好的bit置爲0,所以如果要再次select時,一定要重新用FD_SET設置你感興趣的描述字的對應bit
忘記重新填寫timeout的值


server.c代碼如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/select.h>

ssize_t readn(int fd, void *buf, size_t count);
ssize_t writen(int fd, const void *buf, size_t count);
ssize_t readline(int fd, void *buf, int size);

int init_fdset(fd_set *set, int listen_socketfd, 
	int connect_socketfd[],int size, int *maxfd);

int deal_listen_socket(int listen_socketfd,
	int connect_socketfd[], int size);
int deal_connect_socket(int connect_socketfd[], int index);

int open_listen_socket(const char *ipstr, uint16_t port);


int main()
{
	int                 ret = -1;
	char                recv_buf[100];
	
	int                 listen_socketfd = -1;
	int                 connect_socketfd[100];
	int                 temp_fd = -1;
	int                 maxfd = -1;
	
	int                 *pcnn_sd = NULL;
	fd_set              rset;
	int                 i = 0;

	for (i=0; 
		i<sizeof(connect_socketfd)/sizeof(int); 
		i++)
	{
		connect_socketfd[i] = -1;
	}
	
	
	listen_socketfd = open_listen_socket("127.0.0.1", 4321);
	if (listen_socketfd < 0)
	{
		printf("open_listen_socket err\n");
		return -1;
	}
	
	while (1)
	{
		init_fdset(&rset, listen_socketfd, connect_socketfd, 
			sizeof(connect_socketfd)/sizeof(int), &maxfd);

		ret = select(maxfd+1, &rset, NULL, NULL, NULL);
		if (ret < 0)
		{
			perror("select err");
			return -1;
		}

		if (FD_ISSET(listen_socketfd, &rset))
		{
			deal_listen_socket(listen_socketfd, connect_socketfd, 
				sizeof(connect_socketfd)/sizeof(int));
		}

		for (i=0; 
			i<sizeof(connect_socketfd)/sizeof(int); 
			i++)
		{
			if (connect_socketfd[i] > 0)
			{
				if (FD_ISSET(connect_socketfd[i], &rset))
				{
					deal_connect_socket(connect_socketfd, i);
				}
			}
		}
	}

	close(listen_socketfd);
	return 0;
}

int init_fdset(fd_set *set, int listen_socketfd, int connect_socketfd[],
	int size, int *maxfd)
{
	int     i = 0;
	
	FD_ZERO(set);
	FD_SET(listen_socketfd, set);

	*maxfd = listen_socketfd;
	for (i=0; 
		i<size; 
		i++)
	{
		if (connect_socketfd[i] >= 0)
		{
			FD_SET(connect_socketfd[i], set);
			if (connect_socketfd[i] > *maxfd)
			{
				*maxfd = connect_socketfd[i];
			}
		}
	}

	return 0;
}

int deal_listen_socket(int listen_socketfd,
	int connect_socketfd[], int size)
{
	int                 temp_fd = -1;
	int                 i = -1;
	struct sockaddr_in  client_addr;
	socklen_t           client_addr_len = 0;
	
	client_addr_len = sizeof(struct sockaddr_in);
	temp_fd = accept(listen_socketfd, 
		(struct sockaddr *)&client_addr, 
		&client_addr_len);
	if (temp_fd < 0)
	{
		perror("accept err");
		return -1;
	}

	for (i=0; 
		i<size; 
		i++)
	{
		if (connect_socketfd[i] < 0)
		{
			connect_socketfd[i] = temp_fd;
			break;
		}
	}

	return 0;
}

int deal_connect_socket(int connect_socketfd[], int index)
{
	char       recv_buf[100];
	int        ret = -1;
	
	memset(recv_buf, 0, sizeof(recv_buf));
	ret = readline(connect_socketfd[index],
		recv_buf, sizeof(recv_buf));
	if (ret < 0)
	{
		perror("readline err");
	}
	else if (ret == 0)
	{
		printf("client close\n");
		close(connect_socketfd[index]);
		connect_socketfd[index] = -1;
	}
	else
	{
		ret = writen(connect_socketfd[index], 
			recv_buf, ret);
		if (ret < 0)
		{
			perror("writen err");
			close(connect_socketfd[index]);
			connect_socketfd[index] = -1;
		}
	}

	return 0;
}

int open_listen_socket(const char *ipstr, uint16_t port)
{
	int                 ret = -1;
	int                 listen_sd = -1;
	struct sockaddr_in  server_addr;
	

	listen_sd = socket(AF_INET, SOCK_STREAM, 0);
	if (listen_sd < 0)
	{
		perror("socket err");
		return -1;
	}

	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	ret = inet_pton(AF_INET, ipstr, 
		&(server_addr.sin_addr.s_addr));  
	if (ret < 0)
	{
		perror("inet_pton err");
		close(listen_sd);
		return -1;
	}

	ret = bind(listen_sd, (struct sockaddr *)&server_addr, 
		sizeof(struct sockaddr_in));
	if (ret < 0)
	{
		perror("bind err");
		close(listen_sd);
		return -1;
	}
	
	ret = listen(listen_sd, 6);
	if (ret < 0)
	{
		perror("listen err");
		close(listen_sd);
		return -1;
	}

	return listen_sd;
}

ssize_t readn(int fd, void *buf, size_t count)
{
	char          *strtmp = NULL;
	ssize_t       reval = 0;
	ssize_t       realcount = 0;
  
	strtmp = (char *)buf;
  
	while (count > 0)
	{
		reval = read(fd, strtmp, count);
		if (reval < 0)
		{
			if (errno == EINTR)
			{
				continue;
			}
			else 
			{
				return -1;
			}
		}
		else if (reval > 0)
		{
			count -= reval;
			strtmp += reval;
			realcount += reval;
			continue;
		}
		else 
		{
			break;
		}
	}
	return realcount;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
	char           *strtmp = NULL;
	ssize_t        reval = 0;
	ssize_t        realcount = 0;

	strtmp = (char *)buf;

	while (count > 0)
	{
		reval = write(fd, strtmp, count);
		if (reval < 0)
		{
			if (errno == EINTR)
			{
				continue;
			}
			else 
			{
				return -1;
			}
		}
		else if (reval > 0)
		{
			count -= reval;
			strtmp += reval;
			realcount += reval;
			continue;
		}
		else 
		{
			break;
		}
	}
	return realcount;
}

ssize_t readline(int fd, void *buf, int size)
{
	char           *strtmp = NULL;
	ssize_t        reval;
	ssize_t        realcount=0;
	
	strtmp = (char *)buf;

	while(size>1)
	{
		reval = read(fd, strtmp, 1);
		if (reval < 0)
		{
			if (errno == EINTR)
			{
				continue;
			}
			else 
			{
				return -1;
			}
		}
		else if (reval == 0)
		{
			break;
		}
		else
		{
			realcount++;
			size--;
			if (*strtmp++ == '\n')
			{
				break;
			}
		}
	}
	*strtmp='\0';
	return realcount;
}

client.c代碼如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/select.h>


ssize_t readn(int fd, void *buf, size_t count);
ssize_t writen(int fd, const void *buf, size_t count);
ssize_t readline(int fd, void *buf, int size);

void read_stdin(int socketfd);

void read_socket(int socketfd);

int open_socket(const char *ipstr, uint16_t port);

int main()
{
	int                 socketfd = -1;
	int                 *psocket = NULL;
	int                 ret = -1;
	fd_set              rset;

	socketfd = open_socket("127.0.0.1", 4321);
	if (socketfd < 0)
	{
		return -1;
	}

	printf("sizeof(fd_set)*8 = %d\n", sizeof(fd_set)*8);

	while (1)
	{
		FD_ZERO(&rset);
		FD_SET(0, &rset);
		FD_SET(socketfd, &rset);

		ret = select(socketfd+1, &rset, NULL, NULL, NULL);
		if (ret < 0)
		{
			perror("select err");
			return -1;
		}

		if (FD_ISSET(0, &rset))
		{
			read_stdin(socketfd);
		}

		if (FD_ISSET(socketfd, &rset))
		{
			read_socket(socketfd);
		}
	}

	return 0;
}


void read_stdin(int socketfd)
{
	int     ret = -1;
	char    send_buf[100];
	
	memset(send_buf, 0, sizeof(send_buf));
	ret = readline(0, send_buf, sizeof(send_buf));
	if (ret < 0)
	{
		perror("readline 0 err");
		return;
	}
	else if (ret == 0)
	{
		printf("readline ret = 0\n");
		shutdown(socketfd, SHUT_WR);
		return;
	}
	else
	{
		ret = writen(socketfd, send_buf, strlen(send_buf));
		if (ret < 0)
		{
			perror("writen err");
			return;
		}
	}
}

void read_socket(int socketfd)
{
	char   recv_buf[100];
	int    ret = -1;
	
	memset(recv_buf, 0, sizeof(recv_buf));
	ret = readline(socketfd, recv_buf, sizeof(recv_buf));
	if (ret < 0)
	{
		perror("read sd err");
		return;
	}
	else if (ret == 0)
	{
		printf("sever close\n");
		close(socketfd);
		exit(0);
	}
	else
	{
		printf("recv_buf = %s\n", recv_buf);
	}
}


int open_socket(const char *ipstr, uint16_t port)
{
	int                 ret = -1;
	int                 socketfd = -1;
	struct sockaddr_in  server_addr;
	
	
	socketfd = socket(AF_INET, SOCK_STREAM, 0);
	if (socketfd < 0)
	{
		perror("socket err");
		return -1;
	}

	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	ret = inet_pton(AF_INET, ipstr, 
		&(server_addr.sin_addr.s_addr));  
	if (ret < 0)
	{
		perror("inet_pton err");
		close(socketfd);
		return -1;
	}
	
	ret = connect(socketfd, 
		(struct sockaddr *)&server_addr, 
		sizeof(struct sockaddr_in));
	if (ret < 0)
	{
		perror("connect err");
		close(socketfd);
		return -1;
	}

	return socketfd;
}

ssize_t readn(int fd, void *buf, size_t count)
{
	char          *strtmp = NULL;
	ssize_t       reval = 0;
	ssize_t       realcount = 0;
	
	strtmp = (char *)buf;
	while (count > 0)
	{
		reval = read(fd, strtmp, count);
		if (reval < 0)
		{
			if (errno == EINTR)
			{
				continue;
			}
			else 
			{
				return -1;
			}
		}
		else if (reval > 0)
		{
			count -= reval;
			strtmp += reval;
			realcount += reval;
			continue;
		}
		else 
		{
			break;
		}
	}
	return realcount;
}

ssize_t writen(int fd, const void *buf, size_t count)
{
	char           *strtmp = NULL;
	ssize_t        reval = 0;
	ssize_t        realcount = 0;

	strtmp = (char *)buf;

	while (count > 0)
	{
		reval = write(fd, strtmp, count);
		if (reval < 0)
		{
			if (errno == EINTR)
			{
				continue;
			}
			else 
			{
				return -1;
			}
		}
		else if (reval > 0)
		{
			count -= reval;
			strtmp += reval;
			realcount += reval;
			continue;
		}
		else 
		{
			break;
		}
	}
	return realcount;
}

ssize_t readline(int fd, void *buf, int size)
{
	char           *strtmp = NULL;
	ssize_t        reval;
	ssize_t        realcount=0;
	
	strtmp = (char *)buf;

	while(size>1)
	{
		reval = read(fd, strtmp, 1);
		if (reval < 0)
		{
			if (errno == EINTR)
			{
				continue;
			}
			else 
			{
				return -1;
			}
		}
		else if (reval == 0)
		{
			break;
		}
		else
		{
			realcount++;
			size--;
			if (*strtmp == '\n' || *strtmp == '\r')
			{
				break;
			}
			strtmp++;
		}
	}
	strtmp++; //žÃÐÐŽø'\n' or '\r'
	*strtmp='\0';
	return realcount;
}



發佈了24 篇原創文章 · 獲贊 53 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章