Chinaunix首页 | 论坛 | 博客
  • 博客访问: 565515
  • 博文数量: 65
  • 博客积分: 2844
  • 博客等级: 上尉
  • 技术积分: 996
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-02 12:01
文章分类

全部博文(65)

文章存档

2014年(7)

2012年(20)

2011年(35)

2010年(3)

分类: LINUX

2011-07-12 09:36:35


1、简介

使用预先分配线程的并发服务器与之前使用预先分配进程的并发服务器的主要过程是一致的。主程序先建立多个处理线程,然后等待线程的结束,在多个线程中对客户端的请求进行处理。处理过程包括接收客户端的链接,处理数据,发送响应过程。


2、tcp模型


image


3、服务器端源代码(concurrency-server2.c)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <errno.h>
  5. #include <sys/socket.h>
  6. #include <arpa/inet.h>
  7. #include <netinet/in.h>
  8. #include <sys/types.h>
  9. #include <unistd.h>
  10. #include <time.h>
  11. #include <pthread.h>

  12. #define BUFLEN 1024
  13. #define THREADNUM 3
  14. /*互斥量*/
  15. pthread_mutex_t ALOCK = PTHREAD_MUTEX_INITIALIZER;

  16. /****并发服务器模型之二:预先分配多个线程(单客户端单线程,各个线程统一accept,并且使用互斥锁) ****/

  17. static void *handle_thread(void *argv){
  18.     int sockfd = *((int *)argv);    
  19.     int newfd;
  20.     struct sockaddr_in c_addr;
  21.     socklen_t len;
  22.     char buf[BUFLEN];
  23.     time_t now;

  24.     while(1){
  25.         len = sizeof(struct sockaddr);
  26.         /*拒绝客户端的请求*/
  27.         pthread_mutex_lock(&ALOCK);
  28.         if((newfd = accept(sockfd,(struct sockaddr*) &c_addr, &len)) == -1){
  29.             perror("accept");        
  30.             exit(errno);
  31.         }else{
  32.             printf("\n*****************通信开始***************\n");
  33.             printf("正在与您通信的客户端是:%s: %d\n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));        
  34.         }
  35.         /*接受客户端的请求*/
  36.         pthread_mutex_unlock(&ALOCK);
  37.         /******处理客户端请求*******/
  38.         bzero(buf,BUFLEN);
  39.         len = recv(newfd,buf,BUFLEN,0);
  40.         if(len >0 && !strncmp(buf,"TIME",4)){
  41.             bzero(buf,BUFLEN);
  42.             /*获取系统当前时间*/
  43.             now = time(NULL);
  44.             /*ctime将系统时间转换为字符串,sprintf使转化后的字符串保存在buf*/
  45.             sprintf(buf,"%24s\r\n",ctime(&now));
  46.             //******发送系统时间*******/
  47.             send(newfd,buf,strlen(buf),0);
  48.         }
  49.         /*关闭通讯的套接字*/
  50.         close(newfd);
  51.     }
  52.     return NULL;
  53. }

  54. int main(int argc, char **argv)
  55. {
  56.     int sockfd;
  57.     struct sockaddr_in s_addr;
  58.     unsigned int port, listnum;
  59.     pthread_t thread_s[THREADNUM];
  60.     
  61.     /*建立socket*/
  62.     if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
  63.         perror("socket");
  64.         exit(errno);
  65.     }else
  66.         printf("socket create success!\n");
  67.     /*设置服务器端口*/    
  68.     if(argv[2])
  69.         port = atoi(argv[2]);
  70.     else
  71.         port = 4567;
  72.     /*设置侦听队列长度*/
  73.     if(argv[3])
  74.         listnum = atoi(argv[3]);
  75.     else
  76.         listnum = 3;
  77.     /*设置服务器ip*/
  78.     bzero(&s_addr, sizeof(s_addr));
  79.     s_addr.sin_family = AF_INET;
  80.     s_addr.sin_port = htons(port);
  81.     if(argv[1])
  82.         s_addr.sin_addr.s_addr = inet_addr(argv[1]);
  83.     else
  84.         s_addr.sin_addr.s_addr = INADDR_ANY;
  85.     /*把地址和端口帮定到套接字上*/
  86.     if((bind(sockfd, (struct sockaddr*) &s_addr,sizeof(struct sockaddr))) == -1){
  87.         perror("bind");
  88.         exit(errno);
  89.     }else
  90.         printf("bind success!\n");
  91.     /*侦听本地端口*/
  92.     if(listen(sockfd,listnum) == -1){
  93.         perror("listen");
  94.         exit(errno);    
  95.     }else
  96.         printf("the server is listening!\n");
  97.     /*处理客户端的连接*/
  98.     int i = 0;
  99.     for(i = 0; i < THREADNUM; i++){
  100.         /*创建线程来处理连接*/
  101.         pthread_create(&thread_s[i],NULL,handle_thread,(void *)&sockfd);        
  102.     }
  103.     /*等待线程结束*/
  104.     for(i = 0; i < THREADNUM; i++){
  105.         pthread_join(thread_s[i],NULL);
  106.     }
  107.     /*关闭服务器的套接字*/
  108.     close(sockfd);
  109.     return 0;
  110. }


4、客户端源代码(concurrency-client.c)与之前的一致。

5、编译源代码:

new@new-desktop:~/linux/c$ gcc -Wall -lpthread concurrency-server2.c -o server

new@new-desktop:~/linux/c$ gcc -Wall concurrency-client.c -o client

6、运行,试试吧


阅读(4175) | 评论(7) | 转发(3) |
给主人留下些什么吧!~~

ITGrit2011-08-30 10:26:32

wujiajia: 这种模型比一个线程accept,然后分配给工作线程的优点是少去了线程切换的开销,让所有线程竞争上岗,呵呵。
之所以在accept前加lock,是为了防止惊群的发生,不过.....
让所有的线程竞争上岗到没错,但是如果所有的线程都有事可做(处理client请求)的时候,谁去accept。在当前的服务器处理中来看,处理足够简单,出现所有线程都忙碌的情况可能性不大,但是对客户的处理足够复杂,就危险了

wujiajia2011-08-10 17:44:03

这种模型比一个线程accept,然后分配给工作线程的优点是少去了线程切换的开销,让所有线程竞争上岗,呵呵。
之所以在accept前加lock,是为了防止惊群的发生,不过貌似2.6的linux在底层已经做了处理了。

Renwen05242011-07-13 12:14:48

liuhaigang: 为什么不在主进程里面做select,然后又连接就创建线程去处理.....
这个模型是改进了预先分配多进程的模型,只是对预先分配多进程模型的一个优化

liuhaigang2011-07-13 09:31:53

Renwen0524: 为了不在多个线程之间造成accept的竞争,所以加了一个互斥锁。加锁后,一个线程使用accpet,其余线程不能进行accept。.....
为什么不在主进程里面做select,然后又连接就创建线程去处理

Renwen05242011-07-12 17:01:47

okmmno1: accept之前为何要lock?.....
为了不在多个线程之间造成accept的竞争,所以加了一个互斥锁。加锁后,一个线程使用accpet,其余线程不能进行accept。