生活随笔
收集整理的這篇文章主要介紹了
Linux网络编程——tcp并发服务器(多线程)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
https://blog.csdn.net/lianghe_work/article/details/46504243
tcp多線程并發服務器 多線程服務器是對多進程服務器的改進,由于多進程服務器在創建進程時要消耗較大的系統資源,所以用線程來取代進程,這樣服務處理程序可以較快的創建。據統計,創建線程與創建進程要快 10100 倍 ,所以又把線程稱為“輕量級”進程 。線程與進程不同的是:一個進程內的所有線程共享相同的全局內存、全局變量等信息,這種機制又帶來了同步問題。 tcp多線程并發服務器框架:
我們在使用多線程并發服務器時,直接使用以上框架,我們僅僅修改client_fun()里面的內容。 代碼示例: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> void *client_fun(void *arg){ int recv_len = 0 ; char recv_buf[1024 ] = "" ; int connfd = (int )arg; while ((recv_len = recv(connfd, recv_buf, sizeof (recv_buf), 0 )) > 0 ) { printf("recv_buf: %s\n" , recv_buf); send(connfd, recv_buf, recv_len, 0 ); } printf("client closed!\n" ); close(connfd); return NULL; } int main (int argc, char *argv[] ){ int sockfd = 0 ; int connfd = 0 ; int err_log = 0 ; struct sockaddr_in my_addr; unsigned short port = 8080 ; pthread_t thread_id; printf("TCP Server Started at port %d!\n" , port); sockfd = socket(AF_INET, SOCK_STREAM, 0 ); if (sockfd < 0 ) { perror("socket error" ); exit(-1 ); } bzero(&my_addr, sizeof (my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(port); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); printf("Binding server to port %d\n" , port); err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof (my_addr)); if (err_log != 0 ) { perror("bind" ); close(sockfd); exit(-1 ); } err_log = listen(sockfd, 10 ); if ( err_log != 0 ) { perror("listen" ); close(sockfd); exit(-1 ); } printf("Waiting client...\n" ); while (1 ) { char cli_ip[INET_ADDRSTRLEN] = "" ; struct sockaddr_in client_addr; socklen_t cliaddr_len = sizeof (client_addr); connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len); if (connfd < 0 ) { perror("accept this time" ); continue ; } inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN); printf("----------------------------------------------\n" ); printf("client ip=%s,port=%d\n" , cli_ip,ntohs(client_addr.sin_port)); if (connfd > 0 ) { pthread_create(&thread_id, NULL, (void *)client_fun, (void *)connfd); pthread_detach(thread_id); } } close(sockfd); return 0 ; } 運行結果: 注意 :1.上面pthread_create()函數的最后一個參數是void *類型, 為啥可以傳值connfd ? while (1 ){ int connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len); pthread_create(&thread_id, NULL, (void *)client_fun, (void *)connfd); pthread_detach(thread_id); } 因為void *是 4個字節, 而connfd為int類型也是 4個字節, 故可以傳值。如果connfd為char、short,上面傳值就會出錯
2.上面pthread_create()函數的最后一個參數是可以傳地址嗎? 可以,但會對服務器造成不可預知的問題
while (1 ){ int connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len); pthread_create(&thread_id, NULL, (void *)client_fun, (void *)&connfd); pthread_detach(thread_id); } 原因:假如有多個客戶端要連接這個服務器,正常的情況下,一個客戶端連接對應一個 connfd,相互之間獨立不受影響,但是,假如多個客戶端同時連接這個服務器,A 客戶端的連接套接字為 connfd,服務器正在用這個 connfd 處理數據,還沒有處理完,突然來了一個 B 客戶端,accept()之后又生成一個 connfd, 因為是地址傳遞, A 客戶端的連接套接字也變成 B 這個了,這樣的話,服務器肯定不能再為 A 客戶端服務器了
2.如果我們想將多個參數 傳給線程函數,我們首先考慮到就是結構體參數 ,而這時傳值 是行不通的,只能傳遞地址 。
這時候,我們就需要考慮多任務的 互斥或同步 問題了,這里通過 互斥鎖 來解決這個問題,確保這個結構體參數 值被一個臨時變量保存過后,才允許修改。
#include <pthread.h> pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); pthread_mutex_lock(&mutex); int connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len); pthread_create(&thread_id, NULL, (void *)client_process, (void *)&connfd); void *client_process(void *arg) { int connfd = *(int *)arg; pthread_mutex_unlock(&mutex); return NULL; }
示例代碼:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> pthread_mutex_t mutex; void *client_process(void *arg){ int recv_len = 0 ; char recv_buf[1024 ] = "" ; int connfd = *(int *)arg; pthread_mutex_unlock(&mutex); while ((recv_len = recv(connfd, recv_buf, sizeof (recv_buf), 0 )) > 0 ) { printf("recv_buf: %s\n" , recv_buf); send(connfd, recv_buf, recv_len, 0 ); } printf("client closed!\n" ); close(connfd); return NULL; } int main (int argc, char *argv[] ){ int sockfd = 0 ; int connfd = 0 ; int err_log = 0 ; struct sockaddr_in my_addr; unsigned short port = 8080 ; pthread_t thread_id; pthread_mutex_init(&mutex, NULL); printf("TCP Server Started at port %d!\n" , port); sockfd = socket(AF_INET, SOCK_STREAM, 0 ); if (sockfd < 0 ) { perror("socket error" ); exit(-1 ); } bzero(&my_addr, sizeof (my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(port); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); printf("Binding server to port %d\n" , port); err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof (my_addr)); if (err_log != 0 ) { perror("bind" ); close(sockfd); exit(-1 ); } err_log = listen(sockfd, 10 ); if ( err_log != 0 ) { perror("listen" ); close(sockfd); exit(-1 ); } printf("Waiting client...\n" ); while (1 ) { char cli_ip[INET_ADDRSTRLEN] = "" ; struct sockaddr_in client_addr; socklen_t cliaddr_len = sizeof (client_addr); pthread_mutex_lock(&mutex); connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len); if (connfd < 0 ) { perror("accept this time" ); continue ; } inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN); printf("----------------------------------------------\n" ); printf("client ip=%s,port=%d\n" , cli_ip,ntohs(client_addr.sin_port)); if (connfd > 0 ) { pthread_create(&thread_id, NULL, (void *)client_process, (void *)&connfd); pthread_detach(thread_id); } } close(sockfd); return 0 ; }
運行結果:
注意 :這種用互斥鎖對服務器的運行效率有致命的影響
總結
以上是生活随笔 為你收集整理的Linux网络编程——tcp并发服务器(多线程) 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。