标签:des style blog http color os 使用 io for
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie
这是一个简单的回射服务器程序。它将客户发送的数据读入缓冲区并回射其中内容
/**
 * TCP/IPv4 协议相关
 * **/
#include	"unp.h"
int
main(int argc, char **argv)
{
	int					listenfd, connfd;
	pid_t				childpid;
	socklen_t			clilen;
	struct sockaddr_in	cliaddr, servaddr;
    //1.创建套接字,捆绑服务器的众所周知端口
	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, (SA *) &servaddr, sizeof(servaddr));
	Listen(listenfd, LISTENQ);
    //2.等待完成客户连接
	for ( ; ; ) {
		clilen = sizeof(cliaddr);
		connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
        //3.并发服务器
		if ( (childpid = Fork()) == 0) {	/* 子进程 */
			Close(listenfd);	/* 关闭监听套接字*/
			str_echo(connfd);	/* 处理客户请求*/
			exit(0);
		}
		Close(connfd);			/* 父进程关闭连接套接字 */
	}
}
#include	"unp.h"
void
str_echo(int sockfd)
{
	ssize_t		n;
	char		buf[MAXLINE];
again:
	// 读入缓冲区并回射其中内容
	while ( (n = read(sockfd, buf, MAXLINE)) > 0)
		Writen(sockfd, buf, n);
	if (n < 0 && errno == EINTR)
		goto again;
	else if (n < 0)
		err_sys("str_echo: read error");
}
 /**
* TCP/IPv4 协议相关 收拾终止了的子进程
**/
#include	"unp.h"
int
main(int argc, char **argv)
{
	int					listenfd, connfd;
	pid_t				childpid;
	socklen_t			clilen;
	struct sockaddr_in	cliaddr, servaddr;
	void				sig_chld(int);
	//1.创建套接字,绑定众所周知的端口
	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, (SA *) &servaddr, sizeof(servaddr));
	Listen(listenfd, LISTENQ);
	//2.当fork 子进程时,必须捕获 SIGCHLD 信号, sig_chld 为信号处理函数
	Signal(SIGCHLD, sig_chld);	/* must call waitpid() */
	//3.等待客户连接
	for ( ; ; ) {
		clilen = sizeof(cliaddr);
		//4.当捕获信号时,必须处理被中断的系统调用
		if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
			if (errno == EINTR)
				continue;		/* back to for() */
			else
				err_sys("accept error");
		}
		//5.并发服务器
		if ( (childpid = Fork()) == 0) {	/* child process */
			Close(listenfd);	/* close listening socket */
			str_echo(connfd);	/* process the request */
			exit(0);
		}
		Close(connfd);			/* parent closes connected socket */
	}
}
#include	"unp.h"
void
sig_chld(int signo)
{
	pid_t	pid;
	int		stat;
	while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
		printf("child %d terminated\n", pid);
	return;
}
/**
* TCP 使用 select 
**/
#include	"unp.h"
int
main(int argc, char **argv)
{
	int					i, maxi, maxfd, listenfd, connfd, sockfd;
	int					nready, client[FD_SETSIZE];
	ssize_t				n;
	fd_set				rset, allset;
	char				buf[MAXLINE];
	socklen_t			clilen;
	struct sockaddr_in	cliaddr, servaddr;
	//1.创建套接字,绑定众所周知的端口
	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, (SA *) &servaddr, sizeof(servaddr));
	Listen(listenfd, LISTENQ);
	//2.初始化
	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);
/* end fig01 */
/* include fig02 */
	for ( ; ; ) {
		//3.阻塞于 select
		//select 等待某个事件发生:或是新客户连接的建立,或是数据、FIN或 RST的到达
		rset = allset;		/* structure assignment */
		nready = Select(maxfd+1, &rset, NULL, NULL, NULL);
		//4.accept 新的连接
		if (FD_ISSET(listenfd, &rset)) {	/* new client connection */
			clilen = sizeof(cliaddr);
			connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
#ifdef	NOTDEF
			printf("new client: %s, port %d\n",
					Inet_ntop(AF_INET, &cliaddr.sin_addr, 4, NULL),
					ntohs(cliaddr.sin_port));
#endif
			//用 client 数组中第一个未用项记录这个已连接描述符
			for (i = 0; i < FD_SETSIZE; i++)
				if (client[i] < 0) {
					client[i] = connfd;	/* save descriptor */
					break;
				}
			if (i == FD_SETSIZE)
				err_quit("too many clients");
			//在 allset 描述符中打开已连接套接字 connfd 的对应位
			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 */
			//nready 是 select 函数的返回值,表示跨所有描述符集的已就绪的总位数
			//就绪描述符数目减 1,若其值变为 0,就可以避免进入下一个 for 循环
			if (--nready <= 0)
				continue;				/* no more readable descriptors */
		}
		//5.检查现有连接
		//对于每个现在的客户连接,测试其描述符是否在 select 返回的描述符集中 --> ? select 有返回描述符集吗 ?
		//如果是,就从客户读入一行文本并回射给它。
		//如果关闭了连接,那么 read 将返回 0,要相应地更新数据结构
		for (i = 0; i <= maxi; i++) {	/* check all clients for data */
			if ( (sockfd = client[i]) < 0)
				continue;
			if (FD_ISSET(sockfd, &rset)) {
				if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {
						/*4connection closed by client */
					Close(sockfd);
					FD_CLR(sockfd, &allset);
					client[i] = -1;
				} else
					Writen(sockfd, buf, n);
				if (--nready <= 0)
					break;				/* no more readable descriptors */
			}
		}
	}
}
/* end fig02 *//**
 * TCP/IPv4 协议相关,使用poll,单个进程处理所有客户
 * **/
/* include fig01 */
#include	"unp.h"
#include	<limits.h>		/* for OPEN_MAX */
int
main(int argc, char **argv)
{
	int					i, maxi, listenfd, connfd, sockfd;
	int					nready;
	ssize_t				n;
	char				buf[MAXLINE];
	socklen_t			clilen;
    //1.分配 pollfd 结构数组
	struct pollfd		client[OPEN_MAX];
	struct sockaddr_in	cliaddr, servaddr;
	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, (SA *) &servaddr, sizeof(servaddr));
	Listen(listenfd, LISTENQ);
    //2.初始化
    //把 client 数组的第一项用于监听套接字,并设置 events为 POLLRDNORM
    //把其余各项的成员转为 -1
	client[0].fd = listenfd;
	client[0].events = POLLRDNORM;
	for (i = 1; i < OPEN_MAX; i++)
		client[i].fd = -1;		/* -1 indicates available entry */
	maxi = 0;					/* max index into client[] array */
/* end fig01 */
/* include fig02 */
	for ( ; ; ) {
        //3.调用 poll,检查新的连接
		nready = Poll(client, maxi+1, INFTIM);
        //检查 client[0],即监听套接字上是否有新客户连接
		if (client[0].revents & POLLRDNORM) {	/* new client connection */
			clilen = sizeof(cliaddr);
			connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
#ifdef	NOTDEF
			printf("new client: %s\n", Sock_ntop((SA *) &cliaddr, clilen));
#endif
            //保存已连接套接字
			for (i = 1; i < OPEN_MAX; i++)
				if (client[i].fd < 0) {
					client[i].fd = connfd;	/* save descriptor */
					break;
				}
			if (i == OPEN_MAX)
				err_quit("too many clients");
			client[i].events = POLLRDNORM;
			if (i > maxi)
				maxi = i;				/* max index in client[] array */
			if (--nready <= 0)
				continue;				/* no more readable descriptors */
		}
        //4.检查某个现有连接上的数据
        // i 从 1,开始,因为 client[0]是监听套接字
		for (i = 1; i <= maxi; i++) {	/* check all clients for data */
			if ( (sockfd = client[i].fd) < 0)
				continue;
			if (client[i].revents & (POLLRDNORM | POLLERR)) {
                //读取数据并回射
				if ( (n = read(sockfd, buf, MAXLINE)) < 0) {
					if (errno == ECONNRESET) {
							/*4connection reset by client */
#ifdef	NOTDEF
						printf("client[%d] aborted connection\n", i);
#endif
						Close(sockfd);
						client[i].fd = -1;
					} else
						err_sys("read error");
                //终止连接
				} else if (n == 0) {
						/*4connection closed by client */
#ifdef	NOTDEF
					printf("client[%d] closed connection\n", i);
#endif
					Close(sockfd);
					client[i].fd = -1;
				} else
					Writen(sockfd, buf, n);
				if (--nready <= 0)
					break;				/* no more readable descriptors */
			}
		}
	}
}
/* end fig02 */
/**
 * TCP 每个用户一个线程
 * **/
#include	"unpthread.h"
static void	*doit(void *);		/* each thread executes this function */
int
main(int argc, char **argv)
{
	int				listenfd, connfd;
	pthread_t		tid;
	socklen_t		addrlen, len;
	struct sockaddr	*cliaddr;
    //1.创建监听套接字
	if (argc == 2)
		listenfd = Tcp_listen(NULL, argv[1], &addrlen);
	else if (argc == 3)
		listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
	else
		err_quit("usage: tcpserv01 [ <host> ] <service or port>");
	cliaddr = Malloc(addrlen);
    //2.创建线程
	for ( ; ; ) {
		len = addrlen;
		connfd = Accept(listenfd, cliaddr, &len);
        //accept 返回之后,改为调用 pthread_create 取代调用 fork
		Pthread_create(&tid, NULL, &doit, (void *) connfd);
	}
}
//3.线程函数
static void *
doit(void *arg)
{
    //pthread_detach 使自身脱离主线程,这样主线程不用等待它
	Pthread_detach(pthread_self());
	str_echo((int) arg);	/* same function as before */
    //关闭已连接套接字
	Close((int) arg);		/* done with connected socket */
	return(NULL);
}/**
 * TCP 每个客户一个线程,可移植的参数传递
 * **/
#include	"unpthread.h"
static void	*doit(void *);		/* each thread executes this function */
int
main(int argc, char **argv)
{
	int				listenfd, *iptr;
	thread_t		tid;
	socklen_t		addrlen, len;
	struct sockaddr	*cliaddr;
    //1.创建监听套接字
	if (argc == 2)
		listenfd = Tcp_listen(NULL, argv[1], &addrlen);
	else if (argc == 3)
		listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
	else
		err_quit("usage: tcpserv01 [ <host> ] <service or port>");
	cliaddr = Malloc(addrlen);
    //2.创建线程
	for ( ; ; ) {
		len = addrlen;
		iptr = Malloc(sizeof(int));
		*iptr = Accept(listenfd, cliaddr, &len);
        Pthread_create(&tid, NULL, &doit, iptr);
	}
}
//3.线程函数
static void *
doit(void *arg)
{
	int		connfd;
	connfd = *((int *) arg);
	free(arg);
	Pthread_detach(pthread_self());
	str_echo(connfd);		/* same function as before */
	Close(connfd);			/* done with connected socket */
	return(NULL);
}UNIX网络编程卷1 回射服务器程序 TCP服务器程序设计范式 四个版本
标签:des style blog http color os 使用 io for
原文地址:http://blog.csdn.net/zhengsenlie/article/details/38826865