1. 程式人生 > >Linux學習之網路程式設計(多程序併發伺服器)

Linux學習之網路程式設計(多程序併發伺服器)

言之者無罪,聞之者足以戒。 - “詩序”

上面我們所說過的通訊都是一個伺服器一個客戶端之間的通訊,下面我們來交流一下多程序併發伺服器的相關知識

邏輯上就是這個樣子的,就是一個伺服器多個客戶端進行資料的傳輸。

1、傳送資料的函式:

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

第一個引數:是由socket函式返回的套接字描述符

第二個引數:要傳送的資料

第三個引數:要傳送的資料的大小

第四個引數:一個標誌位(具體的取值下面會給出一個表格)

2、接收資料的函式:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

第一個引數:是由socket函式返回的套接字描述符

第二個引數:接收資料的儲存位置

第三個引數:接收的位元組數

第四個引數:一個標誌位(具體的取值下面會給出一個表格)

flags 說明 recv send
MSG_DONTROUTE 不查路由表   yes
MSG_DONTWAIT 本操作不阻塞 yes yes
MSG_OOB 傳送或接收帶外資料 yes yes
MSG_WAITALL 檢視外來訊息 yes  
MSG_PEEK 等待所有資料 yes  

下面看一下多程序併發伺服器的流程圖:

命令: ps aux|grep server
檢視server的數量

下面來看一下程式:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>


#include <unistd.h>
#include <fcntl.h>
#include <signal.h>


//最大連線數
#define MAX_LISTEN_QUE 5
//埠號
#define SERV_PORT 8888
//陣列的最大位元組數
#define MAX_BUFFER_SIZE 100
//建立套接字函式
int mz_ipv4_tcp_create_socket(void)
{
	int listenfd, sockfd, opt = 1;
	struct sockaddr_in server, client;
	socklen_t len;
	int timep;
	int ret;
    //建立套接字
	listenfd = socket(AF_INET, SOCK_STREAM, 0);//IPv4,全雙工通訊
	if(listenfd < 0){
		perror("Create socket fail.");
		return -1;
	} 
    //設定地址的重用
	if((ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) < 0){
		perror("Error, set socket reuse addr failed");  
		return -1;
	}
    //初始化清零操作
	bzero(&server, sizeof(server));
	server.sin_family = AF_INET;//IPv4
	server.sin_port   = htons(SERV_PORT);//埠號
	server.sin_addr.s_addr  = htonl(INADDR_ANY);//設定為所有都可連線
	//獲得結構體地址長度
	len = sizeof(struct sockaddr);
    //套接字與伺服器介面繫結
	if(bind(listenfd, (struct sockaddr *)&server, len)<0){
			  perror("bind error.");
		return -1;
	}
	//設定最大連線數  
	listen(listenfd, MAX_LISTEN_QUE);
    //返回套接字
	return listenfd;
}
//資料處理函式
int mz_process_data(int sockfd)
{
	int bytes;
	char buf[MAX_BUFFER_SIZE], read_buf[MAX_BUFFER_SIZE];
    //清空陣列
	memset(buf, 0x00, MAX_BUFFER_SIZE);

	while(1){
         //清空陣列
	    memset(read_buf, 0x00, MAX_BUFFER_SIZE);
		//從通訊套接字中接收資料,並 存到read_buf中
		bytes = recv(sockfd, read_buf, 100, 0);
		if(bytes < 0){
			printf("read err.\n");
			return -1;
		}
		if(bytes == 0){
			printf("client connection closed.\n");
			return 0;
		}
		printf("Bytes:%d\n", strlen(read_buf));//列印read_buf的長度
		printf("read_buf: %x %x %x \n", read_buf[0], read_buf[1], read_buf[2]);//列印read_buf的前三個的內容
        //將讀取到的內容傳送到通訊套接字中
		send(sockfd, read_buf, bytes, 0);
        //比較接收的資料是不是'q'
		if(!strcmp(read_buf, "q")){
			return 0;
		}
         //比較接收的資料是不是'c'
		if(!strcmp(read_buf, "c")){
			printf("i love you.\n");
			sprintf(read_buf, "%s","i love you.\n" );
			send(sockfd, read_buf, 12, 0);
		}
	}
    //關閉通訊套接字
	close(sockfd);
	return 0;
}
//訊號處理函式
int mz_process_signal(int signo)
{
    //判斷訊號的型別
	switch(signo){
		case SIGCHLD:
			printf("dddddddddd\n");
			while(waitpid(-1, NULL, WNOHANG)>0);	
		break;
	}
}
//建立訊號處理的控制代碼
int mz_set_signal_handler(void)
{
	struct sigaction act, oact;
	act.sa_handler = (void *)mz_process_signal;//將訊號處理函式的控制代碼賦值給handler
	sigemptyset(&act.sa_mask);//清空訊號集
	act.sa_flags = 0;
	act.sa_flags |= SA_RESTART;
    //定義要處理的訊號和處理方法
	if(sigaction(SIGCHLD, &act, &oact) < 0){
		return -1;
		}
		return 0;
}

int main(int argc, char *argv[])
{
	int listenfd, sockfd;
	struct sockaddr_in server, client;
	socklen_t len;
	int bytes =0 ;

	//呼叫建立訊號控制代碼函式
	mz_set_signal_handler();
	
	len = sizeof(struct sockaddr);
	//呼叫套接字處理函式
	listenfd = mz_ipv4_tcp_create_socket();
	
	while(1){
        //伺服器等待客戶端連線
		sockfd = accept(listenfd, (struct sockaddr *)&client, &len);
		if(sockfd < 0){
			perror("accept error.");
			return -1;
		}

		printf("sockfd=%d\n", sockfd);
        //列印客戶端地址和埠號
		printf("IP: 0x%x, Port:%d\n", ntohl(client.sin_addr.s_addr), ntohs(client.sin_port));
        //建立子程序
		if(fork() == 0){
            //關閉套接字
			close(listenfd);
            //呼叫資料處理函式
			mz_process_data(sockfd);
			exit(0);
		}
           //關閉通訊套接字
		close(sockfd);
	  

	}
	return 0;
}