linux 系統實現多個進程監聽同一個端口(會有驚羣)

linux 系統實現多個進程監聽同一個端口

轉自: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

相關文章
相關標籤/搜索