您的位置:首页 > 科技 > IT业 > 江苏官网建设公司_全国疫情最新的消息今天_太原关键词优化服务_一个具体网站的seo优化

江苏官网建设公司_全国疫情最新的消息今天_太原关键词优化服务_一个具体网站的seo优化

2025/1/4 13:15:58 来源:https://blog.csdn.net/luoqing418/article/details/144139207  浏览:    关键词:江苏官网建设公司_全国疫情最新的消息今天_太原关键词优化服务_一个具体网站的seo优化
江苏官网建设公司_全国疫情最新的消息今天_太原关键词优化服务_一个具体网站的seo优化

1.  IO多路复用

1.1poll

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
   参数:
   struct pollfd *fds
     关心的文件描述符数组struct pollfd fds[N];
   nfds:个数
   timeout: 超时检测
    毫秒级的:如果填1000,1秒
     如果-1,阻塞 struct pollfd {
     int   fd;         /* 检测的文件描述符 */
     short events;     /* 检测事件 */
     short revents;    /* 调用poll函数返回填充的事件,poll函数一旦返回,将对应事件自动填充结构体这个成员。只需要判断这个成员的值就可以确定是否产生事件 */
 };
 事件:     POLLIN :读事件
           POLLOUT : 写事件
           POLLERR:异常事件

流程

select

poll

1.建立一个文件描述符的表

fd_set线性表

struct pollfd fds[n]结构体数组

2.将关心的文件描述符加到表中

FD_SET(fd,&readfds)

结构体内容填充fds[m].fd= fd

fds[m].events=POLLIN

3. 然后调用一个函数。 select / poll

4. 当这些文件描述符中的一个或多个已准备好进行I/O操作的时候

该函数才返回(阻塞)

select

poll

5.判断

FD_ISSET

revents==POLLIN

6.相关操作

特点:

1. 优化文件描述符个数的限制;(根据poll函数第一个参数来定,如果监听的事件为1个,则结构体数组元素个数为1,如果想监听100个,那么这个结构体数组的元素个数就为100,由程序员自己来决定)

2.  poll被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低

3.  poll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可

1.2epoll

特点:

1.  监听的最大的文件描述符没有个数限制(理论上,取决与你自己的系统)

2.  异步I/O,Epoll当有事件产生被唤醒之后,文件描述符主动调用callback(回调函数)函数直接拿到唤醒的文件描述符,不需要轮询,效率高

3.  epoll不需要重新构造文件描述符表,只需要从用户空间向内核空间拷贝一次数据即可.

2.  服务器模型

2.1循环服务器模型

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

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

2.2并发服务器模型

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

多进程模型

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

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

多进程特点 :

1.  fork之前的代码被复制,但是不会重新执行一遍;fork之后的代码被复制,并且被再次执行一遍

2.  fork之后两个进程相互独立,子进程拷贝了父进程的所有代码,但是内存空间独立

3.  fork之前打开的文件,fork之后拿到的是同一个文件描述符,操作的是同一个文件指针

多进程实现并发服务器:

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

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

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>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;
    }
    printf("socket success\n");
    // 2.填充结构体
    struct sockaddr_in addr, caddr;
    addr.sin_family = AF_INET;            // 协议族
    addr.sin_port = htons(atoi(argv[1])); // 端口号(网络字节序)
    // addr.sin_addr.s_addr = inet_addr(argv[1]); // ip地址(32位无符号整数)
    addr.sin_addr.s_addr = inet_addr("192.168.50.201");    socklen_t len = sizeof(caddr);    // 3.绑定
    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind success\n");    // 4.监听    if (listen(sockfd, 5) < 0)
    {
        perror("listen err");
        return -1;
    }    signal(SIGCHLD, handler);
    while (1)
    {
        // 5.等待客户端链接,产生用于通信的文件描述符
        int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("accept err");
            return -1;
        }
        printf("acceptfd = %d \n", acceptfd);
        // 打印客户端ip和端口号
        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);
            while (1) // 连接成功后循环接收信息
            {
                // 6.接受数据
                char buf[128] = {};
                int ret = recv(acceptfd, buf, 128, 0);
                if (ret < 0)
                {
                    perror("recv err");
                    return -1;
                }
                else if (ret == 0)
                {
                    printf("client exit\n");
                    break;
                }
                printf("recv :%s\n", buf);
            }
            // 7.关闭文件描述符
            close(acceptfd);
            exit(0);
        }
    }
    close(sockfd);    return 0;
}

多线程模型

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

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

多线程实现并发服务器:

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>void *mythread(void *arg)
{
    int acceptfd = *(int *)arg;
    while (1) // 连接成功后循环接收信息
    {
        // 6.接受数据
        char buf[128] = {};
        int ret = recv(acceptfd, buf, 128, 0);
        if (ret < 0)
        {
            perror("recv err");
            return NULL;
        }
        else if (ret == 0)
        {
            printf("client exit\n");
            break;
        }
        printf("recv :%s\n", buf);
    }
    // 7.关闭文件描述符
    close(acceptfd);
    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;
    }
    printf("socket success\n");
    // 2.填充结构体
    struct sockaddr_in addr, caddr;
    addr.sin_family = AF_INET;            // 协议族
    addr.sin_port = htons(atoi(argv[1])); // 端口号(网络字节序)
    // addr.sin_addr.s_addr = inet_addr(argv[1]); // ip地址(32位无符号整数)
    addr.sin_addr.s_addr = inet_addr("192.168.50.201");    socklen_t len = sizeof(caddr);    // 3.绑定
    if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind success\n");    // 4.监听    if (listen(sockfd, 5) < 0)
    {
        perror("listen err");
        return -1;
    }
    while (1)
    {
        // 5.等待客户端链接,产生用于通信的文件描述符
        int acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("accept err");
            return -1;
        }
        printf("acceptfd = %d \n", acceptfd);
        // 打印客户端ip和端口号
        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机制,将新连接的客户端描述符增加到描述符表中,只需要一个线程即可处理所有的客户端连接,在嵌入式开发中应用广泛,不过代码写起来稍显繁琐。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com