读书人

并发服务器的实现(历程与线程)

发布时间: 2013-09-29 11:07:08 作者: rapoo

并发服务器的实现(进程与线程)

总体来说,服务器的运行模式大体有两类:循环服务器和并发服务器。所谓的循环服务器就是说他给客户端提供的服务时一个接着一个的,不能同时服务,也就是说当一个用户使用服务器的时候,其他用户不没能使用只能等待。这显然不符合实际服务器的要求,而并发服务器就可以很好地解决这个问题。并发服务器能够同时为多个客户端服务,同时并发服务的能力是服务器性能的一个重要指标。

并发服务器的实现总体有以下几种方法:

① 服务器和每个接收到的客户机进行连接,创建一个新的子进程处理这个客户机请求。

② 服务器预先创建多个子进程,由子进程处理客户机请求。这种方式叫做“预创建”服务器。

③ 服务器用函数select实现对多个客户机连接的多路复用。

④ 超级服务器激活的服务器

这里我们主要讨论第一种实现,我们分为两种实现方式:利用子进程实现,利用线程的方式实现。这里服务器端的功能就是接受客户端的数据,并在服务器屏幕上打印出来,而且要求客户机可以主动断开链接,服务器能够为多个客户机服务。

利用子进程方式实现,服务器端代码:

/*server_fork.c*/#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <string.h>#include <pthread.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <semaphore.h>/*定义通讯端口*/#define MYPORT 4000/*定义服务器端等待队列长度*/#define BACKLOG 10/*定义数据缓冲区大小*/#define MAXDATASIZE 1024/*定义服务器同时可以服务的客户端数,为了方便测试,这里设置为2*/#define MAXTHREAD 2/*定义客户端发给服务器端断开链接的信号*/#define FINISH "close"/*服务器套接字和线程套接字*/int sock_fd,new_fd[MAXTHREAD];int numbytes;int n;int sin_size;/*定义标志缓冲区是否可用的标志*/int flag[MAXTHREAD];/*定义缓冲区,一共MAXTHREAD个,每个MAXDATASIZE字节*/char buf[MAXTHREAD][MAXDATASIZE];/*定义每个缓冲区的信号量,实现互斥操作*/sem_t sem[MAXTHREAD];/*线程号*/pthread_t thread[MAXTHREAD];/*IP地址数据结构*/struct sockaddr_in server_ip,client_ip;/*自定义函数,功能是找到合适的缓冲区来分配给线程使用,当缓冲区的标志为0时表示可用,则返回序号,否则不可用返回-1,进行其他处理*/int find(void){int i = 0;for(i=0;i<MAXTHREAD;i++){if( flag[i] == 0 )return i;}return -1;}/*线程处理函数*/void *func(void *arg){int thrd_num = (int)arg;/*执行P操作*/sem_wait(&sem[thrd_num]);/*打印链接信息*/printf("server get connection from %s,thread %d.\n",inet_ntoa(client_ip.sin_addr),thrd_num);while(1){/*接收客户端信息*/if( (numbytes=recv(new_fd[thrd_num],(void *)buf[thrd_num],MAXDATASIZE,0)) == -1 ){perror("recv");pthread_exit(NULL);}/*判断客户端的信息是不是断开信号,是则断开链接,否则打印收到的信息*/if( strcmp(FINISH,buf[thrd_num]) ){buf[thrd_num][numbytes] == '\0';printf("server: In the thread %d,%s has been read!\n",thrd_num,buf[thrd_num]);}else{printf("%s\n",buf[thrd_num]);if( (numbytes=send(new_fd[thrd_num],"Connection closed!\n",MAXDATASIZE,0)) == -1 ){printf("close error!\n");pthread_exit(NULL);}printf("In the thread %d,connection closed!\n",thrd_num);/*关闭线程操作的套接字*/close(new_fd[thrd_num]);/*执行V操作*/sem_post(&sem[thrd_num]);/*将旗标设置为0,以便下一个线程使用*/flag[thrd_num] = 0;pthread_exit(NULL);}}}int main(void){int i,no = 0;int ret;/*初始化缓冲区和信号量*/for(i=0;i<MAXTHREAD;i++){memset(buf[i],0,MAXDATASIZE);sem_init(&sem[i],0,1);}/*创建套接字,类型为流式,默认TCP*/if( (sock_fd=socket(AF_INET,SOCK_STREAM,0)) == -1 ){perror("socket");exit(1);}/*设置协议族为IPv4,端口为自定义端口,地址为本地任意可用IP地址*/server_ip.sin_family = AF_INET;server_ip.sin_port = htons(MYPORT);server_ip.sin_addr.s_addr = htonl(INADDR_ANY);n = 1;/*设置套接字选项,使其可以重复使用端口*/setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(int));/*绑定地址和端口信息*/if( bind(sock_fd,(struct sockaddr *)&server_ip,sizeof(struct sockaddr)) == -1 ){perror("bind");exit(1);}/*监听来自客户端的链接,队列长度为BACKLOG*/if( listen(sock_fd,BACKLOG) == -1 ){perror("listen");exit(1);}/*主操作部分*/while(1){sin_size = sizeof(struct sockaddr);/*利用find函数找到合适的缓冲区,如果全部被占用,则从头依次向后排列,均衡阻塞*/if( (ret=find()) == -1 ){no++;no = no % MAXTHREAD;/*等待占用线程结束,如果有多个在等待则形成等待队列*/pthread_join(thread[no],NULL);}elseno = ret;/*接受下一个客户端链接*/if( (new_fd[no] = accept(sock_fd,(struct sockaddr *)&client_ip,&sin_size)) == -1 ){perror("accept");exit(1);}/*创建线程*/if( pthread_create(&thread[no],NULL,func,(void *)no) ){perror("pthread_create");continue;}/*创建成功,设置旗标*/else{printf("Thread %d has created!\n",no);flag[no] = 1;}}/*关闭服务器套接字*/close(sock_fd);return 0;}

两个服务器端的效果一样,只是实现的方式不同。本人初学网络编程,文中必有瑕疵,如有高见,请赐教!!!!






读书人网 >编程

热点推荐