服务器模型有循环服务器,
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 10000 void 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. ", argv[0]); return -1; } if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { err_log("fail to socket"); } printf("sockfd = %d ", 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 ", 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 ", 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. ", 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. "); 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 ", 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 ", 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 讲的很好,https://www.cnblogs.com/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 10000 pthread_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 ", acceptfd); //set_blocking(acceptfd); while(1) { if((ret = recv(acceptfd, buf, N, 0)) < 0) { if(errno == 11) { continue; } else { printf("errno = %d ", 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 ", 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]; int nfds, 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. ", 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; }