Linux socket多进程服务器框架三
Posted 庖丁解牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux socket多进程服务器框架三相关的知识,希望对你有一定的参考价值。
在使用select管理服务器连接的时候: 注意1:select是可中断睡眠函数,需要屏蔽信号 注意2:必须获取select的返回值nread,每次处理完一个事件,nread需要-1 注意3:如果客户端的连接超过连接池的大小,需要关闭客户端连接 注意4:获取最大套接字的方法是每次有客户端连接过来时,在和maxfd比较,这样就不用每次select之前都遍历池,查找最大值
服务器
//serhelp.h #ifndef _vxser #define _vxser #ifdef __cplusplus extern "C" { #endif /** * sersocket_init - socket初始化 * @listenfd:文件描述符 * 成功返回0,失败返回错误码 * */ int sersocket_init(int *listenfd); /** * listen_socket - 绑定端口号,监听套接字 * @listenfd:文件描述符 * @port:绑定的端口号 * 成功返回0,失败返回错误码 * */ int listen_socket(int listenfd, int port); /** * run_server - 运行服务器 * @listenfd:文件描述符 * */ void run_server(int listenfd); #ifdef __cplusplus extern "C" } #endif #endif
//serhelp.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/wait.h> #include <signal.h> #include "commsocket.h" /** * sersocket_init - socket初始化 * @listenfd:文件描述符 * 成功返回0,失败返回错误码 * */ int sersocket_init(int *listenfd) { int ret = 0; if (listenfd == NULL) { ret = Sck_ParamErr; printf("sersocket_init() params not correct !\n"); return ret; } //初始化socket环境 int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd == -1) { ret = Sck_BaseErr; perror("socket() err"); return ret; } *listenfd = fd; return ret; } /** * listen_socket - 绑定端口号,监听套接字 * @listenfd:文件描述符 * @port:绑定的端口号 * 成功返回0,失败返回错误码 * */ int listen_socket(int listenfd, int port) { int ret = 0; if (listenfd < 0 || port < 0 || port > 65535) { ret = Sck_ParamErr; printf("listen_socket() params not correct !\n"); return ret; } //reuse addr int optval = 1; ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); if (ret == -1) { ret = Sck_BaseErr; perror("setsockopt() err"); return ret; } //bind struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr("127.0.0.1"); ret = bind(listenfd, (struct sockaddr *) &addr, sizeof(addr)); if (ret == -1) { ret = Sck_BaseErr; perror("bind() err"); return ret; } //listen ret = listen(listenfd, SOMAXCONN); if (ret == -1) { ret = Sck_BaseErr; perror("listen() err"); return ret; } return ret; } /** * handler - 信号捕捉函数 * @sign:信号码 * */ void handler(int sign) { if (sign == SIGPIPE) { printf("server accept SIGPIPE !\n"); } } /** * run_server - 运行服务器 * @listenfd:文件描述符 * */ void run_server(int listenfd) { int ret = 0; //屏蔽SIGPIPIE信号 if (signal(SIGPIPE, handler) == SIG_ERR) { printf("signal() failed !\n"); return; } //定义文件描述符集合 fd_set allsets; FD_ZERO(&allsets); fd_set readfds; FD_ZERO(&readfds); //定义客户端套接字池 char socketPond[128] = { 0 }; int i = 0; for (i = 0; i < 128; i++) { socketPond[i] = -1; } //定义池子最后一个元素的下标 int maxindex = 0; //定义文件描述符中值最大的fd int maxfd = listenfd; //将监听套接字加入到集合中 FD_SET(listenfd, &allsets); struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(struct sockaddr_in); //定义接收缓冲区 char buf[MAXBUFSIZE] = { 0 }; int buflen = 0; int conn = 0; int nread = 0; while (1) { readfds = allsets; do { nread = select(maxfd + 1, &readfds, NULL, NULL, NULL); } while (nread == -1 && errno == EINTR);//屏蔽信号(重点) if (nread == -1) { perror("select() err"); return; } else if (nread == 0) { //超时 continue; } else if (nread > 0) { //执行操作 //1.处理服务器监听套接字 if (FD_ISSET(listenfd, &readfds)) { //客户端有连接完成三次握手 memset(&peeraddr, 0, sizeof(struct sockaddr_in)); peerlen = sizeof(struct sockaddr_in); conn = accept(listenfd, (struct sockaddr *) &peeraddr, &peerlen); if (conn == -1) { perror("accept() err"); break; } printf("accept from %s\n", inet_ntoa(peeraddr.sin_addr)); //将客户端套接字加入池子中 for (i = 0; i < 128; i++) { if (socketPond[i] == -1) { socketPond[i] = conn; //数组最大下标后移 if (maxindex <= i) maxindex = i + 1; break; } } //如果超过最大连接数,直接关闭连接(重点) if (i == 128) { close(conn); continue; } //将客户端套接字加入到监听集合中 FD_SET(conn, &allsets); //每新加一个连接,就更新最大套接字(重点) if(conn>maxfd) maxfd=conn; //需要处理的事件数-1(重点) if (--nread <= 0) continue; } //2.客户端读事件 for (i = 0; i < maxindex; i++) { if (socketPond[i] == -1) continue; if (FD_ISSET(socketPond[i], &readfds)) { //接收客户端信息 memset(buf, 0, sizeof(buf)); buflen = MAXBUFSIZE; ret = socket_recv(socketPond[i], buf, &buflen); if (ret == -1) { //接收信息出错,关闭套接字 close(socketPond[i]); //将该套接字移除池子 socketPond[i] = -1; //将该套接字移除监听集合 FD_CLR(conn, &allsets); } else { //打印信息 fputs(buf, stdout); //向客户端发送数据 ret = socket_send(socketPond[i], buf, buflen); if (ret == -1) { //发送数据出错,关闭套接字 close(socketPond[i]); //将该套接字移除池子 socketPond[i] = -1; //将该套接字移除监听集合 FD_CLR(conn, &allsets); } } //处理的事件数-1 if (--nread <= 0) break; } } } } return; }
//服务器 #include "serhelp.h" #include <stdio.h> #include "commsocket.h" int main() { int ret = 0; int sockfd = 0; //初始化socket ret = sersocket_init(&sockfd); if (ret != 0) { printf("error message:%s\n", strsockerr(ret)); return -1; } //监听 ret = listen_socket(sockfd, 8080); if (ret != 0) { printf("error message:%s\n", strsockerr(ret)); return -1; } //运行 run_server(sockfd); return 0; }
.SUFFIXES:.c .o CC=gcc SRCS1=mserver.c serhelp.c commsocket.c sockhelp.c OBJS1=$(SRCS1:.c=.o) EXEC1=mser SRCS2=mclient.c clthelp.c commsocket.c sockhelp.c OBJS2=$(SRCS2:.c=.o) EXEC2=mcl start:$(OBJS1) $(OBJS2) $(CC) -o $(EXEC1) $(OBJS1) $(CC) -o $(EXEC2) $(OBJS2) @echo "--------OK-----------" .c.o: $(CC) -Wall -g -o [email protected] -c $< clean: rm -f $(OBJS1) rm -f $(OBJS2) rm -f $(EXEC1) rm -f $(EXEC2)
以上是关于Linux socket多进程服务器框架三的主要内容,如果未能解决你的问题,请参考以下文章
linux C/C++多进程教程(多进程原理以及多进程的应用以多连接socket服务端为例(fork子进程处理socket_fd),同时介绍了僵尸进程产生原因与解决方法)(getpidfork)