轉自:http://blog.csdn.net/youjun9007228198/article/details/19946129linux
經過 fork 建立子進程的方式能夠實現,其餘狀況下不行。
當鏈接到來時,子進程、父進程均可以 accept, 這就是著名的「驚羣」問題(thundering herd problem)。服務器
驚羣現象多線程
在該模型下(多個子進程同時共享監聽套接字)便可實現服務器併發處理客戶端的鏈接。這裏要注意的是,計算機三次握手建立鏈接是不須要服務進程參數的,而服務進程僅僅要作的事調用accept將已創建的鏈接構建對應的鏈接套接字connfd(可參考 http://blog.csdn.net/ordeder/article/details/21551567)。多個服務進程同時阻塞在accept等待監聽套接字已創建鏈接的信息,那麼當內核在該監聽套接字上創建一個鏈接,那麼將同時喚起這些處於accept阻塞的服務進程,從而致使「驚羣現象」的產生,喚起多餘的進程間影響服務器的性能(僅有一個服務進程accept成功,其餘進程被喚起後沒搶到「鏈接」而再次進入休眠)。併發
一直疑惑一個應用app如何才能以多進程,多線程的方式運行。對於多線程可能很好理解,咱們只要在進程中啓用多線程的模式便可。也就是來一個請求,咱們就用函數pthread_create()啓用一個線程便可。這樣咱們的應用就能夠在單進程,多線程的模式下工做。app
但咱們知道一個應用app一般工做在多進程,多線程的模式下,它的效率是最高的。那麼咱們如何才能作到多進程模式呢?經驗告訴咱們,若是屢次啓動一個進程會報錯:「Address already in use!"。這是因爲bind函數致使的,因爲該端口號已經被監聽了。socket
其實咱們只要在綁定端口號(bind函數)以後,監聽端口號以前(listen函數),用fork()函數生成子進程,這樣子進程就能夠克隆父進程,達到監聽同一個端口的目的。好了,廢話很少說,咱們看一下具體代碼。函數
server.coop
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<unistd.h> #define oops(m) {perror(m); exit(1);} int main(){ int sock_id; struct sockaddr_in saddr; sock_id = socket(PF_INET, SOCK_STREAM, 0); saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); saddr.sin_port = htons(9988); saddr.sin_family = AF_INET; int ret = bind(sock_id, (struct sockaddr *) &saddr, sizeof(saddr));//綁定IP地址和端口 if(ret == -1) oops("bind error");//若是返回-1,則綁定失敗,通常爲「Address alreay in use」 int i; for(i = 0; i < 6; i++){//連續建立六個子進程 int pid = fork(); if(pid == 0) break; } listen(sock_id, 1); while(1){ int sock = accept(sock_id, NULL, 0); char buf[128]; int readnum; readnum = read(sock, buf, 127); buf[readnum] = '\0'; printf("pid=%d, mesg: %s\n", getpid(), buf); fflush(stdout); close(sock); } return 1; }
client.c性能
#include<stdio.h> #include<stdlib.h> #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<netinet/in.h> int main(){ int i=0; struct sockaddr_in saddr; saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); saddr.sin_port = htons(9988); saddr.sin_family = AF_INET; int addlen = sizeof(saddr); for(; i < 30; i++){ int sock=socket(AF_INET, SOCK_STREAM, 0); connect(sock, (struct sockaddr *) &saddr, addlen); write(sock, "Hello Server!", 13); } }
運行結果:spa