1、简介
前面的服务器模型主要集中在并发服务器上,并发服务器有个比较大的缺陷,它需要建立多个并行的处理单元。当客户端增加时,随着处理单元的增加,系统的负载会逐渐转移到并行单元的现场切换上。因此有一个比较新型的IO复用循环服务器。该模型在系统开始时,建立多个不同工作类型的处理单元,当客户端的请求到来时,将客户端的连接放到一个状态池中,对所有客户端的连接状态在一个处理单元中进行轮询处理。
2、tcp模型
3、服务器源代码(concurrency-server5.c):
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/socket.h>
- #include <arpa/inet.h>
- #include <netinet/in.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <time.h>
- #include <pthread.h>
- #define BUFLEN 1024
- #define THREADNUM 2
- #define CLIENTNUM 1024
- int connect_host[CLIENTNUM];
- int connect_num = 0;
- /*******************并发服务器模型之五:IO复用循环服务器**********************/
- static void *handle_request(void *argv){
- char buf[BUFLEN];
- int len;
- time_t now;
- int maxfd = -1;
- fd_set rfds;
- struct timeval tv;
- tv.tv_sec = 1;
- tv.tv_usec = 0;
- int i =0;
- int err = -1;
- while(1){
- FD_ZERO(&rfds);
- for(i = 0; i < CLIENTNUM; i++){
- if(connect_host[i] != -1){
- FD_SET(connect_host[i],&rfds);
- if(maxfd < connect_host[i])
- maxfd = connect_host[i];
- }
- }
- err = select(maxfd+1, &rfds, NULL, NULL, &tv);
- switch(err){
- case 0: break;
- case -1: break;
- default:
- if (connect_num < 0)
- break;
- for(i = 0; i < CLIENTNUM; i++){
- if(connect_host[i] != -1){
- if(FD_ISSET(connect_host[i],&rfds)){
- /******处理客户端请求*******/
- bzero(buf,BUFLEN);
- len = recv(connect_host[i],buf,BUFLEN,0);
- if(len >0 && !strncmp(buf,"TIME",4)){
- bzero(buf,BUFLEN);
- /*获取系统当前时间*/
- now = time(NULL);
- /*ctime将系统时间转换为字符串,sprintf使转化后的字符串保存在buf*/
- sprintf(buf,"%24s\r\n",ctime(&now));
- //******发送系统时间*******/
- send(connect_host[i],buf,strlen(buf),0);
- }
- /*关闭通讯的套接字*/
- close(connect_host[i]);
- /*更新文件描述符在数组中的值*/
- connect_host[i] = -1;
- connect_num--;
- }
- }
- }
- break;
- }
- }
- return NULL;
- }
- static void *handle_connect(void *arg){
- int sockfd = *((int *)arg);
- int newfd;
- struct sockaddr_in c_addr;
- socklen_t len;
- int i;
- while(1){
- len = sizeof(struct sockaddr);
- if((newfd = accept(sockfd,(struct sockaddr*) &c_addr, &len)) >0){
- printf("\n*****************通信开始***************\n");
- printf("正在与您通信的客户端是:%s: %d\n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));
- for(i = 0; i < CLIENTNUM; i++){
- if(connect_host[i] == -1){
- connect_host[i] = newfd;
- /*客户端计数器*/
- connect_num++;
- /*继续等待新的客户端*/
- break;
- }
- }
- }
- }
- return NULL;
- }
- int main(int argc, char **argv)
- {
- int sockfd;
- struct sockaddr_in s_addr;
- unsigned int port, listnum;
- pthread_t thread_s[2];
- /**/
- memset(connect_host,-1,CLIENTNUM);
- /*建立socket*/
- if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
- perror("socket");
- exit(errno);
- }else
- printf("socket create success!\n");
- /*设置服务器端口*/
- if(argv[2])
- port = atoi(argv[2]);
- else
- port = 4567;
- /*设置侦听队列长度*/
- if(argv[3])
- listnum = atoi(argv[3]);
- else
- listnum = 3;
- /*设置服务器ip*/
- bzero(&s_addr, sizeof(s_addr));
- s_addr.sin_family = AF_INET;
- s_addr.sin_port = htons(port);
- if(argv[1])
- s_addr.sin_addr.s_addr = inet_addr(argv[1]);
- else
- s_addr.sin_addr.s_addr = INADDR_ANY;
- /*把地址和端口帮定到套接字上*/
- if((bind(sockfd, (struct sockaddr*) &s_addr,sizeof(struct sockaddr))) == -1){
- perror("bind");
- exit(errno);
- }else
- printf("bind success!\n");
- /*侦听本地端口*/
- if(listen(sockfd,listnum) == -1){
- perror("listen");
- exit(errno);
- }else
- printf("the server is listening!\n");
- /*创建线程处理客户端的连接*/
- pthread_create(&thread_s[0],NULL,handle_connect,(void *)&sockfd);
- /*创建线程处理客户端的请求*/
- pthread_create(&thread_s[1],NULL,handle_request, NULL);
- /*等待线程结束*/
- int i;
- for(i = 0; i < THREADNUM; i++){
- pthread_join(thread_s[i],NULL);
- }
- /*关闭服务器的套接字*/
- close(sockfd);
- return 0;
- }
4、客户端源代码(concurrency-client.c)与之前的一样。
5、编译源代码:
new@new-desktop:~/linux/c$ gcc -Wall –lpthread concurrency-server5.c -o server
new@new-desktop:~/linux/c$ gcc -Wall concurrency-client.c -o client
6、运行,试试吧