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参数,这样编译就可以顺利通过了

原文地址:https://www.cnblogs.com/rafell/p/7819005.html