I/O多路複用 select模型【續集】

上一篇文章介紹了基本的select模型,使用select模型編寫了一個功能超級簡單的echo服務器,可以同時監聽一個套接口(用戶接受客戶端連接)和標準輸入。對於每一個客戶端連接都是輸出客戶端的內容後,立馬終止與客戶端的連接,這一片文章中,同時監聽標準輸入,監聽的套接口與已連接的客戶端。

srv.c

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


int conn[FD_SETSIZE] = {0, 1, 2};
int connNum = 3;

int getPortFromParam(int argc, char *argv[]);
int openListenfd(int port);
int init_fdset(fd_set *fdset);

void echo_cmd();
void process_new_connection(int listenfd);
void process_cli(fd_set *fdset);

int main(int argc, char *argv[])
{
	int port = getPortFromParam(argc, argv);
	int listenfd = openListenfd(port);
	conn[connNum++] = listenfd;
	fd_set fdset;
	while(1) {
		int maxfd = init_fdset(&fdset);
		int num = select(maxfd + 1, &fdset, NULL, NULL, NULL);
		if(num == -1) { //error
			perror("select error!");
			exit(1);
		} else if( num == 0) {
			continue; //time out
		} else {
			if(FD_ISSET(0, &fdset)) {
				echo_cmd(); //stdin
			}
			if(FD_ISSET(listenfd, &fdset) ) {
				process_new_connection(listenfd);
			}
			process_cli(&fdset);
			
		}
	}
	return 0;
}

int getPortFromParam(int argc, char *argv[])
{
	if(argc != 2) {
		printf("%s <port>\n", argv[0]);
		exit(1);
	}
	int port = atoi(argv[1]);
}
int openListenfd(int port)
{
	int listenfd = socket(AF_INET, SOCK_STREAM, 0);
	if(listenfd == -1) {
		perror("create socket error");
		exit(1);
	}

	struct sockaddr_in socksrv;
	bzero(&socksrv, sizeof(socksrv));
	socksrv.sin_family = AF_INET;
	socksrv.sin_port = htons(port);
	socksrv.sin_addr.s_addr = htonl(INADDR_ANY);

	int result = bind(listenfd, (struct sockaddr*)&socksrv, sizeof(socksrv));
	if(result == -1) {
		perror("bind error");
		exit(1);
	}
	result = listen(listenfd, 20);
	if(result == -1) {
		perror("listen error");
		exit(1);
	}
	return listenfd;
}
int init_fdset(fd_set *fdset)
{
	FD_ZERO(fdset);
	int i, max = 0;
	for(i = 0; i < connNum; i++) {
		FD_SET(conn[i], fdset);
		if(conn[i] > max)
			max = conn[i];
	}
	return max;
}
void echo_cmd()
{
	char buf[100] = {0};
	int nread = read(STDIN_FILENO, buf, sizeof(buf));
	if(nread == 0) {
		printf("%s\n", "exit");
		int i = 3;
		for(; i < connNum; i++)
			close(conn[i]);
		exit(1);
	} else if(nread > 0) {
		printf("%s", buf);
	}
}
void process_new_connection(int listenfd)
{
	int fd = accept(listenfd, NULL, NULL);
	if( fd == -1 ) {
		perror("accept error!");
		exit(1);
	} else {
		conn[connNum++] = fd;
		printf("new connection:%d\n", fd);
	}
}

void process_cli(fd_set *fdset)
{
	int i;
	for(i = 4; i<connNum; i++) {
		if(FD_ISSET(conn[i], fdset)) {	
			char buf[100] = {0};
			int nread = read(conn[i], buf ,sizeof(buf));
			if(nread == 0) { //close
				close(conn[i]);
				printf("%d close!\n", conn[i]);
				if(i != connNum - 1) {
					conn[i] = conn[connNum - 1];		
				}		
				--i;
				--connNum;
			} else if(nread > 0){
				buf[strlen(buf)-1] = 0;
				printf("%d %s\n", conn[i], buf);
			}
		}
	}
}

cli.c

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

int main(int argc, char *argv[])
{
	if(argc != 2) {
		printf("%s <port>\n", argv[0]);
		exit(1);
	}
	int port = atoi(argv[1]);
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	struct sockaddr_in socksrv;
	bzero(&socksrv, sizeof(socksrv));
	socksrv.sin_family = AF_INET;
	socksrv.sin_addr.s_addr = htonl(INADDR_ANY);
	socksrv.sin_port = htons(port);

	int result = connect(fd, (struct sockaddr*)&socksrv, sizeof(socksrv));

	while(1) {
		char buf[100]  = {0};
		int nread = read(0, buf, sizeof(buf));
		if(nread == 0) {
			close(fd);
			break;
		} else if(nread > 0) {
			write(fd, buf, strlen(buf));
		}
	}
	return 0;
}


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