1. 程式人生 > >併發伺服器的實現(多程序、多執行緒...)

併發伺服器的實現(多程序、多執行緒...)

一、多程序實現併發伺服器

程式碼如下: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);
}