zoukankan      html  css  js  c++  java
  • 20155321 第八周课堂实践3

    20155321 第八周课堂实践3

    多进程实现daytime服务器

    • 知识储备

      • daytime协议:daytime服务其实是一个好用的调试工具,它的返回值是当前的日期和时间(字符串格式)。而基于TCP的daytime服务,一旦有连接建立就返回ASCII形式的日期和时间,在传送完后关闭连接,接收到的数据则被忽略。
      • 因为是多进程实现daytime服务器,因此需要用到fork()函数创建进程,而课堂上所实现的daytime服务器每次只是接收一个客户端的请求,这会导致服务器的效率比较低下。因此可以采用多进程并发技术提高服务器的处理效率。
      • 关于fork()函数:调用了fork()函数后,子进程和父进程会继续执行fork()函数后的代码。因为子进程是父进程的副本,所以子进享有父进程的数据空间、堆栈,但是父进程和子进程并不共享这些内存的存储空间。
    • 在基于socket编程的基础上,以下为服务端代码部分

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<string.h>
    #include<ctype.h>
    #include<arpa/inet.h>
    #include<unistd.h>
    #include<errno.h>
    #include <time.h>
    
    #define SERV_PORT 13321
    
    int std_err(const char* name)
    {
        perror(name);
        exit(1);
    }
    
    int main(void)
    {
        int sfd, cfd, ret;
        int len;
        pid_t pid;
        socklen_t clie_len;
    
        char buf[BUFSIZ], clibuf[32];
    
        //创建服务器套节字
        sfd = socket(AF_INET, SOCK_STREAM, 0);
        if(sfd == -1)
            std_err("socket");
    
        //定义地址类型
        struct sockaddr_in serv_addr, clie_addr;
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(SERV_PORT);
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    
        //绑定服务器的IP、端口;
        ret = bind(sfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
        if(ret == -1)
            std_err("bind");
    
        //监听链接服务器的客户数量
        ret = listen(sfd, 3);
        if(ret == -1)
            std_err("listen");
        clie_len = sizeof(clie_addr);
    
        while(1)
        {
            //阻塞等待客户端发起链接请求
            cfd = accept(sfd, (struct sockaddr*)&clie_addr, &clie_len);
    	printf("服务器实现者20155321	");
    	printf("客户端IP:%s
    ",inet_ntoa(clie_addr.sin_addr));	
    	
            time_t t = time(0); 
            char tmp[64]; 
            strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A
    	", localtime(&t) ); 
    
    	//这里无需打印当前时间,应该是客户端打印当前时间
            if(cfd == -1)
                std_err("accept");
    
            pid = fork();
    
            if(pid < 0)
                std_err("fork:");
            else if(pid == 0)
            {
                close(sfd);
                break;
            }
            else        //1.回收子进程,2,关闭不必要的文件描述父 3,继续等待客户端链接,如果有,则继续创建子进程
            {
                send(cfd,tmp,strlen(tmp),0);
        	    close(cfd);
            }
        }
        return 0;
    }
    
    • 在基于socket编程的基础上,以下为客户端代码部分
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<string.h>
    #include<ctype.h>
    #include<arpa/inet.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<signal.h>
    #include<errno.h>
    #include<pthread.h>
    
    #define SERV_PORT 13321
    #define SERV_IP "127.0.0.1"
    #define NUM 3
    
    int std_err(const char* name)
    {
        perror(name);
        exit(1);
    }
    
    int main(void)
    {
        int cfd, ret;
        char buf[BUFSIZ];
        pid_t pid;
    
        int i;
        for(i = 0; i < NUM; i++){
            pid = fork();
            if(pid == 0)
                break;
            else if(pid < 0)
                std_err("fork");
        }
    
        //子进程逻辑
        if(pid == 0)
        {
            //创建套节字
            cfd = socket(AF_INET, SOCK_STREAM, 0);
            if(cfd == -1)
                std_err("socket");
    
            //定义IP , 端口
            struct sockaddr_in clie_addr;
            clie_addr.sin_family = AF_INET;
            clie_addr.sin_port = htons(SERV_PORT);
    
            //转换IP 字符串的地址
            ret = inet_pton(AF_INET, SERV_IP, &clie_addr.sin_addr.s_addr);
            if(ret != 1)
                std_err("inet_pton");
    
            //链接服务器
            ret = connect(cfd, (struct sockaddr*)&clie_addr, sizeof(clie_addr));
            if(ret == -1)
                std_err("connect");
    
    	char buff[256];
    	int nRecv=recv(cfd,buff,256,0);
    	if(nRecv>0)
    	{   
    	    buff[nRecv]='';
    	    printf("当前时间:%s
    ",buff);
    	} 
        }
        //关闭套节字
        close(cfd);
        return 0;
    }
    
    
    • 实验结果截图

    • 实践中遇到的问题与解决办法
      刚开始测试的时候出现了客户端打印的当前时间不正确的情况,如下图所示

      后来发现,是在获取时间的代码部分有错误,因为是多进程,因此获取时间要放在创建了子进程之后,在while循环里,如下所示

      while(1)
        {
            …………
    	
            time_t t = time(0); 
            char tmp[64]; 
            strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A
    	", localtime(&t) ); 
            …………
        }
    

    多线程实现daytime服务器

    • 知识储备

      • 上面是是采用多进程的方法来实现服务器,可以明显看到,这样处理后的主进程的效率提高了许多。但是,创建一个新的进程本身开销还是比较大的,因为这样会消耗很多务器端的资源。因此可以采用多线程的方法来实现服务器端。
      • 通过学习,Linux下创建一个线程也比较方便,调用pthread_create()函数来创建线程即可,其中的一个参数的回调函数,也就是线程本身的执行体函数。
    • 在基于socket编程的基础上,以下为服务端代码部分

    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #include<sys/socket.h>
    #include<sys/types.h>       //pthread_t , pthread_attr_t and so on.
    #include<netinet/in.h>      //structure sockaddr_in
    #include<arpa/inet.h>
    #include<assert.h>
    #include<string.h>         
    #include<unistd.h>          
    #include<ctype.h>
    #include<arpa/inet.h>
    #include <time.h>
    #define SOCK_PORT 9988
    #define BUFFER_LENGTH 1024
    #define MAX_CONN_LIMIT 5    //MAX connection limit
    
    static void Data_handle(void * sock_fd);
    struct sockaddr_in s_addr_in;
    struct sockaddr_in s_addr_client;
    int sockfd;
    
    int main()
    {
        int sockfd_server
        int fd_temp;
        int client_length;
    
        sockfd_server = socket(AF_INET,SOCK_STREAM,0);  //ipv4,TCP
        assert(sockfd_server != -1);
    
        memset(&s_addr_in,0,sizeof(s_addr_in));
        s_addr_in.sin_family = AF_INET;
        s_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);  
        s_addr_in.sin_port = htons(SOCK_PORT);         
    
        fd_temp = bind(sockfd_server,(struct scokaddr*)(&s_addr_in),sizeof(s_addr_in));
    
        if(fd_temp == -1)
        {
            fprintf(stderr,"bind error!
    ");
            exit(1);
        }
    
        fd_temp = listen(sockfd_server,MAX_CONN_LIMIT);
    
        if(fd_temp == -1)
        {
            fprintf(stderr,"listen error!
    ");
            exit(1);
        }
    
       while(1)
        {
            pthread_t thread_id;
            client_length = sizeof(s_addr_client);
    
            sockfd = accept(sockfd_server,(struct sockaddr*)(&s_addr_client),&client_length);
    
    	time_t t = time(0); 
            char tmp[64]; 
            strftime( tmp, sizeof(tmp), "%Y/%m/%d %X %A
    	", localtime(&t) ); 
    	send(sockfd,tmp,strlen(tmp),0);	
    	close(sockfd);	//close a file descriptor.
            if(sockfd == -1)
            {
                fprintf(stderr,"Accept error!
    ");
                continue; 
            }
    
            if(pthread_create(&thread_id,NULL,(void *)(&Data_handle),(void *)(&sockfd)) == -1)
            {
                fprintf(stderr,"pthread_create error!
    ");
                break;  
            }
        }
    
        int ret = shutdown(sockfd_server,SHUT_WR); //shut down the all or part of a full-duplex connection.
        assert(ret != -1);
    
        return 0;
    }
    
    static void Data_handle(void * sock_fd)
    {
        int fd = *((int *)sock_fd);
        int i_recvBytes;
        char data_recv[BUFFER_LENGTH];
       
        printf("服务器实现者20155321	");
        printf("客户端IP:%s
    ",inet_ntoa(s_addr_client.sin_addr));	
        pthread_exit(NULL);   //terminate calling thread!
    }
    
    
    • 在基于socket编程的基础上,以下为客户端代码部分
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<string.h>
    #include<ctype.h>
    #include<arpa/inet.h>
    #include<unistd.h>
    #include<sys/wait.h>
    #include<signal.h>
    #include<errno.h>
    #include<pthread.h>
    
    #define SERV_PORT 13321
    #define SERV_IP "127.0.0.1"
    #define NUM 3
    
    int std_err(const char* name)
    {
        perror(name);
        exit(1);
    }
    
    int main(void)
    {
        int cfd, ret;
        char buf[BUFSIZ];
        pid_t pid;
    
        int i;
    
        //创建套节字
        cfd = socket(AF_INET, SOCK_STREAM, 0);
        if(cfd == -1)
            std_err("socket");
    
        //定义IP , 端口
        struct sockaddr_in clie_addr;
        clie_addr.sin_family = AF_INET;
        clie_addr.sin_port = htons(SERV_PORT);
    
        //转换IP 字符串的地址
        ret = inet_pton(AF_INET, SERV_IP, &clie_addr.sin_addr.s_addr);
        if(ret != 1)
            std_err("inet_pton");
    
        //链接服务器
        ret = connect(cfd, (struct sockaddr*)&clie_addr, sizeof(clie_addr));
        if(ret == -1)
            std_err("connect");
    
        char buff[256];
        int nRecv=recv(cfd,buff,256,0);
        if(nRecv>0)
        {   
            buff[nRecv]='';
            printf("当前时间:%s
    ",buff);
        } 
    }
        //关闭套节字
        close(cfd);
        return 0;
    }
    
    
    • 实验结果截图

    • 实践中遇到的问题与解决办法
      在开始编译server.c文件的时候编译出现了错误,如下图所示:

      后来在网上资料的提示下,才记得要加上-lpthread参数,这样编译就可以顺利通过了

  • 相关阅读:
    skywalking源码改造
    skywalking包覆盖
    skywalking-拦截器实现(2)
    skywalking-拦截器实现(1)
    skywalking-过滤某些不需要被监控的接口
    扩展Spring-data-jpa导致注解@NamedEntityGraphs失效
    Skywalking日志收集功能使用:
    LRU缓存机制(基于LinkedHashMap)
    2020年总结
    Hbase简介
  • 原文地址:https://www.cnblogs.com/rafell/p/7819005.html
Copyright © 2011-2022 走看看