http://www.javashuo.com/article/p-zolorgfe-r.html 只是簡單的處理,服務器返回客戶端一個時間,而後關閉了socket。html
若是要進行雙向通訊,服務器勢必要調用read函數,而read默認阻塞,那麼若是客戶端不向服務器發送數據,則主線程一直阻塞,其它客戶端沒法鏈接成功。這就須要處理高併發問題。服務器
服務器高併發處理的三種方式網絡
目錄 makefile的設置都在多進程裏面有介紹 http://www.javashuo.com/article/p-rsygfewi-e.html多線程
多線程的效率明顯高於多進程,並且由於線程共享大部分進程空間,而多進程複製空間,從而下降了系統開銷。併發
客戶端和多進程的用同一個。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 }