Linux學習之網路程式設計(多程序併發伺服器)
阿新 • • 發佈:2018-11-15
言之者無罪,聞之者足以戒。 - “詩序”
上面我們所說過的通訊都是一個伺服器一個客戶端之間的通訊,下面我們來交流一下多程序併發伺服器的相關知識
邏輯上就是這個樣子的,就是一個伺服器多個客戶端進行資料的傳輸。
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; }