嵌入式学习-网络-Day04-2.服务器模型

2.1循环服务器模型

同一时刻只能响应一个客户端的请求

socket();
bind();
listen();
while(1)
{
    accept();
    while(1)
    {
        process();//处理
    }
    close();
}

2.2并发服务器模型

同一时刻能响应多个客户端的请求,常用模型:多进程模型、多线程模型、IO多路复用

多进程模型

每来一个客户端连接,开一个子进程来专门处理客户端的数据,实现简单,但是系统开销相对较大,更推荐使用线程模型。

伪代码:

socket();
bind();
listen();
while(1)
{
    accept();
    if(fork()==0)//子进程
    {
         while(1)
        {
            process();//处理
        }
        close();
        exit();
    }
    else
    {
    }
    
}

多进程特点 :

  1. fork之前的代码被复制,但是不会重新执行一遍;fork之后的代码被复制,并且被再次执行一遍
  2. fork之后两个进程相互独立,子进程拷贝了父进程的所有代码,但是内存空间独立
  3. fork之前打开的文件,fork之后拿到的是同一个文件描述符,操作的是同一个文件指针

例:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    printf("hello world\n");
    int a = 100;
    int fd = open("./poll.c", O_RDONLY);

    pid_t pid = fork();
    if(pid < 0 )
    {
        perror("fork err:");
        return -1;
    }
    else if(pid  == 0)
    {
        char buf[32]="";
        a = 200;
        printf("child:a = %d\n",a);
        read(fd,buf,10);
        printf("buf:%s\n",buf);
        printf("childfd:%d\n",fd);
    }
    else
    {
        sleep(1);
        char buf[32]="";
        printf("father:a=%d\n",a);
        read(fd,buf,10);
        printf("fbuf:%s\n",buf);
        printf("fatherfd:%d\n",fd);
    }
    printf("---------%d\n",a);
    return 0;
}

注意:收到客户端消息后,打印下是来自哪个客户端的数据(来电显示)

使用SIGCHLD来处理子进程结束的信号,信号函数中回收进程资源。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#define N 128
void handler(int arg)
{
    waitpid(-1,NULL,WNOHANG);
}
int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("please input %s  port\n", argv[0]);
        return -1;
    }

    // 1.创建套接字         协议族    类型    协议
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err:");
        return -1;
    }
    // 2.填充结构体(ipv4)
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;            // 协议族ipv4
    saddr.sin_port = htons(atoi(argv[1])); // 端口号(网络字节序)
    // saddr.sin_addr.s_addr = inet_addr(argv[1]); // ip地址(网络字节序)
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");

    socklen_t len = sizeof(caddr);
    // 3.绑定
    int ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
    if (ret < 0)
    {
        perror("bind err:");
        return -1;
    }

    // 4.监听
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err");
        return -1;
    }

    signal(SIGCHLD,handler);

    while (1)
    {
        // 5.等待连接
        // int acceptfd = accept(sockfd, NULL, NULL);
        int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("accpet err:");
            return -1;
        }
         printf("acceptfd = %d\n", acceptfd);
        printf("client ip :%s,port:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
        pid_t pid =fork();
        if(pid < 0)
        {
            perror("fork err");
            return -1;
        }
        else if(pid == 0)
        {
            close(sockfd);
            char buf[N]={};
            int ret;
            while(1)
            {
                ret  = recv(acceptfd,buf,N,0);
                if(ret  < 0 )
                {
                    perror("recv err");
                    return -1;
                }
                else if(ret  == 0)
                {
                    printf("client exit\n");
                    break;
                }
                else
                {
                    printf("buf:%s\n",buf);
                }
            }
            exit(-1);
        }
        close(acceptfd);
    }
    close(sockfd);
    return 0;
}

多线程模型

每来一个客户端连接,开一个子线程来专门处理客户端的数据,实现简单,占用资源较少,属于使用比较广泛的模型:

伪代码:

socket();
bind();
listen()
while(1)
{
    accept();
    pthread_create();
    
}

多线程实现并发服务器:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

#define N 128

void * mythread(void * arg)
{
    int  acceptfd = *(int *)arg;
    char buf[N]={};
    int ret;
    while(1)
    {   
        ret = recv(acceptfd,buf,N,0);
        if(ret < 0)
        {
            perror("recv err:");
            break;
        }
        else if(ret == 0)
        {
            printf("client exit\n");
            close(acceptfd);
            break;
        }
        else
        {
            printf("%d said:%s\n",acceptfd,buf);
        }

    }
    pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
     if (argc != 2)
    {
        printf("please input %s  port\n", argv[0]);
        return -1;
    }

    // 1.创建套接字         协议族    类型    协议
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err:");
        return -1;
    }
    // 2.填充结构体(ipv4)
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;            // 协议族ipv4
    saddr.sin_port = htons(atoi(argv[1])); // 端口号(网络字节序)
    // saddr.sin_addr.s_addr = inet_addr(argv[1]); // ip地址(网络字节序)
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");

    socklen_t len = sizeof(caddr);
    // 3.绑定
    int ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
    if (ret < 0)
    {
        perror("bind err:");
        return -1;
    }

    // 4.监听
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err");
        return -1;
    }

    while (1)
    {
        // 5.等待连接
        // int acceptfd = accept(sockfd, NULL, NULL);
        int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("accpet err:");
            return -1;
        }
        printf("acceptfd = %d\n", acceptfd);
        printf("client ip :%s,port:%d\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
        pthread_t tid;

        pthread_create(&tid,NULL,mythread,&acceptfd);
        pthread_detach(tid);
    }
    close(sockfd);
    return 0;
}

IO多路复用模型

借助select、poll、epoll机制,将新连接的客户端描述符增加到描述符表中,只需要一个线程即可处理所有的客户端连接,在嵌入式开发中应用广泛,不过代码写起来稍显繁琐。

上一篇:[ 问题解决篇 ] 解决windows虚拟机安装vmtools报错-winserver2012安装vmtools及安装KB2919355补丁 (附离线工具)-1 前言


下一篇:C++:继承~派生类以及衍生的多继承与菱形继承问题