併發伺服器的實現(多程序、多執行緒...)
阿新 • • 發佈:2018-11-01
一、多程序實現併發伺服器
程式碼如下:multiprocess_server.c
/* ============================================================================ Name : TCPServer.c Author : jiangyu Version : date : 2018-10-8 Description : Simple Socket Server ============================================================================ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <arpa/inet.h> #include <sys/socket.h> #include <signal.h> #define MYPORT 8080 int main( int argc , char ** argv ) { signal(SIGCHLD,SIG_IGN);//把子程序的殭屍程序給init程序處理 struct sockaddr_in saddr, caddr; char send_buf[1024]; char recv_buf[1024]; char ipbuf[50]; int sockfd, connfd; int addr_len; pid_t pid; sockfd = socket( AF_INET, SOCK_STREAM, 0 ); memset(&saddr, 0, sizeof(saddr) ); memset( send_buf, 0, sizeof(send_buf) ); memset( recv_buf, 0, sizeof(recv_buf) ); memset( ipbuf, 0, sizeof(ipbuf) ); saddr.sin_family = AF_INET; saddr.sin_port = htons(MYPORT); saddr.sin_addr.s_addr = htonl( INADDR_ANY ); //any address bind( sockfd, (struct sockaddr *)&saddr, 16 ); listen( sockfd, 20 ); printf( "Accepting connections ... \n" ); int n; addr_len = sizeof( caddr ); //迴圈接收 客戶端的連入 while(1) { if((connfd = accept( sockfd, (struct sockaddr*)&caddr, &addr_len ))>0) { printf("new client connect successful!\n"); } //列印新接入客戶端IP、埠 inet_ntop(AF_INET,(void*)&caddr.sin_addr.s_addr,ipbuf,50); //地址格式轉換為數值格式 printf("the client:%s is in\n",ipbuf); //建立一個子程序來接受新客戶端的資訊 pid = fork(); if(pid<0) //fork失敗 { perror("failed fork()"); return -1; } else if(pid == 0) //子程序 { close(sockfd);//關閉從父程序程序來的監聽套接字,因為在子程序用不到,關閉不需要的套接字可節省系統資源,同時可避免父子程序共享這些套接字可能帶來的不可預計的後果 //這個子程序迴圈接收客戶端資訊 while(1) { n = recv( connfd, recv_buf, sizeof(recv_buf), 0 ); if(n<0) { perror("failed recv"); } else if(n == 0) { bzero(ipbuf,50); inet_ntop(AF_INET,(void*)&caddr.sin_addr.s_addr,ipbuf,50); printf("the client:%s is out\n",ipbuf); exit(0); close(connfd); } else{ //子程序處理 inet_ntop(AF_INET,(void*)&caddr.sin_addr.s_addr,ipbuf,50); printf("ip:%s,port:%d\n",ipbuf,ntohs(caddr.sin_port)); printf("Recived %d bytes\n",n); int i; for(i=0;i<n;i++) { printf("%02x\n", recv_buf[i]); } //printf("Recived %d bytes: %s \n",n, recv_buf); send(connfd,recv_buf,n,0); memset( recv_buf, 0, sizeof(recv_buf) ); } } } else if(pid>0) //父程序,用於繼續監測有沒有新的客戶端連入 { close(connfd);//關閉新客戶端返回的套接字,因為在父程序中用不到 continue; } /* printf("please input:\n"); gets(send_buf); send(connfd,send_buf,strlen(send_buf),0); printf("send :%s\n",send_buf); memset( send_buf, 0, sizeof(send_buf) ); n = recv( connfd, recv_buf, sizeof(recv_buf), 0 ); if(n>0) { printf("Recived %d bytes: %s \n",n, recv_buf); send(connfd,recv_buf,n,0); memset( recv_buf, 0, sizeof(recv_buf) ); } */ } close( sockfd ); close( connfd ); return -1; }
二、多執行緒實現併發伺服器
程式碼如下:multipthread_server.c
/* ============================================================================ Name : multipthread_server.c Author : Greein-jy Version : v1.0 date : 2018-10-10 Description : Multipthread Socket Server ============================================================================ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <arpa/inet.h> #include <sys/socket.h> #include <pthread.h> #include <signal.h> #include <sys/select.h> #define MYPORT 8080 #define REQ_FRAME_SIZE 9 //感測器資訊協議幀總長 //感測器資料請求格式 #define HEAD 0xAA #define LEN 0x09 #define OPTION 0x00 #define CMD 0X01 #define DATA1 0x0f //資料域,表示需要請求哪些資料 #define DATA2 0xff //資料域,表示需要請求哪些資料 #define TAIL 0x55 unsigned short CRC_Compute(unsigned char*,unsigned char); int sockfd, new_sock; char req_buf[REQ_FRAME_SIZE] = {0}; unsigned char ipbuf[50]; struct sockaddr_in saddr, caddr; //請求幀建構函式 void req_data_frame() { req_buf[0]=HEAD; req_buf[1]=LEN; req_buf[2]=OPTION; req_buf[3]=CMD; req_buf[4]=DATA1; req_buf[5]=DATA2; unsigned short crc16 = CRC_Compute(req_buf,(unsigned char)6); req_buf[6] = (char)crc16; req_buf[7] = (char)(crc16>>8); req_buf[8]=TAIL; } //執行緒處理函式 void* pthread_handler(void* sock) { unsigned char recv_buf[1024] = {0}; int newsock = *((int*)sock); int n; req_data_frame(); while(1) { fd_set fds; //定義檔案描述符集 struct timeval timeout; timeout.tv_sec = 10; //設定超時時間為10s timeout.tv_usec = 0; FD_ZERO(&fds);//清除檔案描述符集 FD_SET(newsock, &fds);//將檔案描述符加入檔案描述符集中; //傳送資料請求 send(newsock,req_buf,REQ_FRAME_SIZE,0); printf("send req success!\n"); if(select(newsock+1,&fds,NULL,NULL,&timeout) > 0 ) //監聽socket,超時時間為10s { printf("start to receive data...\n"); //接收資料,處理資料 n = recv( newsock, recv_buf, REQ_FRAME_SIZE, 0 ); if(n<0) { perror("failed recv"); } else if(n == 0) { memset( ipbuf, 0, sizeof(ipbuf) ); inet_ntop(AF_INET,(void*)&caddr.sin_addr.s_addr,ipbuf,50); printf("the client:%s is quit\n",ipbuf); close(newsock); break; } else { printf("ip:%s,port:%d\n",ipbuf,ntohs(caddr.sin_port)); printf("Recived %d bytes\n",n); int i; for(i=0;i<n;i++) { printf("%02x", recv_buf[i]); } printf("\n"); printf("Recived %d bytes: %s \n",n, recv_buf); //send(newsock,recv_buf,n,0); memset( recv_buf, 0, sizeof(recv_buf) ); } sleep(5); //延時5s,每5s傳送一次資料請求 } } close(newsock); } int main( int argc , char ** argv ) { int addr_len; pid_t pid; sockfd = socket( AF_INET, SOCK_STREAM, 0 ); memset(&saddr, 0, sizeof(saddr) ); memset( ipbuf, 0, sizeof(ipbuf) ); saddr.sin_family = AF_INET; saddr.sin_port = htons(MYPORT); saddr.sin_addr.s_addr = htonl( INADDR_ANY ); //any address bind( sockfd, (struct sockaddr *)&saddr, 16 ); listen( sockfd, 20 ); printf( "Accepting connections ... \n" ); addr_len = sizeof( caddr ); //迴圈接收 客戶端的連入 while(1) { if((new_sock = accept( sockfd, (struct sockaddr*)&caddr, &addr_len ))>0) { printf("new client connect successful!\n"); //列印新接入客戶端IP、埠 inet_ntop(AF_INET,(void*)&caddr.sin_addr.s_addr,ipbuf,50); //地址格式轉換為數值格式 printf("the client:%s is in\n",ipbuf); } //建立一個執行緒來接收新客戶端的資訊 pthread_t id; pthread_create(&id,NULL,pthread_handler,(void*)&new_sock); /*pthread有兩種狀態:joinable狀態和unjoinable狀態 一個執行緒預設的狀態是joinable,如果執行緒是joinable狀態,當執行緒函式自己返回退出時或pthread_exit時都不會釋放執行緒所佔用堆疊和執行緒描述符(總計8K多)。只有當你呼叫了pthread_join之後這些資源才會被釋放。 若是unjoinable狀態的執行緒,這些資源線上程函式退出時或pthread_exit時自動會被釋放。 unjoinable屬性可以在pthread_create時指定,或線上程建立後線上程中pthread_detach自己, 如:pthread_detach(當前執行緒ID),將狀態改為unjoinable狀態,確保資源的釋放。如果執行緒狀態為 joinable,需要在之後適時呼叫pthread_join. */ pthread_detach(id); } close( sockfd ); return 0; } //CRC校驗函式 unsigned short CRC_Compute(unsigned char* snd,unsigned char len) { auto int i, j; auto unsigned short c,crc=0xFFFF; for(i = 0;i < len; i ++) { c = snd[i] & 0x00FF; crc ^= c; for(j = 0;j < 8; j ++) { if (crc & 0x0001) { crc>>=1; crc^=0xA001; }else crc>>=1; } } return (crc); }