1. 程式人生 > >linux網路程式設計十八:統一事件源

linux網路程式設計十八:統一事件源

訊號是一種非同步事件:訊號處理函式和程式的主迴圈是兩條不同的執行路線。

//統一事件源
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <signal.h>

#define MAX_EVENT_NUMBER 1024

static int pipefd[2];


int setnonblocking(int fd);			//設定非阻塞
void addfd(int epollfd, int fd);		//新增描述符的事件
void sig_handler(int sig);			//訊號處理函式
void addsig(int sig);					//新增訊號處理


int main(int argc, char **argv)
{
	if (argc != 2) {
		fprintf(stderr, "Usage: %s port\n", basename(argv[0]));
		return 1;
	}
	
	int port = atoi(argv[1]);
	int ret = 0;
	int error;
	
	struct sockaddr_in address;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	address.sin_addr.s_addr = htonl(INADDR_ANY);
	
	int sockfd = socket(PF_INET, SOCK_STREAM, 0);	
	if (sockfd == -1)
		return 1;
		
	printf("server start...\n");
	
	//設定地址可重用
	int reuse = 1;
	ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
	if (ret == -1) {
		error = errno;
		while ((close(sockfd) == -1) && (errno == EINTR));
		errno = error;
		return 1;
	}
	
	printf("server reuseaddr success\n");
	
	if ((bind(sockfd, (struct sockaddr*)&address, sizeof(address)) == -1) ||
		(listen(sockfd, 5) == -1)) {
		error = errno;
		while ((close(sockfd) == -1) && (errno == EINTR));
		errno = error;
		return 1;
	}
	
	printf("server bind and listen success\n");
	
	epoll_event events[MAX_EVENT_NUMBER];
	
	int epollfd = epoll_create(5);
	if (epollfd == -1) {
		error = errno;
		while ((close(sockfd) == -1) && (errno == EINTR));
		errno = error;
		return 1;
	}
	
	addfd(epollfd, sockfd);
	
	//使用socketpair建立管道,註冊pipefd[0]上的可讀事件
	ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
	if (ret == -1) {
		error = errno;
		while ((close(sockfd) == -1) && (errno == EINTR));
		errno = error;
		return 1;
	}
	
	setnonblocking(pipefd[1]);
	addfd(epollfd, pipefd[0]);
	
	//設定訊號處理
	addsig(SIGHUP);
	addsig(SIGCHLD);
	addsig(SIGTERM);
	addsig(SIGINT);
	
	bool stop_server = false;
	
	while (!stop_server) {
		int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
		if (number < 0 && errno != EINTR) {
			fprintf(stderr, "epoll failed\n");
			break;
		}
		
		for (int i = 0; i < number; i++) {
			int listenfd = events[i].data.fd;
			if (listenfd == sockfd) {			//處理新連線
				struct sockaddr_in client_address;
				socklen_t client_addrlength = sizeof(client_address);
				
				int connfd = -1;
				while ( ((connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength)) == -1) &&
						(connfd == EINTR) );
				
				addfd(epollfd, connfd);
			}
			else if (listenfd == pipefd[0] && events[i].events & EPOLLIN) {	//處理訊號
				char signals[1024];
				ret = recv(pipefd[0], signals, sizeof(signals), 0);
				if (ret == -1)
					continue;
				else if (ret == 0)
					continue;
				else {
					//每個訊號值佔1位元組,所以按位元組來逐個接收訊號
					for (int i = 0; i < ret; i++) {
						switch(signals[i]) {
							case SIGCHLD:
							{
								fprintf(stderr, "recv SIGCHLD\n");
								continue;
								break;
							}
							case SIGHUP:
							{
								fprintf(stderr, "recv SIGHUP\n");
								continue;
								break;
							}
							case SIGTERM:
							{
								fprintf(stderr, "recv SIGTERM, close server\n");
								stop_server = true;
								break;
							}							
							case SIGINT:
							{
								fprintf(stderr, "recv SIGINT, close server\n");
								stop_server = true;
								break;
							}
							default:
								break;
							
						}
					}
				}
			}
			else {
				
			}
		}
				
	}
	
	
	printf("close fds\n");
	close(sockfd);
	close(pipefd[1]);
	close(pipefd[0]);
	
	
	return 0;
}

int setnonblocking(int fd)
{
	int old_option = fcntl(fd, F_GETFL);
	int new_option = old_option | O_NONBLOCK;
	fcntl(fd, F_SETFL, new_option);
	return old_option;
}

void addfd(int epollfd, int fd)
{
	epoll_event event;
	event.data.fd = fd;
	event.events = EPOLLIN | EPOLLET;
	epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
	setnonblocking(fd);
}

void sig_handler(int sig)
{
	int save_errno = errno;
	int msg = sig;
	send(pipefd[1], (char*)&msg, 1, 0);		//將訊號寫入管道,以通知主迴圈
	errno = save_errno;
}

void addsig(int sig)
{
	struct sigaction sa;
	memset(&sa, '\0', sizeof(sa));
	sa.sa_handler = sig_handler;
	sa.sa_flags |= SA_RESTART;
	
	sigfillset(&sa.sa_mask);
	assert(sigaction(sig, &sa, NULL) != -1);
}






參考:《linux高效能伺服器程式設計》