失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > linux网络编程--服务器模型(epoll/select/poll)

linux网络编程--服务器模型(epoll/select/poll)

时间:2022-09-26 14:34:53

相关推荐

linux网络编程--服务器模型(epoll/select/poll)

服务器模型有循环服务器,

tcp流程如下:

socket();

bind();

listen();

while(1)

{

accept(...);

while(1)

{

recv();

process(...);

send();

}

close();

}

udp流程如下:

socket();

bind();

while(1)

{

recvfrom();

process(...);

sendto();

}

循环服务器最大的缺点就是:同一个时刻只能处理一个请求,只有在当前客户的所有请求都完成后,服务器才能处理下一个客户的连接/服务请求

并发服务器:

tcp并发服务器:

linstenfd=socket();bind(); listen();

signal(SIGCHLD,handler);

while(1)

{

connfd=accept();

if(fork==0)

{

close(listenfd);

while(1)

{

recv(); process(...);send();

}

close(connd);

exit(....)

}

close(connfd)

}

void handler(int signo){

waitpid(-1,NULL,WNOHANANGE);

}

并发服务器的设计思路是服务器接收客户端的连接请求后创建子进程来为客户服务

tcp并发服务器可以避免TCP循环服务器中客户端独占服务器的情况

为了响应客户机的请求,服务器要创建子进程来处理,如果有多个客户端的话,服务器需要创建多个子进程,过多的子进程会影响服务器的运行效率

udp类似

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <arpa/inet.h>#include <signal.h>#include <unistd.h>#define err_log(errlog) do{perror(errlog); exit(1);}while(0)#define N 128// ./server 192.168.0.196 10000void handler(int sig){waitpid(-1, NULL, WNOHANG);}int main(int argc, const char *argv[]){int sockfd;int confd;pid_t pid;struct sockaddr_in serveraddr, clientaddr;char buf[N] = {};if(argc != 3){fprintf(stderr, "Usage:%s serverip port.\n", argv[0]);return -1;}if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){err_log("fail to socket");}printf("sockfd = %d\n", sockfd);serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(argv[1]);serveraddr.sin_port = htons(atoi(argv[2]));if(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){err_log("fail to bind");}if(listen(sockfd, 10) < 0){err_log("fail to listen");}socklen_t addrlen = sizeof(struct sockaddr);signal(SIGCHLD, handler);while(1){if((confd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) < 0){err_log("fail to accept");}printf("confd = %d , %s --> %d \n", confd , inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));if((pid = fork()) < 0){err_log("fail to fork");}else if(pid == 0) //child {close(sockfd);while(1){if(recv(confd, buf, N, 0) < 0){err_log("fail to recv");}printf("%s\n", buf);if(strncmp(buf, "quit", 4) == 0){break;}strcpy(buf, "Message from server.");if(send(confd, buf, N, 0) < 0){err_log("fail to send");}}close(confd);exit(0);}else{close(confd);}}close(sockfd);return 0;}

I/O多路复用并发服务器模型

初始化linstenfd=socket();bind(); listen();

while(1)

{

设置监听的读写文件描述符集合(FD_*);

调用select;

如果是监听套接字准备就绪,说明有新的连接请求{

建立连接(acept);

加入监听文件描述符集合;

}

否则说明是一个已经连接过的文件描述符{

进行操作(send或者recv);

}

}

实例如下:

server.c

#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <string.h>#include <netinet/in.h>#include <arpa/inet.h>#define N 128#define err_log(errlog) do{perror(errlog);exit(1);}while(0)int main(int argc, const char *argv[]){fd_set readfds;fd_set tempfds;int maxfd;int i = 0;char buf[N] = {};int sockfd;int acceptfd;struct sockaddr_in serveraddr, clientaddr;socklen_t addrlen = sizeof(clientaddr);if(argc < 3){fprintf(stderr, "Usage:%s serverip port.\n", argv[0]);}if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){err_log("fail to socket");}serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(argv[1]);serveraddr.sin_port = htons(atoi(argv[2]));if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){err_log("fail to bind");}if(listen(sockfd, 5) < 0){err_log("fail to listen");}FD_ZERO(&readfds);FD_SET(sockfd, &readfds);maxfd = sockfd;while(1){tempfds = readfds;if(select(maxfd+1,&tempfds, NULL, NULL,NULL) < 0){err_log("fail to select");}printf("After select.\n");for(i = 0; i < maxfd+1; i++){if(FD_ISSET(i, &tempfds)){if(i == sockfd){if((acceptfd = accept(sockfd, (struct sockaddr*)&clientaddr, &addrlen)) < 0){err_log("fail to accept");}printf("%s --> %d --> acceptfd = %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), acceptfd);maxfd = maxfd > acceptfd ? maxfd:acceptfd;FD_SET(acceptfd, &readfds);}else{if(recv(i, buf, N, 0) < 0){err_log("fail to recv");}printf("server:%s\n", buf);if(strncmp(buf, "quit", 4) == 0){FD_CLR(i, &readfds);close(i);continue;}strcpy(buf, "Message from server.");if(send(i, buf, N, 0) < 0){err_log("fail to send");}}}}}return 0;}

首先这篇讲epoll 讲的很好,/lojunren/p/3856290.html

这儿就不具体说了

#include <stdio.h>#include <stdlib.h>#include <sys/socket.h>#include <sys/types.h>#include <arpa/inet.h>#include <netinet/in.h>#include <unistd.h>#include <string.h>#include <sys/epoll.h>#include <fcntl.h>#include <sys/resource.h>#include <pthread.h>#include <errno.h>#define N 128#define err_log(errlog) do{perror(errlog); exit(1);}while(0)#define MAX_EVENTS 10000pthread_mutex_t mutex;int epollfd;void *pthread_client(void *arg){char buf[N] = {0};int acceptfd;struct epoll_event ev;int ret = 0;acceptfd = *((int *)arg);printf("acceptfd = %d \n", acceptfd);//set_blocking(acceptfd);while(1){if((ret = recv(acceptfd, buf, N, 0)) < 0){if(errno == 11){continue;}else {printf("errno = %d \n", errno);pthread_exit("pthread exit sucess!");}}if(ret == 0){if (epoll_ctl(epollfd, EPOLL_CTL_DEL, acceptfd,NULL) < 0) {err_log("fail to epoll_ctl");}close(acceptfd);pthread_exit("pthread exit sucess!");break;}printf("client:%s\n", buf);strcat(buf, " from server..");if(send(acceptfd, buf, N, 0) < 0){err_log("fail to send");}memset(buf, 0, sizeof(buf));}}int set_non_blocking(int acceptfd){int flags ;flags = fcntl(acceptfd, F_GETFL, 0);flags |= O_NONBLOCK;fcntl(acceptfd, F_SETFL, flags);return 0;}int set_blocking(int acceptfd){int flags ;flags = fcntl(acceptfd, F_GETFL, 0);flags |= ~O_NONBLOCK;fcntl(acceptfd, F_SETFL, flags);return 0;}int main(int argc, const char *argv[]){struct epoll_event ev, events[MAX_EVENTS];intnfds, n;int sockfd;struct sockaddr_in serveraddr, clientaddr;int acceptfd;char buf[N];socklen_t addrlen = sizeof(struct sockaddr);if(argc != 3){fprintf(stderr,"Usage:%s serverip port.\n", argv[0]);return -1;}if(pthread_mutex_init(&mutex, NULL) != 0){err_log("fail to pthread_mutex_init");}if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){err_log("fail to socket");}serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(argv[1]);serveraddr.sin_port = htons(atoi(argv[2]));if(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){err_log("fail to bind");}if(listen(sockfd, 10) < 0){err_log("fail to listen");}/*** size 表示你所关心的最大文件描述符的个数,只与内存有关* epollfd 非负的文件描述符,专用文件描述符,epoll实例的句柄*/if((epollfd = epoll_create(10)) < 0){err_log("fail to epoll_create");}//定义一个epoll事件,包括读,要监听的文件描述符ev.events = EPOLLIN;ev.data.fd = sockfd;//控制专用文件描述符,将sockfd 和 ev 添加到专用文件描述符上,完成注册if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {err_log("fail to epoll_ctl");}for (;;) {pthread_mutex_lock(&mutex);// 等待专用文件描述符epollfd 上的事件的发生// events 待处理事件的数组,保存的是已经准备好的要处理的事件。// MAX_EVENTS 最大处理的事件数// nfds 返回发生的事件数if((nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1)) < 0){pthread_mutex_unlock(&mutex);err_log("fail to epoll_wait");}pthread_mutex_unlock(&mutex);for (n = 0; n < nfds; ++n) {// 查看是那个事件发生了if(events[n].data.fd == sockfd) {if((acceptfd = accept(sockfd,(struct sockaddr *) &serveraddr, &addrlen)) < 0){err_log("fail to accept");}// set_non_blocking(acceptfd); ev.events = EPOLLIN | EPOLLET;ev.data.fd = acceptfd;if (epoll_ctl(epollfd, EPOLL_CTL_ADD, acceptfd,&ev) == -1) {err_log("fail to epoll_ctl");}} else {// 开启一个新的线程,专门用来处理对应客户端的请求pthread_t tid;if(pthread_create(&tid, NULL, pthread_client,&(events[n].data.fd)) < 0){err_log("fail to pthread_create");}}}}return 0;}

如果觉得《linux网络编程--服务器模型(epoll/select/poll)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。