生活随笔
收集整理的這篇文章主要介紹了
linux网络编程之并发服务器的三种实现模型 (超级经典)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
轉(zhuǎn)載 : http://blog.csdn.net/tennysonsky/article/details/45671215
服務器設計技術(shù)有很多,按使用的協(xié)議來分有 TCP 服務器和 UDP 服務器,按處理方式來分有循環(huán)服務器 和并發(fā)服務器 。
循環(huán)服務器與并發(fā)服務器模型 在網(wǎng)絡程序里面,一般來說都是許多客戶對應一個服務器(多對一),為了處理客戶的請求,對服務端的程序就提出了特殊的要求。 目前最常用的服務器模型有:
·循環(huán)服務器:服務器在同一時刻只能響應一個 客戶端的請求
·并發(fā)服務器:服務器在同一時刻可以響應多個 客戶端的請求
UDP 循環(huán)服務器的實現(xiàn)方法 UDP 循環(huán)服務器每次從套接字上讀取一個客戶端的請求 -> 處理 -> 然后將結(jié)果返回給客戶機。 因為 UDP 是非面向連接的,沒有一個客戶端可以老是占住服務端。只要處理過程不是死循環(huán),或者耗時不是很長,服務器對于每一個客戶機的請求在某種程度上來說是能夠滿足。
UDP 循環(huán)服務器模型為 :
[objc] view plaincopy
socket(...);??? bind(...);????? ?? while (1 )??{?? ????recvfrom(...);??? ????process(...);???? ????sendto(...);????? }??
示例代碼如下:
[cpp] view plaincopy
#include?<stdio.h> ??#include?<stdlib.h> ??#include?<string.h> ??#include?<unistd.h> ??#include?<sys/socket.h> ??#include?<netinet/in.h> ??#include?<arpa/inet.h> ???? int ?main(int ?argc,?char ?*argv[])??{?? ????unsigned?short ?port?=?8080;??? ?? ????int ?sockfd;?? ????sockfd?=?socket(AF_INET,?SOCK_DGRAM,?0);??? ????if (sockfd?<?0)?? ????{?? ????????perror("socket" );?? ????????exit(-1);?? ????}?? ?????? ?????? ????struct ?sockaddr_in?my_addr;?? ????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);?? ?????? ?????? ????int ?err_log;?? ????err_log?=?bind(sockfd,?(struct ?sockaddr*)&my_addr,?sizeof (my_addr));?? ????if (err_log?!=?0)?? ????{?? ????????perror("bind" );?? ????????close(sockfd);???????? ????????exit(-1);?? ????}?? ?????? ????printf("receive?data...\n" );?? ????while (1)?? ????{?? ????????int ?recv_len;?? ????????char ?recv_buf[512]?=?{0};?? ????????struct ?sockaddr_in?client_addr;?? ????????char ?cli_ip[INET_ADDRSTRLEN]?=?"" ;?? ????????socklen_t?cliaddr_len?=?sizeof (client_addr);?? ?????????? ?????????? ????????recv_len?=?recvfrom(sockfd,?recv_buf,?sizeof (recv_buf),?0,?(struct ?sockaddr*)&client_addr,?&cliaddr_len);?? ?????????? ?????????? ????????inet_ntop(AF_INET,?&client_addr.sin_addr,?cli_ip,?INET_ADDRSTRLEN);?? ????????printf("\nip:%s?,port:%d\n" ,cli_ip,?ntohs(client_addr.sin_port));??? ????????printf("data(%d):%s\n" ,recv_len,recv_buf);???? ?????????? ?????????? ????????sendto(sockfd,?recv_buf,?recv_len,?0,?(struct ?sockaddr*)&client_addr,?cliaddr_len);?? ????}?? ?????? ????close(sockfd);?? ?????? ????return ?0;?? }?? 運行結(jié)果如下:
TCP 循環(huán)服務器的實現(xiàn)方法 TCP 循環(huán)服務器接受一個客戶端的連接,然后處理,完成了這個客戶的所有請求后,斷開連接。TCP 循環(huán)服務器一次只能處理一個客戶端的請求,只有在這個客戶的所有請求滿足后,服務器才可以繼續(xù)后面的請求 。如果 有一個客戶端占住服務器不放時,其它的客戶機都不能工作了,因此 ,TCP 服務器一般很少用循環(huán)服務器模型的。
TCP循環(huán)服務器模型為:
[objc] view plaincopy
socket(...);?? bind(...);?? listen(...);?? ?? while (1 )??{?? ????accept(...);?? ????process(...);?? ????close(...);?? }??
示例代碼如下:
[cpp] view plaincopy
#include?<stdio.h> ??#include?<stdlib.h> ??#include?<string.h>??????????????????????? ??#include?<unistd.h> ??#include?<sys/socket.h> ??#include?<netinet/in.h> ??#include?<arpa/inet.h> ???????????????? int ?main(int ?argc,?char ?*argv[])??{?? ????unsigned?short ?port?=?8080;??????? ?? ?????? ????int ?sockfd?=?socket(AF_INET,?SOCK_STREAM,?0);????? ????if (sockfd?<?0)?? ????{?? ????????perror("socket" );?? ????????exit(-1);?? ????}?? ?????? ?????? ????struct ?sockaddr_in?my_addr;?? ????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);??? ?????? ?????? ????int ?err_log?=?bind(sockfd,?(struct ?sockaddr*)&my_addr,?sizeof (my_addr));?? ????if (?err_log?!=?0)?? ????{?? ????????perror("binding" );?? ????????close(sockfd);???????? ????????exit(-1);?? ????}?? ?????? ?????? ????err_log?=?listen(sockfd,?10);??? ????if (err_log?!=?0)?? ????{?? ????????perror("listen" );?? ????????close(sockfd);???????? ????????exit(-1);?? ????}????? ?????? ????printf("listen?client?@port=%d...\n" ,port);?? ?? ????while (1)?? ????{????? ?????? ????????struct ?sockaddr_in?client_addr;?????????? ????????char ?cli_ip[INET_ADDRSTRLEN]?=?"" ;??????? ????????socklen_t?cliaddr_len?=?sizeof (client_addr);?????? ?????????? ?????????? ????????int ?connfd;?? ????????connfd?=?accept(sockfd,?(struct ?sockaddr*)&client_addr,?&cliaddr_len);????????? ????????if (connfd?<?0)?? ????????{?? ????????????perror("accept" );?? ????????????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));?? ?????????? ?????????? ????????char ?recv_buf[512]?=?{0};?? ????????int ?len?=??recv(connfd,?recv_buf,?sizeof (recv_buf),?0);?? ?????????? ?????????? ????????printf("\nrecv?data:\n" );?? ????????printf("%s\n" ,recv_buf);?? ?????????? ?????????? ????????send(connfd,?recv_buf,?len,?0);?? ?????????? ????????close(connfd);??????? ????????printf("client?closed!\n" );?? ????}?? ?????? ????close(sockfd);??????????? ?????? ????return ?0;?? }?? 運行結(jié)果如下:
三種并發(fā)服務器實現(xiàn)方法 一個好的服務器,一般都是并發(fā)服務器(同一時刻可以響應多個客戶端的請求)。并發(fā)服務器設計技術(shù)一般有 :多進程服務器、多線程服務器、I/O復用服務器等。
多進程并發(fā)服務器 在 Linux 環(huán)境下多進程的應用很多,其中最主要的就是網(wǎng)絡/客戶服務器。多進程服務器是當客戶有請求時,服務器用一個子進程來處理客戶請求。父進程繼續(xù)等待其它客戶的請求。這種方法的優(yōu)點 是當客戶有請求時,服務器能及時處理客戶,特別是在客戶服務器交互系統(tǒng)中。對于一個 TCP 服務器,客戶與服務器的連接可能并不馬上關(guān)閉,可能會等到客戶提交某些數(shù)據(jù)后再關(guān)閉,這段時間服務器端的進程會阻塞,所以這時操作系統(tǒng)可能調(diào)度其它客戶服務進程,這比起循環(huán)服務器大大提高了服務性能 。
TCP多進程并發(fā)服務器 TCP 并發(fā)服務器的思想是每一個客戶機的請求并不由服務器直接處理,而是由服務器創(chuàng)建一個子進程來處理。
示例代碼如下:
[cpp] view plaincopy
#include?<stdio.h> ??#include?<stdlib.h> ??#include?<string.h>??????????????????????? ??#include?<unistd.h> ??#include?<sys/socket.h> ??#include?<netinet/in.h> ??#include?<arpa/inet.h>???? ???? int ?main(int ?argc,?char ?*argv[])??{?? ????unsigned?short ?port?=?8080;??????? ?? ?????? ????int ?sockfd?=?socket(AF_INET,?SOCK_STREAM,?0);????? ????if (sockfd?<?0)?? ????{?? ????????perror("socket" );?? ????????exit(-1);?? ????}?? ?????? ?????? ????struct ?sockaddr_in?my_addr;?? ????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);??? ?????? ?????? ????int ?err_log?=?bind(sockfd,?(struct ?sockaddr*)&my_addr,?sizeof (my_addr));?? ????if (?err_log?!=?0)?? ????{?? ????????perror("binding" );?? ????????close(sockfd);???????? ????????exit(-1);?? ????}?? ?????? ?????? ????err_log?=?listen(sockfd,?10);??? ????if (err_log?!=?0)?? ????{?? ????????perror("listen" );?? ????????close(sockfd);???????? ????????exit(-1);?? ????}?? ?????? ????while (1)??? ????{?? ?????????? ????????char ?cli_ip[INET_ADDRSTRLEN]?=?{0};?? ????????struct ?sockaddr_in?client_addr;?? ????????socklen_t?cliaddr_len?=?sizeof (client_addr);?? ?????????? ?????????? ????????int ?connfd?=?accept(sockfd,?(struct ?sockaddr*)&client_addr,?&cliaddr_len);?? ????????if (connfd?<?0)?? ????????{?? ????????????perror("accept" );?? ????????????close(sockfd);?? ????????????exit(-1);?? ????????}?? ?????????? ????????pid_t?pid?=?fork();?? ????????if (pid?<?0){?? ????????????perror("fork" );?? ????????????_exit(-1);?? ????????}else ?if (0?==?pid){??? ????????????? ? ? ?? ????????????close(sockfd);????? ?????????? ????????????char ?recv_buf[1024]?=?{0};?? ????????????int ?recv_len?=?0;?? ?????????????? ?????????????? ????????????memset(cli_ip,?0,?sizeof (cli_ip));??? ????????????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));?? ?????????????? ?????????????? ????????????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);?????? ?????????????? ????????????exit(0);?? ????????}else ?if (pid?>?0){????? ?????????? ????????????close(connfd);?????? ?????????????? ????????}?? ????}?? ?????? ????close(sockfd);?? ?????? ????return ?0;?? }?? 運行結(jié)果如下:
多線程服務器 多線程服務器是對多進程的服務器的改進,由于多進程服務器在創(chuàng)建進程時要消耗較大的系統(tǒng)資源,所以用線程來取代進程,這樣服務處理程序可以較快的創(chuàng)建。據(jù)統(tǒng)計,創(chuàng)建線程與創(chuàng)建進程要快 10100 倍 ,所以又把線程稱為“輕量級”進程 。線程與進程不同的是 :一個進程內(nèi)的所有線程共享相同的全局內(nèi)存、全局變量等信息,這種機制又帶來了同步問題 。
以下是多線程服務器模板:
示例代碼如下:
[cpp] view plaincopy
#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_process(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_process,?(void ?*)connfd);???? ????????????pthread_detach(thread_id);??? ????????}?? ????}?? ?????? ????close(sockfd);?? ?????? ????return ?0;?? }?? 運行結(jié)果如下:
注意 ,上面例子給線程傳參有很大的局限性,最簡單的一種情況,如果我們需要給線程傳多個參數(shù),這時候我們需要結(jié)構(gòu)體傳參,這種值傳遞編譯都通不過,這里之所以能夠這么值傳遞,是因為 , int 長度時 4 個字節(jié), void * 長度也是 4 個字節(jié)。
[cpp] view plaincopy
int ?connfd?=?accept(sockfd,?(struct ?sockaddr*)&client_addr,?&cliaddr_len);??pthread_create(&thread_id,?NULL,?(void ?*)client_process,?(void ?*)connfd);??? 如果考慮類型匹配的話,應該是這么傳參,pthread_create()最后一個參數(shù)應該傳地址( &connfd ),而不是值:
[cpp] view plaincopy
int ?connfd?=?accept(sockfd,?(struct ?sockaddr*)&client_addr,?&cliaddr_len);??pthread_create(&thread_id,?NULL,?(void ?*)client_process,?(void ?*)&connfd);??? 但是,如果按地址傳遞的話,又會有這么一個問題,假如有多個客戶端要連接這個服務器,正常的情況下,一個客戶端連接對應一個 connfd,相互之間獨立不受影響,但是,假如多個客戶端同時連接這個服務器,A 客戶端的連接套接字為 connfd,服務器正在用這個 connfd 處理數(shù)據(jù),還沒有處理完,突然來了一個 B 客戶端,accept()之后又生成一個 connfd, 因為是地址傳遞, A 客戶端的連接套接字也變成 B 這個了,這樣的話,服務器肯定不能再為 A 客戶端服務器了,這時候,我們就需要考慮多任務的互斥或同步 問題了,這里通過互斥鎖來解決這個問題,確保這個connfd值被一個臨時變量保存過后,才允許修改 。
[cpp] view plaincopy
#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;?? }?? 修改的完整代碼如下:
[cpp] view plaincopy
#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;?? }??
I/O復用服務器 I/O 復用技術(shù)是為了解決進程或線程阻塞到某個 I/O 系統(tǒng)調(diào)用而出現(xiàn)的技術(shù),使進程不阻塞于某個特定的 I/O 系統(tǒng)調(diào)用。它也可用于并發(fā)服務器的設計,常用函數(shù) select() 或 epoll() 來實現(xiàn)。詳情,請看《select、poll、epoll的區(qū)別使用》。 [objc] view plaincopy
socket(...);??? bind(...);????? listen(...);??? ?? while (1 )??{?? ????if (select(...)?>?0 )??? ????{?? ????????if (FD_ISSET(...)>0 )??? ????????{?? ????????????accpet(...);?? ????????????process(...);?? ????????}?? ????}?? ????close(...);??? }??
示例代碼如下:
[cpp] view plaincopy
#include?<stdio.h>? ??#include?<unistd.h> ??#include?<stdlib.h> ??#include?<errno.h> ??#include?<string.h> ??#include?<sys/socket.h> ??#include?<sys/types.h> ??#include?<netinet/in.h> ??#include?<arpa/inet.h> ??#include?<sys/select.h> ???? #define?SERV_PORT?8080 ??#define?LIST?20????????????????//服務器最大接受連接 ??#define?MAX_FD?10??????????????//FD_SET支持描述符數(shù)量 ???? ?? int ?main(int ?argc,?char ?*argv[])??{?? ????int ?sockfd;?? ????int ?err;?? ????int ?i;?? ????int ?connfd;?? ????int ?fd_all[MAX_FD];??? ?????? ?????? ????fd_set?fd_read;?????? ????fd_set?fd_select;???? ?? ????struct ?timeval?timeout;??????????? ????struct ?timeval?timeout_select;???? ?????? ????struct ?sockaddr_in?serv_addr;????? ????struct ?sockaddr_in?cli_addr;?????? ????socklen_t?serv_len;?? ????socklen_t?cli_len;?? ?????? ?????? ????timeout.tv_sec?=?10;?? ????timeout.tv_usec?=?0;?? ?????? ?????? ????sockfd?=?socket(AF_INET,?SOCK_STREAM,?0);?? ????if (sockfd?<?0)?? ????{?? ????????perror("fail?to?socket" );?? ????????exit(1);?? ????}?? ?????? ?????? ????memset(&serv_addr,?0,?sizeof (serv_addr));?? ????serv_addr.sin_family?=?AF_INET;??????????? ????serv_addr.sin_port?=?htons(SERV_PORT);???? ????serv_addr.sin_addr.s_addr?=?htonl(INADDR_ANY);??? ?? ?? ????serv_len?=?sizeof (serv_addr);?? ?????? ?????? ????err?=?bind(sockfd,?(struct ?sockaddr?*)&serv_addr,?serv_len);?? ????if (err?<?0)?? ????{?? ????????perror("fail?to?bind" );?? ????????exit(1);?? ????}?? ?? ?????? ????err?=?listen(sockfd,?LIST);?? ????if (err?<?0)?? ????{?? ????????perror("fail?to?listen" );?? ????????exit(1);?? ????}?? ?????? ?????? ????memset(&fd_all,?-1,?sizeof (fd_all));?? ?? ????fd_all[0]?=?sockfd;????? ?????? ????FD_ZERO(&fd_read);???? ????FD_SET(sockfd,?&fd_read);???? ?? ????int ?maxfd;?? ????maxfd?=?fd_all[0];???? ?????? ????while (1){?? ?????? ?????????? ????????fd_select?=?fd_read;?? ????????timeout_select?=?timeout;?? ?????????? ?????????? ?????????? ????????err?=?select(maxfd+1,?&fd_select,?NULL,?NULL,?NULL);?? ?????????? ????????if (err?<?0)?? ????????{?? ????????????????perror("fail?to?select" );?? ????????????????exit(1);?? ????????}?? ?? ????????if (err?==?0){?? ????????????printf("timeout\n" );?? ????????}?? ?????????? ?????????? ????????if (?FD_ISSET(sockfd,?&fd_select)?){?? ?????????????? ????????????cli_len?=?sizeof (cli_addr);?? ?????????????? ?????????????? ????????????connfd?=?accept(sockfd,?(struct ?sockaddr?*)&cli_addr,?&cli_len);?? ????????????if (connfd?<?0)?? ????????????{?? ????????????????perror("fail?to?accept" );?? ????????????????exit(1);?? ????????????}?? ?????????????? ?????????????? ????????????char ?cli_ip[INET_ADDRSTRLEN]?=?{0};?? ????????????inet_ntop(AF_INET,?&cli_addr.sin_addr,?cli_ip,?INET_ADDRSTRLEN);?? ????????????printf("----------------------------------------------\n" );?? ????????????printf("client?ip=%s,port=%d\n" ,?cli_ip,ntohs(cli_addr.sin_port));?? ?????????????? ?????????????? ????????????for (i=0;?i?<?MAX_FD;?i++){?? ????????????????if (fd_all[i]?!=?-1){?? ????????????????????continue ;?? ????????????????}else {?? ????????????????????fd_all[i]?=?connfd;?? ????????????????????printf("client?fd_all[%d]?join\n" ,?i);?? ????????????????????break ;?? ????????????????}?? ????????????}?? ?????????????? ????????????FD_SET(connfd,?&fd_read);?? ?????????????? ????????????if (maxfd?<?connfd)?? ????????????{?? ????????????????maxfd?=?connfd;???? ????????????}?? ?????????? ????????}?? ?????????? ?????????? ????????for (i=1;?i?<?maxfd;?i++){?? ????????????if (FD_ISSET(fd_all[i],?&fd_select)){?? ????????????????printf("fd_all[%d]?is?ok\n" ,?i);?? ?????????????????? ????????????????char ?buf[1024]={0};???? ????????????????int ?num?=?read(fd_all[i],?buf,?1024);?? ????????????????if (num?>?0){?? ?? ?????????????????????? ????????????????????printf("receive?buf?from?client?fd_all[%d]?is:?%s\n" ,?i,?buf);?? ?????????????????????? ?????????????????????? ????????????????????num?=?write(fd_all[i],?buf,?num);?? ????????????????????if (num?<?0){?? ????????????????????????perror("fail?to?write?" );?? ????????????????????????exit(1);?? ????????????????????}else {?? ?????????????????????????? ????????????????????}?? ?????????????????????? ?????????????????????? ????????????????}else ?if (0?==?num){??? ?????????????????????? ?????????????????????? ????????????????????printf("client:fd_all[%d]?exit\n" ,?i);?? ????????????????????FD_CLR(fd_all[i],?&fd_read);?? ????????????????????close(fd_all[i]);?? ????????????????????fd_all[i]?=?-1;?? ?????????????????????? ????????????????????continue ;?? ????????????????}?? ?????????????????? ????????????}else ?{?? ?????????????????? ????????????}?? ????????}?? ?????? ????}?? ?????? ????return ?0;?? }?? 運行結(jié)果如下:
本教程示例代碼下載請點此處。
參考于:http://blog.chinaunix.net
總結(jié)
以上是生活随笔 為你收集整理的linux网络编程之并发服务器的三种实现模型 (超级经典) 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。