并发服务器实现 线程 进程 函数 select poll 代码实现 具体理论细节还需要补充
1. //并发服务器 创建线程负责数据交互
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <sys/un.h> #include <unistd.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #include<string.h> extern int errno; void *routine(void *arg) { int newfd=(int)arg; char buf[100]; while(1) { int cnt1=read(newfd,buf,100); printf("threadid=%u,fd=%d\n",pthread_self(),newfd); write(1,buf,cnt1); if(strncmp(buf,"quit",4)==0) break; } printf("threadid %u exit\n",pthread_self()); close(newfd); pthread_exit(); return NULL; } int main() { //socket int fd=socket(AF_INET,SOCK_STREAM,0); //绑定地址 协议 端口 ip struct sockaddr_in servaddr; servaddr.sin_family = AF_INET; servaddr.sin_port = htons(1234); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); printf("server: ip:%s port:%d\n",inet_ntoa(servaddr.sin_addr),ntohs(servaddr.sin_port)); int ret=bind(fd,(struct sockaddr *)&servaddr,sizeof(servaddr)); if(ret<0) { fprintf(stderr,"error:%s\n",strerror(errno)); close(fd); return -1; } //服务器监听 listen(fd,5); while(1) { pthread_t tid; int newfd; struct sockaddr_in peer; socklen_t size=sizeof(peer); printf("waiting for client connectting.......\n"); //响应客户端连接 newfd=accept(fd,(struct sockaddr *)&peer,&size); //客户端 ip 端口 printf("client: ip:%s port:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port)); //建立线程 负责接收客户端数据 pthread_create(&tid,NULL,routine,(void*)newfd); } return 0; }
2. //并发服务器 创建线程负责数据交互
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <sys/un.h> #include <unistd.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #include<string.h> extern int errno; void *routine(void *arg) { int newfd=(int)arg; char buf[100]; while(1) { int cnt1=read(newfd,buf,100); printf("threadid=%u,fd=%d\n",pthread_self(),newfd); write(1,buf,cnt1); if(strncmp(buf,"quit",4)==0) break; } printf("threadid %u exit\n",pthread_self()); close(newfd); pthread_exit(); return NULL; } int main() { //socket int fd=socket(AF_INET,SOCK_STREAM,0); //绑定地址 协议 端口 ip struct sockaddr_in servaddr; servaddr.sin_family = AF_INET; servaddr.sin_port = htons(1234); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); printf("server: ip:%s port:%d\n",inet_ntoa(servaddr.sin_addr),ntohs(servaddr.sin_port)); int ret=bind(fd,(struct sockaddr *)&servaddr,sizeof(servaddr)); if(ret<0) { fprintf(stderr,"error:%s\n",strerror(errno)); close(fd); return -1; } //服务器监听 listen(fd,5); while(1) { pthread_t tid; int newfd; struct sockaddr_in peer; socklen_t size=sizeof(peer); printf("waiting for client connectting.......\n"); //响应客户端连接 newfd=accept(fd,(struct sockaddr *)&peer,&size); //客户端 ip 端口 printf("client: ip:%s port:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port)); //建立线程 负责接收客户端数据 pthread_create(&tid,NULL,routine,(void*)newfd); } return 0; }
3. 并发服务器 select函数完成IO复用
服务器接收多个客户端请求,接收客户端的数据并打印在服务器端
</pre></blockquote></blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><pre name="code" class="cpp">#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <arpa/inet.h> #define LISTEN_PORT 1234 #define MAX_CLIENT 5 #define BUFFSIZE 256 int fds[MAX_CLIENT]; //存储连接的文件描述符 int main(int argc, char **argv) { //监听套接字 连接套接字 int listenfd, newfd; // 地址结构 : 服务器地址信息,客户端地址信息 struct sockaddr_in servaddr, peeraddr; socklen_t addrsize; // if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } //设置套接字的选项 Address reuse //允许在同一个端口启动服务器的多个实例 int ret; int on = 1; // 级别:普通套接字 地址复用 ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int)); // if ret memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(LISTEN_PORT); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); // if ret ret = listen(listenfd, MAX_CLIENT); // if ret //文件描述符集 fd_set rdfds; // 可读的集合 struct timeval tv; //定时 int maxsockfd; // rdfds集合中最大文件描述符加1 maxsockfd = listenfd + 1; int i; char buff[BUFFSIZE]; while(1) { // 初始化文件描述符集合 FD_ZERO(&rdfds); FD_SET(listenfd, &rdfds); //将listenfd加入rdfds for(i = 0; i < MAX_CLIENT; i++) if(fds[i] != 0) // 表示对应一个客户端连接 FD_SET(fds[i], &rdfds); // 添加活动的连接 // 超时设定 tv.tv_sec = 30; tv.tv_usec = 0; puts("no change ........"); //select 监控fd发生变化 ret = select(maxsockfd, &rdfds, NULL, NULL, &tv); puts("change .........."); // ret < 0 : error // ret ==0 : timout // ret > 0 , IO change if(ret < 0) { perror("select"); break; } if(ret == 0) { printf("time out\n"); continue; } // ret > 0 // new connection addrsize = sizeof(peeraddr); //服务器响应客户端请求 if(FD_ISSET(listenfd, &rdfds)) //有客人已经来访, { newfd = accept(listenfd, (struct sockaddr *)&peeraddr, &addrsize); //开门 // 添加新的fd到数组中 for(i = 0; i < MAX_CLIENT; i++){ if(fds[i] == 0) { fds[i] = newfd; break; } } // maxsockfd 保证大于集合中的所有文件描述符 if(newfd > maxsockfd - 1) maxsockfd = newfd + 1; } // fds[] socket临时存储 // rdfds, FD_SET 集合 // read data fds[] // 遍历客户端fd 查找是否有可读的fd for(i = 0; i < MAX_CLIENT; i++) { if(FD_ISSET(fds[i], &rdfds)) { // can read // recv read //ret = read(fds[i], buff, BUFFSIZE); ret = recv(fds[i], buff, BUFFSIZE, 0); //客户端连接关闭,清除fds相应元素 if(ret <=0 ){ close(fds[i]); FD_CLR(fds[i], &rdfds); fds[i] = 0; } else{ write(1, buff, ret); puts(""); } } // end if } // end for }// end while }
4.并发服务器 poll函数完成IO复用
<span style="font-size:10px;font-weight: normal;">#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/time.h> #include <netinet/in.h> #include <arpa/inet.h> #include <poll.h> #include <sys/poll.h> #include <sys/stat.h> #include <fcntl.h> #include <netdb.h> #define MYPORT 1234 //连接时使用的端口 #define MAXCLINE 5 //连接队列中的个数 #define BUF_SIZE 256 #define MAXUSER 256 void showclient(struct pollfd fds[]) { int i; printf("client amount:%d\n", conn_amount); for(i = 0; i < MAXCLINE; i++) { printf("[%d]:%d ",i,fds[i].fd); } printf("\n\n"); } int main(int argc, char **argv) { int sock_fd,new_fd; //监听套接字 连接套接字 struct sockaddr_in server_addr; // 服务器的地址信息 struct sockaddr_in client_addr; //客户端的地址信息 socklen_t sin_size; //当前的连接数 int conn_amount=0; int yes = 1; char buf[BUF_SIZE]; int ret; int i; if((sock_fd = socket(AF_INET,SOCK_STREAM, 0))==-1) { perror("socket"); exit(1); } //设置套接口的选项 SO_REUSEADDR 允许在同一个端口启动服务器的多个实例 //setsockopt的第二个参数SOL SOCKET 指定系统中,解释选项的级别 普通套接字 //fcntl(sock_fd, F_SETFL, O_NONBLOCK); if(setsockopt(sock_fd,SOL_SOCKET, SO_REUSEADDR,&yes, sizeof(int))==-1) { perror("setsockopt error \n"); exit(1); } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; //主机字节序 server_addr.sin_port = htons(MYPORT); server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//通配IP // memset(server_addr.sin_zero,'\0',sizeof(server_addr.sin_zero)); if(bind(sock_fd,(struct sockaddr *)&server_addr,sizeof(server_addr)) == -1) { perror("bind error!\n"); exit(1); } if(listen(sock_fd, MAXCLINE)== -1) { perror("listen error!\n"); exit(1); } printf("listen port %d\n", MYPORT); struct pollfd fdsr[MAXUSER]; int maxsock = sock_fd; conn_amount =0; sin_size = sizeof(client_addr); /*将数组中的第一个元素设置成监听描述字*/ fdsr[0].fd = sock_fd; //fdsr[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; fdsr[0].events = POLLIN; /*数组中的其它元素将暂时设置成不可用*/ for(i = 1; i < MAXUSER; i++) fdsr[i].fd = -1; int nready; int recv_bytes; while(1){ //将进程阻塞在poll上 printf("wait client \n"); nready = poll(fdsr, maxsock + 1, -1); // INFTIM printf("out\n"); /*先测试监听描述字*/ if( fdsr[0].revents & POLLIN) { printf("wait for new connect...\n"); new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size); /*将新连接加入到测试数组中*/ printf("new connect... %d\n", new_fd); for(i = 1; i < MAXUSER; i++) { if(fdsr[i].fd < 0) { fdsr[i].fd = new_fd; fdsr[i].events = POLLIN;/*测试条件普通数据可读*/ break; } } if(i == MAXUSER) { printf("too many client\n"); continue; } if(i > maxsock) maxsock = i; if(--nready <= 0 ) continue; //如果没有可读的描述符了,就重新监听连接 } /*测试除监听描述字以后的其它连接描述字*/ for( i = 1; i <= maxsock; i++) { if(fdsr[i].fd < 0) continue; if(fdsr[i].revents & (POLLIN | POLLERR)) { memset(buf, 0, BUF_SIZE); recv_bytes = recv(fdsr[i].fd, buf, BUF_SIZE, 0); if(recv_bytes < 0) { if( errno == ECONNRESET) { //如果连接断开,就关闭连接,并设当前描述符不可用 close(fdsr[i].fd); fdsr[i].fd = -1; } else perror("read error"); } else if(recv_bytes == 0) { //如果数据读取完毕,关闭连接,设置当前描述符不可用 close(fdsr[i].fd); fdsr[i].fd = -1; } else printf("newfd = %d : %s\n",fdsr[i].fd, buf); } } showclient(fdsr); } } </span>
5.客户端测试代码 客户端向服务器发送数据
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <sys/un.h> #include <unistd.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> extern int errno; int main() { int fd=socket(AF_INET,SOCK_STREAM,0); fprintf(stderr,"client id=%d\n",fd); struct sockaddr_in servaddr; servaddr.sin_family = AF_INET; servaddr.sin_port = htons(1234); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); int ret=connect(fd,(struct scokaddr *)&servaddr,sizeof(servaddr)); if(ret<0) { fprintf(stderr,"error:%s\n",strerror(errno)); close(fd); return -1; } char buf[100]; while(1) { fgets(buf,100,stdin); write(fd,buf,strlen(buf)); if(strncmp(buf,"quit",4)==0) { close(fd); return 0; } } close(fd); return 0; }