(UNP点滴记录) I/O复用select

2013-02-03 wcdj


1 UNIX下可用的五种I/O模型

(1) 阻塞式I/O

(2) 非阻塞式/O

(3) I/O复用(select和poll)

(4) 信号驱动式I/O(SIGIO)

(5) 异步I/O(POSIX的aio_系列函数)



2 I/O复用select

client代码

// client_v2.cpp  

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


#define SERV_PORT 30008  
#define SA struct sockaddr  
#define MAXLINE 4096  
#define max(a, b) ( (a) > (b) ? (a) : (b) )  

static int read_cnt;  
static char *read_ptr;  
static char read_buf[MAXLINE];  

static ssize_t my_read(int fd, char *ptr)  
{  
	if (read_cnt <= 0)  
	{  
again:  
		if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0)  
		{  
			if (errno == EINTR)  
				goto again;  
			return -1;  
		}  
		else if (read_cnt == 0)  
			return 0;  

		read_ptr = read_buf;  
	}  

	--read_cnt;  
	*ptr = *read_ptr++;  
	return 1;  
}  

ssize_t readlinebuf(void **vptrptr)  
{  
	if (read_cnt)  
		*vptrptr = read_ptr;  

	return read_cnt;  
}  

ssize_t readline(int fd, void *vptr, size_t maxlen)  
{  
	ssize_t n, rc;  
	char c, *ptr;  

	ptr = (char *)vptr;  
	for (n = 1; n < maxlen; ++n)  
	{  
		if ( (rc = my_read(fd, &c)) == 1)  
		{  
			*ptr++ = c;  
			if (c == '\n')  
				break;// newline is stored, like fgets()  
		}  
		else if (rc == 0)  
		{  
			*ptr = 0;  
			return (n-1);// EOF, n - 1 bytes were read  
		}  
		else  
			return -1;// error, errno set by read()  
	}  

	*ptr = 0;  
	return (n);  
}  

ssize_t writen(int fd, const void *vptr, size_t n)  
{  
	size_t nleft;  
	ssize_t nwritten;  
	const char *ptr;  

	ptr = (const char*)vptr;  
	nleft = n;  
	while (nleft > n)  
	{  
		if ( (nwritten = write(fd, ptr, nleft)) <= 0 )  
		{  
			if (nwritten < 0 && errno == EINTR)  
				nwritten = 0;// and call write() again  
			else  
				return -1;// error  
		}  
		nleft -= nwritten;  
		ptr += nwritten;  
	}  
	return n;  
}  

void str_cli(FILE *fp, int sockfd)  
{  
	int maxfdp1;  
	int stdineof = 0;  

	fd_set rset;  
	char buf[MAXLINE];  
	int n;  

	FD_ZERO(&rset);  

	for ( ; ; )  
	{  
		if (stdineof == 0)  
		{  
			FD_SET(fileno(fp), &rset);  
		}  
		FD_SET(sockfd, &rset);  

		maxfdp1 = max(fileno(fp), sockfd) + 1;  

		select(maxfdp1, &rset, NULL, NULL, NULL);  

		// socket is readable  
		if (FD_ISSET(sockfd, &rset))  
		{  
			printf("socket is readable\n");  

			if ( (n = read(sockfd, buf, MAXLINE)) == 0 )  
			{  
				if (stdineof == 1)  
				{  
					printf("normal tremination\n");  

					return;// normal termination  
				}  
				else  
				{  
					printf("str_cli: server terminated prematurely\n");  
					exit(1);  
				}  
			}  
			write(fileno(stdout), buf, n);  
		}  

		// input is readable  
		if (FD_ISSET(fileno(fp), &rset))  
		{  
			printf("input is readable\n");  

			if ( (n = read(fileno(fp), buf, MAXLINE)) == 0 )  
			{  
				printf("input read 0\n");  

				stdineof = 1;  
				shutdown(sockfd, SHUT_WR);// send FIN  
				FD_CLR(fileno(fp), &rset);// turn off the bit for fileno(fp) in rset  
				continue;  
			}  
			buf[n] = '\0';  
			printf("input read:%s", buf);  
			//int iret = writen(sockfd, buf, n);  
			int iret = send(sockfd, buf, n, 0);  
			printf("socket send:%d\n", iret);  
		}  

	}  
}  

int main(int argc, char **argv)  
{  
	int sockfd;  
	struct sockaddr_in servaddr;  

	if (argc != 2)  
	{  
		printf("usage: tcpcli <IPaddress>\n");  
		exit(1);  
	}  

	sockfd = socket(AF_INET, SOCK_STREAM, 0);  
	memset(&servaddr, 0x0, sizeof(servaddr));  
	servaddr.sin_family = AF_INET;  
	servaddr.sin_port = htons(SERV_PORT);  

	if (-1 == connect(sockfd, (SA *)&servaddr, sizeof(servaddr)))
	{
		printf("connect err:%s\n", strerror(errno));  
		exit(1); 
	}

	str_cli(stdin, sockfd);  

	exit(0);  
}  



server代码

// server_v2.cpp
// 使用单进程和select的TCP服务器

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

#define SERV_PORT 30008
#define SA struct sockaddr
#define MAXLINE 4096
#define max(a, b) ( (a) > (b) ? (a) : (b) )
#define LISTENQ 1024

ssize_t writen(int fd, const void *vptr, size_t n)
{
	size_t nleft;
	ssize_t nwritten;
	const char *ptr;

	ptr = (const char*)vptr;
	nleft = n;
	while (nleft > n)
	{
		if ( (nwritten = write(fd, ptr, nleft)) <= 0 )
		{
			if (nwritten < 0 && errno == EINTR)
				nwritten = 0;// and call write() again
			else
				return -1;// error
		}
		nleft -= nwritten;
		ptr += nwritten;
	}
	return n;
}

int 
main(int argc, char **argv)
{
	int i;
	int maxi;
	int maxfd;
	int listenfd;
	int connfd;
	int sockfd;

	int nready, client[FD_SETSIZE];
	ssize_t n;
	fd_set rset, allset;
	char buf[MAXLINE];
	socklen_t clilen;
	struct sockaddr_in cliaddr, servaddr;

	listenfd = socket(AF_INET, SOCK_STREAM, 0);

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

	bind(listenfd, (SA *)&servaddr, sizeof(servaddr));

	listen(listenfd, LISTENQ);

	maxfd = listenfd;  // initialize
	maxi = -1;          // index into client[] array
	for (i = 0; i < FD_SETSIZE; ++i)
	{
		client[i] = -1;// -1 indicates available entry
	}
	FD_ZERO(&allset);
	FD_SET(listenfd, &allset);

	for ( ; ; )
	{
		rset = allset;// structure assignment
		nready = select(maxfd+1, &rset, NULL, NULL, NULL);
		printf("select ready:%d\n", nready);

		// new client connection
		if (FD_ISSET(listenfd, &rset))
		{
			printf("new client connection\n");

			clilen = sizeof(cliaddr);
			connfd = accept(listenfd, (SA *)&cliaddr, &clilen);

			for(i = 0; i < FD_SETSIZE; ++i)
			{
				if (client[i] < 0)
				{
					client[i] = connfd;// save descriptor
					break;
				}
			}
			if (i == FD_SETSIZE)
			{
				printf("too many clients\n");
				exit(1);
			}

			FD_SET(connfd, &allset);// add new descriptor to set
			if (connfd > maxfd)
			{
				maxfd = connfd;// for select
			}
			if (i > maxi)
			{
				maxi = i;// max index in client[] array
			}
			if (--nready <= 0)
			{
				printf("listen, no more readable descriptors\n");
				continue;// no more readable descriptors
			}
		}

		// check all clients for data
		printf("check all clients for data\n");
		for (i = 0; i<= maxi; ++i)
		{
			if ( (sockfd = client[i]) < 0 )
			{
				continue;
			}
			if (FD_ISSET(sockfd, &rset))
			{
				if ( (n = read(sockfd, buf, MAXLINE)) == 0 )
				{
					// connection closed by client
					printf("connection closed by client\n");

					close(sockfd);
					FD_CLR(sockfd, &allset);
					client[i] = -1;
				}
				else
				{
					buf[n] = '\0';
					printf("get info:%s", buf);
					writen(sockfd, buf, n);
				}

				if (--nready <= 0)
					break;// no more readable descriptors
			}
		}
	}

}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值