生活随笔
收集整理的這篇文章主要介紹了
Linux网络编程——tcp并发服务器(多线程)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
https://blog.csdn.net/lianghe_work/article/details/46504243
tcp多線程并發(fā)服務(wù)器 多線程服務(wù)器是對多進(jìn)程服務(wù)器的改進(jìn),由于多進(jìn)程服務(wù)器在創(chuàng)建進(jìn)程時要消耗較大的系統(tǒng)資源,所以用線程來取代進(jìn)程,這樣服務(wù)處理程序可以較快的創(chuàng)建。據(jù)統(tǒng)計,創(chuàng)建線程與創(chuàng)建進(jìn)程要快 10100 倍 ,所以又把線程稱為“輕量級”進(jìn)程 。線程與進(jìn)程不同的是:一個進(jìn)程內(nèi)的所有線程共享相同的全局內(nèi)存、全局變量等信息,這種機(jī)制又帶來了同步問題。 tcp多線程并發(fā)服務(wù)器框架:
我們在使用多線程并發(fā)服務(wù)器時,直接使用以上框架,我們僅僅修改client_fun()里面的內(nèi)容。 代碼示例: #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 ; } 運(yùn)行結(jié)果: 注意 :1.上面pthread_create()函數(shù)的最后一個參數(shù)是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); } 因?yàn)関oid *是 4個字節(jié), 而connfd為int類型也是 4個字節(jié), 故可以傳值。如果connfd為char、short,上面?zhèn)髦稻蜁鲥e
2.上面pthread_create()函數(shù)的最后一個參數(shù)是可以傳地址嗎? 可以,但會對服務(wù)器造成不可預(yù)知的問題
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); } 原因:假如有多個客戶端要連接這個服務(wù)器,正常的情況下,一個客戶端連接對應(yīng)一個 connfd,相互之間獨(dú)立不受影響,但是,假如多個客戶端同時連接這個服務(wù)器,A 客戶端的連接套接字為 connfd,服務(wù)器正在用這個 connfd 處理數(shù)據(jù),還沒有處理完,突然來了一個 B 客戶端,accept()之后又生成一個 connfd, 因?yàn)槭堑刂穫鬟f, A 客戶端的連接套接字也變成 B 這個了,這樣的話,服務(wù)器肯定不能再為 A 客戶端服務(wù)器了
2.如果我們想將多個參數(shù) 傳給線程函數(shù),我們首先考慮到就是結(jié)構(gòu)體參數(shù) ,而這時傳值 是行不通的,只能傳遞地址 。
這時候,我們就需要考慮多任務(wù)的 互斥或同步 問題了,這里通過 互斥鎖 來解決這個問題,確保這個結(jié)構(gòu)體參數(shù) 值被一個臨時變量保存過后,才允許修改。
#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 ; }
運(yùn)行結(jié)果:
注意 :這種用互斥鎖對服務(wù)器的運(yùn)行效率有致命的影響
總結(jié)
以上是生活随笔 為你收集整理的Linux网络编程——tcp并发服务器(多线程) 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。