2023.07.29 驱动开发DAY6

通过epoll实现一个并发服务器

服务器

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

#define ERR_MSG(msg)               \
do {                               \
    printf("LINE: %d\n", __LINE__);\
    perror(msg);                   \
} while(0)

#define PORT 6666
#define IP "127.0.0.1"

int main(int argc,const char * argv[])
{
    //创建流式套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("socket create success\n");

    //允许端口快速复用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    {
        ERR_MSG("setsockopt");
        return -1;
    }
    printf("setsockopt success\n");

    //填充地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORT);
    sin.sin_addr.s_addr = inet_addr(IP);

    //将IP地址和端口号绑定到指定的套接字文件描述符上
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
    {
        ERR_MSG("bind");
        return -1;
    }
    printf("bind success\n");
    
    //将通讯套接字设置为被动监听状态
    if(listen(sfd, 128) < 0)
    {
        ERR_MSG("listen");
        return -1;
    }
    printf("listen success\n");

    // 创建epoll句柄
    int epfd = epoll_create(1);
    if(epfd < 0)
    {
        printf("epoll_create filed\n");
        return -1;
    }

    // 添加准备就绪事件进入epoll;
    struct epoll_event event;
    struct epoll_event events[10]; //存放就绪事件描述符的数组
    event.events = EPOLLIN; // 读事件
    event.data.fd = sfd;
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &event) < 0)
    {
        printf("epoll_ctl add filed\n");
    }

    //监听事件是否发生
    struct sockaddr_in cin;        //存储客户端信息
    socklen_t len = sizeof(cin);
    struct sockaddr_in savecin[1024];
    int ret, res;
    char buf[128] = {};
    while(1)
    {
        //如果成功,ret接收返回的事件个数,把就绪的事件放在events数组中
        ret = epoll_wait(epfd, events, 10, -1);
        if(ret < 0)
        {
            printf("epoll_wait filed\n");
            return -1;
        }

        //循环遍历数组,做事件处理
        for(int i=0; i<ret; i++)
        {
            if(events[i].events & EPOLLIN)
            {
                if(events[i].data.fd == sfd)
                {
                    //触发客户端连接事件
                    int newfd = accept(sfd, (struct sockaddr*)&cin, &len);
                    if(newfd < 0)
                    {
                        ERR_MSG("accpet");
                        return -1;
                    }
                    printf("[%s:%d] 客户端连接成功 newfd=%d\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
                    savecin[newfd] = cin;

                    //添加新连接客户端进入epoll;
                    event.events = EPOLLIN; // 读事件
                    event.data.fd = newfd;
                    if(epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &event) < 0)
                    {
                        printf("epoll_ctl add filed\n");
                        return -1;
                    }
                }
                else
                {
                    //触发客户端交互事件                   
                    //接收数据
                    int cfd = events[i].data.fd;
                    bzero(buf, sizeof(buf));
                    res = recv(cfd, buf, sizeof(buf), 0);
                    if(res < 0)
                    {
                        ERR_MSG("recv");
                        return -1;
                    }
                    else if(res == 0)
                    {
                        printf("[%s:%d] 客户端下线 cfd=%d\n", inet_ntoa(savecin[cfd].sin_addr), ntohs(savecin[cfd].sin_port), cfd);
                        close(cfd); // 关闭文件描述符
                        
                        //添加断开连接的客户端从epoll中删除;
                        if(epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL) < 0)
                        {
                            printf("epoll_ctl del filed\n");
                            return -1;
                        }
                        continue;
                    }
                    printf("[%s:%d] cfd=%d: %s\n", inet_ntoa(savecin[cfd].sin_addr), ntohs(savecin[cfd].sin_port), cfd, buf);
 
                    // 发送数据
                    strcat(buf, "*_*");
                    if(send(cfd, buf, sizeof(buf), 0) < 0)
                    {
                        ERR_MSG("send");
                        return -1;
                    }
                }
            }
        }
    }
    return 0;
}

客户端

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


#define ERR_MSG(msg)               \
do {                               \
    printf("LINE: %d\n", __LINE__);\
    perror(msg);                   \
} while(0)

#define PORT 6666
#define IP "127.0.0.1"

int main(int argc,const char * argv[])
{
    //创建流式套接字
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if(cfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("socket create success\n");

    //填充地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORT);
    sin.sin_addr.s_addr = inet_addr(IP);

    //连接到服务器
    if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
    {
        ERR_MSG("connect");
        return -1;
    }
    printf("connect server success\n");

    char buf[128] = "";
    ssize_t res = 0;
    while(1)
    {
        //发送数据
        bzero(buf, sizeof(buf));
        printf("请输入>>> ");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf)-1] = 0;
 
        if(send(cfd, buf, sizeof(buf), 0) < 0)
        {
            ERR_MSG("send");
            return -1;
        }
        printf("发送成功\n");
 
        //接收数据
        bzero(buf, sizeof(buf));
        res = recv(cfd, buf, sizeof(buf), 0);
        if(res < 0)
        {
            ERR_MSG("recv");
            return -1;
        }
        else if(0 == res)
        {
            printf("[%s:%d] 服务器下线\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
            break;
        }
        printf("[%s:%d]: %s\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), buf); 
    }
 
    close(cfd);
    return 0;
}

运行结果

相关推荐
OH五星上将17 小时前
OpenHarmony(鸿蒙南向开发)——标准系统方案之瑞芯微RK3568移植案例(下)
linux·驱动开发·嵌入式硬件·harmonyos·openharmony·鸿蒙开发·系统移植
gopher95112 天前
linux驱动开发-中断子系统
linux·运维·驱动开发
gopher95113 天前
linux驱动开发-设备树
linux·驱动开发
三菱-Liu4 天前
三菱变频器以模拟量电流进行频率设定(电流输入)
驱动开发·单片机·嵌入式硬件·硬件工程·制造
三菱-Liu4 天前
三菱FX5U CPU 内置以太网功能
网络·驱动开发·硬件工程·制造·mr
让开,我要吃人了4 天前
OpenHarmony鸿蒙( Beta5.0)摄像头实践开发详解
驱动开发·华为·移动开发·harmonyos·鸿蒙·鸿蒙系统·openharmony
OH五星上将5 天前
如何更换OpenHarmony SDK API 10
驱动开发·嵌入式硬件·sdk·harmonyos·openharmony·鸿蒙开发
OH五星上将6 天前
OpenHarmony(鸿蒙南向开发)——标准系统移植指南(二)Linux内核
linux·驱动开发·嵌入式硬件·移动开发·harmonyos·鸿蒙开发·鸿蒙内核
芊言芊语6 天前
蓝牙驱动开发详解
驱动开发
让开,我要吃人了7 天前
OpenHarmony鸿蒙( Beta5.0)RTSPServer实现播放视频详解
驱动开发·嵌入式硬件·华为·移动开发·harmonyos·鸿蒙·openharmony