1. 程式人生 > >Linux 網路程式設計 全解(四)--------多程序併發伺服器和多執行緒併發伺服器

Linux 網路程式設計 全解(四)--------多程序併發伺服器和多執行緒併發伺服器

寫在前面:這個系列也是停滯了20多天了,從今天開始再次步入正軌,以後每個週末都會陸陸續續的更新,這個系列預計完結的時間還會在大約一個月左右,今天靜下心來多整理幾篇。QQ:993650814

正文:

一、多程序併發伺服器

    設計思路:當有新的客戶端連線到伺服器時,伺服器會呼叫accept函式與客戶端建立連線,並建立子程序與連線上來的客戶端進行通訊。

    程式碼如下:

#include "stdio.h"
#include <sys/types.h>        
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
 #include <sys/wait.h>

#define SER_PORT (9999)

void do_sigchild(int param)
{
	printf("%s\n",__FUNCTION__);
	while(waitpid(0,NULL,WNOHANG)>0);
	
}

int main(void)
{
	int lfd = -1;
	int cfd = -1;
	struct sockaddr_in ser_addr;
	struct sockaddr_in cli_addr;
	pid_t pid = -1;
	char buf[1024] = {0x0};
	int read_size = 0;
	struct sigaction newact;
	
	
	lfd = socket(AF_INET, SOCK_STREAM, 0);
	
	memset(&ser_addr,0,sizeof(struct sockaddr_in));
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_port = htons(SER_PORT);
	ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	bind(lfd,(struct sockaddr *)&ser_addr,sizeof(ser_addr));
	
	memset(&newact,0,sizeof(struct sigaction));
	newact.sa_handler = do_sigchild;
	newact.sa_flags = 0;
	sigemptyset(&newact.sa_mask);
	
	//避免出現殭屍程序的情況:指定父程序回收子程序
	//sigaction(SIGCHLD, &newact,NULL);
	
	listen(lfd, 128);

	while(1)
	{
		socklen_t cli_addr_len = sizeof(cli_addr);
		cfd = accept(lfd, (struct sockaddr *)&cli_addr, &cli_addr_len);
		
		printf("client connect server\n");

		pid = fork();
		
		if(0 == pid)
		{
			//子程序
			close(lfd);
			while(1)
			{
				read_size = read(cfd, buf, sizeof(buf));
				if(read_size == 0)
				{
					printf("client has been closed\n");
					
					break;
				}
				printf("read content:%s\n",buf);
				
				memset(buf,0,sizeof(buf));
				
			}	
			
			printf("child pcb return \n");
			close(cfd);
			
			return 0;		
			
		}else if(pid > 0)
		{
			//父程序
			close(cfd);
			
			continue;
			
			
		}else
		{
			printf("fork error\n");
			
		}
		
		
		
	}
	
	
	close(lfd);
	
	return 0;
}


  測試結果:

二、多執行緒併發伺服器

    設計思路:多執行緒伺服器跟多程序伺服器的思路其實一樣,當有client發生連線時,伺服器會呼叫accept跟client發生連線,並建立子執行緒來跟client通訊。

    程式碼如下:

#include "stdio.h"
#include <sys/types.h>          
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <string.h>


#define SERVER_PORT (8888)
typedef struct
{
	struct sockaddr_in cli_addr;
	int cfd;
}cliInfo_t;



void *task(void * arg)
{
	cliInfo_t * pCliInfo = (cliInfo_t *)arg;
	char ipAddr[10] = {0};
	int cli_port = 0;
	int read_size = 0;
	char buf[1024] = {0};
	
	inet_ntop(AF_INET, &(pCliInfo ->cli_addr.sin_addr), ipAddr, sizeof(ipAddr));
	printf("client ip addr : %s\n",ipAddr);
	
	cli_port =  ntohs(pCliInfo ->cli_addr.sin_port);
	printf("client port : %d\n",cli_port);

	//子執行緒分離,防止產生殭屍執行緒
	pthread_detach(pthread_self());
	
	while(1)
	{
		read_size = read(pCliInfo ->cfd, buf, sizeof(buf));
		
		if(read_size == 0)
		{
			printf("client closed\n");
			
			break;
		}
		
		printf("receive data from client:%s",buf);
		
		memset(buf,0,sizeof(buf));
	}
	
	close(pCliInfo ->cfd);
	
	return NULL;
}


int main(void)
{
	int lfd = -1;
	int cfd = -1;
	struct sockaddr_in ser_sock_addr, cli_sock_addr;
	socklen_t cli_addr_len = sizeof(cli_sock_addr);
	pthread_t tid;
	cliInfo_t cliInfo[100];
	int i = 0;
	int ret = -1;
	
	lfd = socket(AF_INET,SOCK_STREAM,0);
	
	ser_sock_addr.sin_family = AF_INET;
	ser_sock_addr.sin_port = htons(SERVER_PORT);
	ser_sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	ret = bind(lfd,  (struct sockaddr *)&ser_sock_addr,sizeof(ser_sock_addr));
	if(ret < 0)
	{
		printf("bind error \n");
		
		return -1;
	}

	listen(lfd, 30);
	
	while(1)
	{
		cfd = accept(lfd, (struct sockaddr *)&cli_sock_addr, &cli_addr_len);
		
		cliInfo[i].cli_addr = cli_sock_addr;
		cliInfo[i].cfd = cfd;
		
		//當有客戶端跟伺服器發生連線時,建立子執行緒跟客戶端資料通訊
	    pthread_create(&tid, NULL,task, (void *)&cliInfo[i]);
		
		i++;
		
	}

	
	return 0;
}



 測試結果:

 現象其實跟多程序伺服器的現象是一樣的。