【米尔王牌产品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;
}
【实验现象】:
可以实现多客户端连接,详见底部视频。
更多回帖