嵌入式学习Day37---Linux软件编程---网络编程

目录

一、多路复用IO

1.1.select

1.缺点

1.2.poll

1.函数接口

2.缺点

3.实例

1.3.epoll

1.函数接口

2.优点

3.实例

1.4.多线程实现

1.5.多进程实现 

二、总结


一、多路复用IO

1.1.select

1.缺点

    1.select监听的文件描述符集合是一个数组,有上限(1024个)
    2.select监听的文件描述符集合在应用层,内核层监听事件后需要传递给用户层带来资源开销
    3.select需要用户手动查找产生事件的文件描述符
    4.select只能工作在水平触发模式(低速模式)而无法工作在边沿触发模式(高速模式)

1.2.poll

1.函数接口

 poll()

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    功能:
        监听文件描述符集合,工作方式类似于select 
    参数:
        fds:文件描述符集合首地址
        nfds:文件描述符集合的数组的长度 
        timeout:超时时间,单位毫秒,-1表示永久等待
    返回值:
        成功返回产生事件文件描述符个数 
        失败返回-1 
        超时仍然没有产生的事件返回0 
    
    struct pollfd {
        int   fd;         /* file descriptor */
        short events;     /* requested events */
        short revents;    /* returned events */
    };

2.缺点

        1.poll监听的文件描述符集合在应用层,内核层监听事件后需要传递给用户层带来资源开销
        2.poll需要用户手动查找产生事件的文件描述符
        3.poll只能工作在水平触发模式(低速模式)而无法工作在边沿触发模式(高速模式)

3.实例

使用poll实现TCP的并发模型,多个客户端可以并发访问服务端

client.c

#include "../head.h"

int CreateTcpConnection(const char *pip, int port)
{
    int sockfd = 0;
    int ret = 0;
    struct sockaddr_in seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(port);
    seraddr.sin_addr.s_addr = inet_addr(pip);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        return -1;
    }

    ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if (-1 == ret)
    {
        return -1;
    }

    return sockfd;
}

int HandleConnection(int sockfd)
{
    char tmpbuff[4096] = {0};
    static int cnt = 0;
    ssize_t nsize = 0;

    sprintf(tmpbuff, "hello world --- %d", cnt);
    nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);
    if (-1 == nsize)
    {
        return -1;
    }
    cnt++;

    memset(tmpbuff, 0, sizeof(tmpbuff));
    nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);  
    if (-1 == nsize)
    {
        return -1;
    }
    else if (0 == nsize)
    {
        return 0;
    }
    
    printf("RECV:%s\n", tmpbuff);

    return nsize;
}

int main(void)
{
    int sockfd = 0;
    int ret = 0;

    sockfd = CreateTcpConnection(SER_IP, SER_PORT);
    if (-1 == sockfd)
    {
        printf("连接服务器异常\n");
        return -1;
    }

    while (1)
    {
        ret = HandleConnection(sockfd);
        if (-1 == ret)
        {
            printf("连接出错!\n");
            break;
        }
        else if (0 == ret)
        {
            printf("连接关闭\n");
            break;
        }

        sleep(1);
    }
    
    close(sockfd);

    return 0;
}

serve.c(核心实现)

#include "../head.h"

#define MAX_POLL_FDNUM      10000

int CreateListenSocket(const char *pip, int port)
{
    int sockfd = 0;
    int ret = 0;
    struct sockaddr_in seraddr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        return -1;
    }

    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(port);
    seraddr.sin_addr.s_addr = inet_addr(pip);
    ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if (-1 == ret)
    {
        return -1;
    }

    ret = listen(sockfd, 10);
    if (-1 == ret)
    {
        return -1;
    }

    return sockfd;
}

int HandleConnection(int confd)
{
    char tmpbuff[4096] = {0};
    ssize_t nsize = 0;

    nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
    if (-1 == nsize)
    {
        return -1;
    }
    else if (0 == nsize)
    {
        return 0;
    }

    printf("RECV:%s\n", tmpbuff);

    sprintf(tmpbuff, "%s --- echo", tmpbuff);

    nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
    if (-1 == nsize)
    {
        return -1;
    }

    return nsize;
}

//初始化pollfd数组
int InitPollFd(struct pollfd *pfds, int maxlen)
{
    int i = 0;
    for (i = 0; i < maxlen; i++)
    {
        pfds[i].fd = -1;
    }

    return 0;
}

//添加监听套接字到pollfd数组中
int AddPollDFd(struct pollfd *pfds, int maxlen, int fd, short env)
{
    int i = 0;
    for (i = 0; i < maxlen; i++ )
    {
        if (pfds[i].fd == -1)
        {
            pfds[i].fd = fd;
            pfds[i].events = env;
            break;
        }
    }

}

//删除pollfd数组中监听套接字
int DelPollFd(struct pollfd *pfds, int maxlen, int tmpfd)
{
    int i = 0;
    for (i = 0; i < maxlen; i++)
    {
        if (pfds[i].fd == tmpfd)
        {
            pfds[i].fd = -1;
            pfds[i].events = 0;
            break;
        }
    }

    return 0;
}
int main(void)
{
    int sockfd = 0;
    int confd = 0;
    int ret = 0;
    int nready = 0;
    struct pollfd fds[MAX_POLL_FDNUM];
    int i = 0;

    sockfd = CreateListenSocket(SER_IP, SER_PORT);
    if (-1 == sockfd)
    {
        printf("创建监听套接字失败\n");
        return -1;
    }

    InitPollFd(fds, MAX_POLL_FDNUM);
    AddPollDFd(fds, MAX_POLL_FDNUM, sockfd, POLLIN);

    while (1)
    {

        //1.检测套接字
        nready = poll(fds, MAX_POLL_FDNUM, -1);
        if (nready == -1)
        {
            perror("poll");
            return -1;
        }

        //2.遍历fds数组,检测事件发生的套接字
        for (i = 0; i < MAX_POLL_FDNUM; i++)
        {
            if (fds[i].fd == -1)
            {
                continue;
            }

            if (fds[i].events & fds[i].revents && fds[i].fd == sockfd)
            {
                confd = accept(sockfd, NULL, NULL);
                if (confd == -1)
                {
                    printf("连接失败!\n");
                    DelPollFd(fds, MAX_POLL_FDNUM, sockfd);
                    close(sockfd);

                }
                AddPollDFd(fds, MAX_POLL_FDNUM, confd, POLLIN);
            }else if (fds[i].events & fds[i].revents && fds[i].fd != sockfd)
            {
                ret = HandleConnection(fds[i].fd);
                if (-1 == ret)
                {
                    printf("接收异常!\n");
                    DelPollFd(fds, MAX_POLL_FDNUM, fds[i].fd);    
                    close(fds[i].fd); 
                    continue;
                }else if (0 == ret)
                {
                    printf("关闭连接!\n");
                    DelPollFd(fds, MAX_POLL_FDNUM, fds[i].fd);  
                    close(fds[i].fd);
                    continue;
                }
            }

        }

    }

    return 0;
}

1.3.epoll

1.函数接口

epoll_creat()

    int epoll_create(int size);
      功能:
        在内核层创建一张epoll监听的事件表
      参数:
        size:监听的事件表大小
      返回值: 
        成功返回新的文件描述符
        失败返回-1

 epoll_ctl()

     int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
      功能:
        管理内核中epoll事件表
      参数:
        epfd:文件描述符 
        op: EPOLL_CTL_ADD   添加客户端 
            EPOLL_CTL_MOD   修改客户端 
            EPOLL_CTL_DEL   删除客户端
        fd:文件描述符 
        event:事件结构体
      返回值:
        成功返回0 
        失败返回-1

epoll_wait() 

     int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
      功能:
        监听epfd对应的事件表中是否有事件发生 
      参数: 
        epfd:文件描述符 
        events:存放产生事件的数组空间首地址
        maxevents:最多存放数组元素个数 
        timeout:超时时间,单位毫秒,-1表示永久等待
      返回值:
        成功返回实际发生事件的个数
        失败返回-1 
        超时仍然没有产生事件返回0

2.优点


        1.epoll没有文件描述符上限限制 
        2.epoll监听的事件表在内核层,内核监听事件不需要操作用户层空间提高效率
        3.epoll会获得产生事件的文件描述符,不需要用户查找
        4.epoll可以工作在边沿触发模式(高速模式),提高效率

3.实例

使用epoll实现TCP的并发模型,多个客户端可以并发访问服务端

client.c

#include "../head.h"

int CreateTcpConnection(const char *pip, int port)
{
    int sockfd = 0;
    int ret = 0;
    struct sockaddr_in seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(port);
    seraddr.sin_addr.s_addr = inet_addr(pip);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        return -1;
    }

    ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if (-1 == ret)
    {
        return -1;
    }

    return sockfd;
}

int HandleConnection(int sockfd)
{
    char tmpbuff[4096] = {0};
    static int cnt = 0;
    ssize_t nsize = 0;

    sprintf(tmpbuff, "hello world --- %d", cnt);
    nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);
    if (-1 == nsize)
    {
        return -1;
    }
    cnt++;

    memset(tmpbuff, 0, sizeof(tmpbuff));
    nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);  
    if (-1 == nsize)
    {
        return -1;
    }
    else if (0 == nsize)
    {
        return 0;
    }
    
    printf("RECV:%s\n", tmpbuff);

    return nsize;
}

int main(void)
{
    int sockfd = 0;
    int ret = 0;

    sockfd = CreateTcpConnection(SER_IP, SER_PORT);
    if (-1 == sockfd)
    {
        printf("连接服务器异常\n");
        return -1;
    }

    while (1)
    {
        ret = HandleConnection(sockfd);
        if (-1 == ret)
        {
            printf("连接出错!\n");
            break;
        }
        else if (0 == ret)
        {
            printf("连接关闭\n");
            break;
        }

        sleep(1);
    }
    
    close(sockfd);

    return 0;
}

 serve.c(核心实现)

#include "../head.h"

#define MAX_POLL_FDNUM      10000

int CreateListenSocket(const char *pip, int port)
{
    int sockfd = 0;
    int ret = 0;
    struct sockaddr_in seraddr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        return -1;
    }

    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(port);
    seraddr.sin_addr.s_addr = INADDR_ANY;
    ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if (-1 == ret)
    {
        return -1;
    }

    ret = listen(sockfd, 10);
    if (-1 == ret)
    {
        return -1;
    }

    return sockfd;
}

int HandleConnection(int confd)
{
    char tmpbuff[4096] = {0};
    ssize_t nsize = 0;

    nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
    if (-1 == nsize)
    {
        return -1;
    }
    else if (0 == nsize)
    {
        return 0;
    }

    printf("RECV:%s\n", tmpbuff);

    sprintf(tmpbuff, "%s --- echo", tmpbuff);

    nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
    if (-1 == nsize)
    {
        return -1;
    }

    return nsize;
}

int AddFd(int epfd, int fd, uint32_t tmpenvent)
{
    struct epoll_event env;
    int ret = 0;

    env.events = tmpenvent;
    env.data.fd = fd;
    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &env);
    if (-1 == ret)
    {
        perror("fail to epoll_ctl");
        return -1;
    }
    
    return ret;
}

int DelFd(int epfd, int fd)
{
    int ret = 0;

    ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
    if (-1 == ret)
    {
        perror("fail to epoll_ctl");
        return -1;
    }

    return ret;
}

int main(void)
{
    int sockfd = 0;
    int confd = 0;
    int ret = 0;
    int epfd = 0;
    int nready = 0;
    int i = 0;
    struct epoll_event retenv[MAX_POLL_FDNUM];

    sockfd = CreateListenSocket(SER_IP, SER_PORT);
    if (-1 == sockfd)
    {
        printf("创建监听套接字失败\n");
        return -1;
    }

    epfd = epoll_create(MAX_POLL_FDNUM);
    if (-1 == epfd)
    {
        perror("fail to epoll_create");
        return -1;
    }

    ret = AddFd(epfd, sockfd, EPOLLIN);
    if (-1 == ret)
    {
        printf("添加套接字失败\n");
        return -1;
    }

    while (1)
    {
        nready = epoll_wait(epfd, retenv, MAX_POLL_FDNUM, -1);
        if (-1 == nready)
        {
            perror("fail to epoll_wait");
            return -1;
        }

        for (i = 0; i < nready; i++)
        {
            if (retenv[i].data.fd == sockfd)
            {
                confd = accept(sockfd, NULL, NULL);
                if (-1 == confd)
                {
                    printf("处理连接失败\n");
                    DelFd(epfd, sockfd);
                    close(sockfd);
                    continue;
                } 

                AddFd(epfd, confd, EPOLLIN);
            }
            else if (retenv[i].data.fd != sockfd)
            {
                ret = HandleConnection(retenv[i].data.fd);
                if (-1 == ret)
                {
                    printf("接收异常!\n");
                    DelFd(epfd, retenv[i].data.fd);
                    close(retenv[i].data.fd);
                    continue;
                }
                else if (0 == ret)
                {
                    printf("关闭连接!\n");                   
                    DelFd(epfd, retenv[i].data.fd);
                    close(retenv[i].data.fd);
                    continue;
                }
            }
        }   
    }

    close(epfd);

    return 0;
}

1.4.多线程实现

注意:需要将线程属性分离,自动回收线程空间,防止使用回收函数接口发生阻塞

1.5.多进程实现 

二、总结

        2024年8月21日,学习的第37天。今天学完了所有的多路复用IO,其中呢效率最高的是epoll。 也可以使用前面学习的线程和进程来实现,不过这个的客户端个数很局限,但也可以实现。不妨我们下去使用UDP结合多进程来实现,多人的在线聊天室。

        加油!

  • 16
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值