当前位置: 首页 > news >正文

1. 使用poll或epoll创建echo服务器

1. 说明:

此篇博客主要记录一种客户端实现方式,和两种使用poll或者epoll分别创建echo服务器的方式,具体可看代码注释:

2. 相关代码:

2.1 echoClient.cpp
#include <iostream>
#include <cstdio>
#include <cstring>#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>#define ERR_EXIT(m) \do {\perror(m); \exit(EXIT_FAILURE); \} while(0)int main() {int sock;struct sockaddr_in svrAddr, localAddr;socklen_t addrlen = sizeof(sockaddr);sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);if (sock < 0)ERR_EXIT("socket");memset(&svrAddr, 0, sizeof(svrAddr));svrAddr.sin_family = AF_INET;svrAddr.sin_port = htons(8888);svrAddr.sin_addr.s_addr = inet_addr("127.0.0.1");if (connect(sock, (sockaddr *)&svrAddr, sizeof(svrAddr)) < 0)ERR_EXIT("connect");if (getsockname(sock, (sockaddr *)&localAddr, &addrlen) < 0)ERR_EXIT("getsockname");std::cout << "ip = " << inet_ntoa(localAddr.sin_addr)<< " port = " << ntohs(localAddr.sin_port) << std::endl;char sendBuf[1024] = {0};char recvBuf[1024] = {0};while (fgets(sendBuf, sizeof(sendBuf), stdin) != NULL){write(sock, sendBuf, strlen(sendBuf));read(sock, recvBuf, sizeof(recvBuf));fputs(recvBuf, stdout);memset(sendBuf, 0, sizeof(sendBuf));memset(recvBuf, 0, sizeof(recvBuf));}close(sock);return 0;
}
2.2 echoServer_poll.cpp
#include <iostream>
#include <vector>
#include <string>#include <cstdio>
#include <cstring>#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <unistd.h>#include <signal.h>
#include <poll.h>
#include <sys/socket.h>#define ERR_EXIT(m) \do {\perror(m); \exit(EXIT_FAILURE); \} while(0)using PollfdList = std::vector<pollfd>;int main()
{//忽略系统提示的一些错误信号/*signal(para1,para2)para1:信号类型para2:信号处理函数(可以自定义)讲解参考:https://blog.csdn.net/u013271656/article/details/114537411*/signal(SIGPIPE, SIG_IGN);signal(SIGCHLD, SIG_IGN);//创建一个监听套接字(非阻塞套接字)int listenfd;listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);if(listenfd < 0){ERR_EXIT("socket");}//设置地址struct sockaddr_in srvAddr;memset(&srvAddr,0,sizeof(srvAddr));//初始化srvAddr.sin_family = AF_INET;srvAddr.sin_port = htons(8888);srvAddr.sin_addr.s_addr = htonl(INADDR_ANY);//设置地址的重复利用int on = 1;if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0){ERR_EXIT("setsockopt");}//绑定if(bind(listenfd, (sockaddr *)&srvAddr, sizeof(srvAddr)) < 0){ERR_EXIT("bind");}//监听if(listen(listenfd, SOMAXCONN) < 0){ERR_EXIT("listen");}//使用poll并关注pollin事件struct pollfd pfd;pfd.fd = listenfd;pfd.events = POLLIN;//储存poll的描述符PollfdList pollfds;pollfds.push_back(pfd);int nready;struct sockaddr_in peerAddr;socklen_t peerlen;int connfd;int idlefd;//空闲描述符//循环处理while (true) {//取事件nready = poll(pollfds.data(), pollfds.size(), -1);if(nready == -1){if(errno == EINTR){continue;}ERR_EXIT("poll");}else if(nready == 0){continue;}//如果有POLLIN事件if(pollfds[0].revents & POLLIN){//接受peerlen = sizeof(peerAddr);connfd = ::accept4(listenfd, (sockaddr *)&peerAddr, &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);//剔除空闲连接if (connfd == -1) {if (errno == EMFILE) {close(idlefd);idlefd = accept(listenfd, NULL, NULL);close(idlefd);idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);continue;}else ERR_EXIT("accept4");}//加入到监听pfd.fd = connfd;pfd.events = POLL_IN;pfd.revents = 0;pollfds.push_back(pfd);--nready;//连接成功std::cout << "ip = " << inet_ntoa(peerAddr.sin_addr)<< " port = " << ntohs(peerAddr.sin_port) << std::endl;if (nready == 0)continue;}//std::cout << "pollfds size: " << pollfds.size() << std::endl;//std::cout << "nready fds: " << nready << std::endl;//遍历判断哪些套接字产生了事件for (auto it = pollfds.begin() + 1; it != pollfds.end() && nready > 0; ++it) {//如果是可读事件if (it->revents & POLL_IN) {--nready;connfd = it->fd;char buf[1024] = {0};//读取数据int ret = read(connfd, buf, 1024);if (ret == -1)ERR_EXIT("read");if (ret == 0) {std::cout << "client closed" << std::endl;it = pollfds.erase(it);--it;close(connfd);continue;}std::cout << buf << std::endl;//将接收的消息返回给客户端write(connfd, buf, strlen(buf));}}}return 0;
}
2.3 echoServer_epoll.cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>#include <cstdio>
#include <cstring>#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <unistd.h>#include <signal.h>
#include <sys/epoll.h>
#include <sys/socket.h>#define ERR_EXIT(m) \do {\perror(m); \exit(EXIT_FAILURE); \} while(0)using EventList = std::vector<epoll_event>;int main()
{signal(SIGPIPE, SIG_IGN);signal(SIGCHLD, SIG_IGN);//创建一个监听套接字(非阻塞套接字)int listenfd;listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);if(listenfd < 0){ERR_EXIT("socket");}//设置地址struct sockaddr_in srvAddr;memset(&srvAddr,0,sizeof(srvAddr));//初始化srvAddr.sin_family = AF_INET;srvAddr.sin_port = htons(8888);srvAddr.sin_addr.s_addr = htonl(INADDR_ANY);//设置地址的重复利用int on = 1;if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0){ERR_EXIT("setsockopt");}//绑定if(bind(listenfd, (sockaddr *)&srvAddr, sizeof(srvAddr)) < 0){ERR_EXIT("bind");}//监听if(listen(listenfd, SOMAXCONN) < 0){ERR_EXIT("listen");}std::vector<int> clients;int epollfd = epoll_create(EPOLL_CLOEXEC);//使用EPOLLIN并关注EPOLLIN事件struct epoll_event event;event.data.fd = listenfd;event.events = EPOLLIN;epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);//储存epoll的描述符EventList events(16);struct sockaddr_in peerAddr;socklen_t peerlen;int connfd;int idlefd;//空闲描述符int nready;//循环处理while (true) {//取事件nready = epoll_wait(epollfd, events.data(), static_cast<int>(events.size()), -1);if(nready == -1){if(errno == EINTR){continue;}ERR_EXIT("epoll_wait");}else if(nready == 0){continue;}if (static_cast<size_t>(nready) == events.size()) {events.resize(events.size() * 2);} for (auto e : events) {if (e.data.fd == listenfd) {peerlen = sizeof(peerAddr);connfd = ::accept4(listenfd, (sockaddr *)&peerAddr, &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);if (connfd == -1) {if (errno == EMFILE) {close(idlefd);idlefd = accept(listenfd, NULL, NULL);close(idlefd);idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);continue;}else ERR_EXIT("accept4");}clients.push_back(connfd);event.data.fd = connfd;event.events = EPOLLIN;epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);std::cout << " connection from ip = " << inet_ntoa(peerAddr.sin_addr)<< " port = " << ntohs(peerAddr.sin_port) << std::endl;}else if (e.events & EPOLLIN) {connfd = e.data.fd;char buf[1024] = {0};int ret = read(connfd, buf, 1024);if (ret == -1)ERR_EXIT("read");if (ret == 0) {std::cout << "client closed" << std::endl;event = e; epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &event);clients.erase(std::remove(clients.begin(), clients.end(), connfd), clients.end());close(connfd);continue;}std::cout << "msg: " << buf << std::endl;write(connfd, buf, strlen(buf));}}}return 0;
}

相关文章:

  • 【小米电脑管家】安装使用教程--非小米电脑
  • [ 蓝桥杯Web真题 ]-布局切换
  • 计算机毕业设计 SpringBoot的医院门诊在线挂号系统 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试
  • 使用linux CentOS本地部署SQL Server数据库
  • c#编码技巧(十五):新语法糖record深入分析
  • echarts词云图echarts-wordcloud使用方法
  • HarmonyOS--ArkTS(1)--基本语法(1)
  • c++新经典模板与泛型编程:const修饰符的移除与增加
  • Python函数和模块:编程的魔法函数
  • Leetcode刷题笔记——摩尔投票法
  • C++初阶(十四)list
  • [论文阅读]BEVFusion
  • Spring Boot 优雅地处理重复请求
  • Java IO流(五)(字符集基础知识简介)
  • 【3】密评-物理和环境安全测评
  • [译]Python中的类属性与实例属性的区别
  • canvas 高仿 Apple Watch 表盘
  • Docker 笔记(2):Dockerfile
  • Docker: 容器互访的三种方式
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • Java小白进阶笔记(3)-初级面向对象
  • Lucene解析 - 基本概念
  • overflow: hidden IE7无效
  • Python_OOP
  • React Native移动开发实战-3-实现页面间的数据传递
  • Spark学习笔记之相关记录
  • Spring Boot MyBatis配置多种数据库
  • 给新手的新浪微博 SDK 集成教程【一】
  • PostgreSQL之连接数修改
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • #Linux(权限管理)
  • (JS基础)String 类型
  • (LeetCode) T14. Longest Common Prefix
  • (附源码)计算机毕业设计ssm基于B_S的汽车售后服务管理系统
  • (机器学习的矩阵)(向量、矩阵与多元线性回归)
  • (算法)求1到1亿间的质数或素数
  • (一)基于IDEA的JAVA基础12
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • .net core使用ef 6
  • @Async注解的坑,小心
  • @RequestBody与@ResponseBody的使用
  • [ 数据结构 - C++] AVL树原理及实现
  • [ACL2022] Text Smoothing: 一种在文本分类任务上的数据增强方法
  • [BT]BUUCTF刷题第9天(3.27)
  • [C++]拼图游戏
  • [C++]指针与结构体
  • [CDOJ 1343] 卿学姐失恋了
  • [CVPR2021]Birds of a Feather: Capturing Avian Shape Models from Images
  • [dart学习]第四篇:函数
  • [Flex][问题笔记]TextArea滚动条问题
  • [IE9] GPU硬件加速到底是实用创新还是噱头
  • [java] 23种设计模式之责任链模式
  • [Jenkins] Docker 安装Jenkins及迁移流程
  • [LeetCode]剑指 Offer 40. 最小的k个数
  • [Linux版本Debian系统]安装cuda 和对应的cudnn以cuda 12.0为例