基於自定義協議的服務器高併發處理之:多線程模型

http://www.javashuo.com/article/p-zolorgfe-r.html 只是簡單的處理,服務器返回客戶端一個時間,而後關閉了socket。html

若是要進行雙向通訊,服務器勢必要調用read函數,而read默認阻塞,那麼若是客戶端不向服務器發送數據,則主線程一直阻塞,其它客戶端沒法鏈接成功。這就須要處理高併發問題。服務器

服務器高併發處理的三種方式網絡

  1. 多進程 http://www.javashuo.com/article/p-rsygfewi-e.html
  2. 多線程 http://www.javashuo.com/article/p-vvrmydhx-x.html
  3.  I/O多路複用

 目錄 makefile的設置都在多進程裏面有介紹 http://www.javashuo.com/article/p-rsygfewi-e.html多線程

多線程的效率明顯高於多進程,並且由於線程共享大部分進程空間,而多進程複製空間,從而下降了系統開銷。併發

  1. 主線程只是負責accepte,而後建立帶分離屬性的子線程。
  2. 每一個客戶端鏈接維護一個帶分離屬性的線程,在此線程裏作雙向通訊。即有多少客戶端鏈接,就會開闢多少子線程。

客戶端和多進程的用同一個。socket

實際上只在main函數accept的時候有區別,建立了分離線程,免去了主線程join等待回收線程。具體的do_something沒有區別。tcp

多線程服務端 echo_tcp_server_thread.c :函數

  1 #include <sys/socket.h>
  2 #include <sys/types.h>
  3 #include <stdio.h>
  4 #include <stdlib.h>
  5 #include <unistd.h>
  6 #include <signal.h>
  7 #include <time.h>
  8 #include <string.h>
  9 #include <netdb.h>
 10 #include <arpa/inet.h>
 11 #include "msg.h"
 12 #include <errno.h>
 13 #include <pthread.h>
 14 
 15 #define SERVER_PORT 8888
 16 #define LISTEN_QUEUE_SISE 10
 17 
 18 int socketfd;
 19 
 20 void signal_handler(int signo)
 21 {
 22     if (signo == SIGINT)
 23     {
 24         printf("this serveice close\n");
 25         close(socketfd);
 26         exit(1);
 27     }
 28 }
 29 
 30 void print_clientinfo(int fd, const char* type)
 31 {
 32     struct sockaddr_in addr;
 33     memset(&addr, 0, sizeof(addr));
 34     socklen_t len = sizeof(addr);
 35 
 36     if(getpeername(fd, (struct sockaddr*)&addr, &len) < 0)
 37     {
 38         perror("getpeername error");
 39         return;
 40     }
 41 
 42     char ipstr[16];
 43     memset(ipstr, 0, sizeof(ipstr));
 44     // 將地址從網絡字節序轉換爲點分十進制
 45     inet_ntop(AF_INET, &addr.sin_addr.s_addr, ipstr, sizeof(ipstr));
 46 
 47     printf("%s(%d) %s \n", ipstr, ntohs(addr.sin_port), type);
 48 }
 49 
 50 void do_something(int fd)
 51 {
 52     char buff[1024];
 53     while(1)
 54     {
 55         //讀取客戶端發送過來的內容
 56         memset(buff, 0, sizeof(buff));
 57         size_t size = read_msg(fd, buff, sizeof(buff));
 58         if (size < 0)
 59         {
 60             // 協議出錯 跳出while 回到main 結束子進程 
 61             perror("read_msg error");
 62             break;
 63         }
 64         else if (size == 0)
 65         {
 66             /* 客戶端斷開了鏈接 掛掉了 跳出while 會結束子線程
 67              **/
 68             break;
 69         }
 70         
 71         printf("recev from client:%s\n",buff);
 72 
 73         //將數據寫回
 74         if (write_msg(fd, buff, sizeof(buff)) < 0)
 75         {
 76             if(errno == EPIPE)
 77             {
 78                 /* 客戶端斷開了鏈接  
 79                  * 產生SIGPIPE信號 
 80                  * 將error設置爲EPIPE
 81                  * 因此可在此判斷errno 也可捕捉SIGPIPE信號
 82                  * 俗稱管道爆裂
 83                  * 跳出while 會結束子線程
 84                  **/
 85                 break;
 86             }
 87             
 88 
 89             perror("write_msg error");
 90         }
 91     }
 92 }
 93 
 94 void* threadWork(void* arg)
 95 {
 96     int fd = (int)arg;
 97     print_clientinfo(fd, "connected");
 98     do_something(fd);
 99     print_clientinfo(fd, "closed");
100     close(fd);
101     return NULL;
102 }
103 
104 
105 int main(int argc, char const *argv[])
106 {
107     if (signal(SIGINT, signal_handler) == SIG_ERR)
108     {
109         perror("signal error");
110         exit(1);
111     }
112 
113     // 1 sokect
114     // AF_INET ipv4
115     // SOCK_STREAM tcp
116     if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
117     {
118         perror("socket error");
119         exit(1);
120     }
121 
122     // 2 bind 綁定本地地址和端口
123     struct sockaddr_in serveraddr;
124     memset(&serveraddr, 0, sizeof(serveraddr));
125     serveraddr.sin_family = AF_INET;//ipv4
126     serveraddr.sin_port = htons(SERVER_PORT); //端口
127     serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);//響應任意網卡的請求
128     if(bind(socketfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)
129     {
130         perror("bind error");
131         exit(1);
132     }
133 
134     // 3 listen 啓動監聽 通知系統接受來自客戶端的鏈接 準備好鏈接隊列
135     if(listen(socketfd, LISTEN_QUEUE_SISE) < 0)
136     {
137         perror("listen error");
138         exit(1);
139     }
140 
141     pthread_t thread;
142     //設置分離屬性相關 不須要主線程調用pthread_join回收子線程
143     pthread_attr_t attr;
144     pthread_attr_init(&attr);
145     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
146     int retno;
147     while(1)
148     {    
149         // 4 accept 從隊列拿出第一個
150         int clientfd = accept(socketfd, NULL, NULL);
151         if (clientfd  < 0)
152         {
153             perror("accept error");
154             continue;
155         }
156         // 5 建立分離屬性的子線程 在子線程中作read/write
157         if( (retno = pthread_create(&thread, &attr, 
158             threadWork, (void*)clientfd)) != 0 )
159         {
160             printf("error nuber:%d, %s\n", retno, strerror(retno));
161             close(clientfd);
162             continue;
163         }
164 
165     }
166     
167     // 銷燬線程屬性
168     pthread_attr_destroy(&attr);
169     // 6 close
170     close(socketfd);
171     return 0;
172 }
相關文章
相關標籤/搜索