米尔电子
直播中

华仔stm32

3年用户 2961经验值
擅长:嵌入式技术
私信 关注
[技术]

【米尔王牌产品MYD-Y6ULX-V2开发板试用体验】socket通信和epoll

【米尔王牌产品MYD-Y6ULX-V2开发板试用体验】创建TCP通信 - 米尔科技 - 电子技术论坛 - 广受欢迎的专业电子论坛! (elecfans.com)
前面创建了socket 单线程的通信。如果客端连接断开后,主服务端也就断开。学习了博客园的@liangf27的帖子来实现单线程服务多个客户端。
修改main.c代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> // open function
#include <unistd.h> // close function
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <sys/epoll.h>
#include "ssd1306.h"
const int PORT = 8888;
/*
    listen_loop(): epoll监听套接字,作不同处理
    accept_conn(): 新的客户端连接进来,执行accept,将fd加入epoll set
    recv_message(): recv并且重复输出一份给客户端
 */
void listen_loop();
void accept_conn(unsigned int sock_fd, unsigned int epollfd);
void recv_message(unsigned int sock_fd);

int main(void) {
    int sock_fd;
    struct sockaddr_in server_addr;
  SSD1306_init();
	SSD1306_clearDisplay();
  SSD1306_setBrightness(255);

  SSD1306_setPageMode();
	SSD1306_setTextXY(0,0);
	SSD1306_putString("HELLO MYD-Y6ULX!");
  SSD1306_setTextXY(1,0);
  SSD1306_putString("SSD1306 DEMO");
  SSD1306_setTextXY(2,0);
  SSD1306_putString("2022-11-07");

    //初始化socket
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0) {
        perror("socket:");
        return 0;
    }

    //编辑地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;//ipv_4
    server_addr.sin_port = htons(PORT);//监听端口8888
    server_addr.sin_addr.s_addr = INADDR_ANY;//本地的任意地址

    //绑定然后监听
    if (bind(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind:");
        return 0;
    }
    if (listen(sock_fd, 10) < 0) {
        perror("listen");
        return 0;
    }

    listen_loop(sock_fd);

    return 0;
}
void accept_conn(unsigned int sock_fd, unsigned int epollfd) {
    struct sockaddr_in clientaddr;
    struct epoll_event event;
    socklen_t len = sizeof(struct sockaddr);
    int accept_fd = 0;

    accept_fd = accept(sock_fd, (struct sockaddr*)&clientaddr, &len);

    if (accept_fd <= 0) {
        perror("accept error");
        return;
    }

    //将新建连接加入epoll set
    event.data.fd = accept_fd;
    event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, accept_fd, &event);
    return;
}

void recv_message(unsigned int sock_fd) {
    char recv_buf[1024], send_buf[1024];

    memset(recv_buf, 0, sizeof(recv_buf));
    memset(send_buf, 0, sizeof(send_buf));

    recv(sock_fd, recv_buf, sizeof(recv_buf), 0);
//接收到数据后,在这里写处理逻辑,我这里把接收到的数据显示到OLED屏上,并原样的回发给客户端。
    fputs(recv_buf, stdout);
    strcpy(send_buf, recv_buf);
    SSD1306_clearDisplay();
    SSD1306_setTextXY(3,0);
   SSD1306_putString(send_buf);
    send(sock_fd, send_buf, sizeof(send_buf), 0);

    return;
}
void listen_loop(unsigned int sock_fd)
{
    int epollfd, i, ret;
    int timeout = 300;
    struct epoll_event event;
    struct epoll_event eventList[10];

    /*创建epoll监听事件*/
    epollfd = epoll_create(10);
    event.events = EPOLLIN | EPOLLET;
    event.data.fd = sock_fd;

    /*注册epoll监听事件.*/
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock_fd, &event) < 0) {
        printf("register epoll event err !");
        return;
    }

    while (1) {
        ret = epoll_wait(epollfd, eventList, 10, timeout);

        /*epoll事件错误.*/
        if (ret < 0) {
            printf("epoll event err!");
            break;
        }
        /*无事件返回.*/
        else if (ret == 0) {
            continue;
        }

        /*epoll返回事件.*/
        for (i = 0; i < ret; i++) {
            /*epoll 错误*/
            if ((eventList[i].events & EPOLLERR) || (eventList[i].events & EPOLLHUP) || !(eventList[i].events & EPOLLIN)) {
                printf("epoll error\n");
                close(eventList[i].data.fd);
                exit(-1);
            }

            //half connection
            if (eventList[i].events & EPOLLRDHUP) {
                printf("//one client close the conne.//\n");
                close(eventList[i].data.fd);
            }

            /*accept事件*/
            if (eventList[i].data.fd == sock_fd) {
                accept_conn(sock_fd, epollfd);
            }
            /*非sock_fd则为其他事件.*/
            else {
                recv_message(eventList[i].data.fd);
            }
        }
    }
    close(epollfd);
    close(sock_fd);
    return;
}

【实验现象】:
可以实现多客户端连接,详见底部视频。

epoll

回帖(1)

dianzi

2022-11-10 15:38:26
感谢华哥 精彩分享
1 举报

更多回帖

发帖
×
20
完善资料,
赚取积分