linux网络编程 | c | epoll实现IO多路转接服务器

news2024/12/19 4:03:53

epoll实现IO多路转接服务器

可通过以下视频学习

06-opell函数实现的多路IO转接_哔哩哔哩_bilibili

通过响应式–多路IO转接实现

文章目录

  • epoll实现IO多路转接服务器
    • 1.思路&功能
      • 核心思路
    • 2.代码实现
      • multi_epoll_sever.c
      • 运行图

1.思路&功能

**功能:**客户端输入小写字符串,服务器转成大写返回给客户端

思路:

核心思路

  1. socket()、bind()、listen()
  2. epoll_create创建红黑树,它的返回值就是树的根节点
  3. epoll_ctl将listenfd添加到树上
  4. 循环epoll_wait进行监听,它的返回值是满足监听的总个数,所以以它的返回值为遍历上限去判断事件
  5. 如果它返回的数组中data.fd等于lfd,那么就accept去连接客户端 并将新的cfd加入树中
  6. 如果不是lfd,就说明有读事件发生,就去判断读到的返回值,<0是出错 ==0是客户端关闭(这两个都要去将该cfd从树中移除 并close),>0就处理数据然后写回

2.代码实现

multi_epoll_sever.c

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define OPEN_MAX 5000
#define SERVE_PORT 9527

int main()
{
    // 所需要的变量
    int lfd, cfd, efd, ret, wait_ret, i, sockfd, len;
    char buf[1024];
    // 地址结构体
    struct sockaddr_in serve_addr, client_addr;
    socklen_t client_addr_len;
    serve_addr.sin_family = AF_INET;                // IPV4
    serve_addr.sin_port = htons(SERVE_PORT);        // 绑定端口
    serve_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定ip(ANY系统自动分配)

    // 创建socket
    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd < 0)
    {
        perror("socket error");
        exit(1);
    }
    // bind绑定
    bind(lfd, (struct sockaddr *)&serve_addr, sizeof(serve_addr));
    // 设置上限
    listen(lfd, 128);

    // 创建红黑树
    efd = epoll_create(1); // efd就是树的根节点

    // 将lfd挂在树上
    // epoll结构体                       ep是epoll_wait所需的数组(存放满足事件的fd)
    struct epoll_event tep, ep[128]; //  tep是epoll_ctl的参数(传监听的事件)
    tep.events = EPOLLIN;
    tep.data.fd = lfd;
	
    ret = epoll_ctl(efd, EPOLL_CTL_ADD, lfd, &tep);
    if (ret < 0)
    {
        perror("epoll_ctl error");
        exit(1);
    }

    // 循环去epoll_wait进行监听
    while (1)
    {
        wait_ret = epoll_wait(efd, ep, 128, -1); // wait_ret就是实际满足事件的总个数

        // 以wait_ret为上限去遍历事件
        for (i = 0; i < wait_ret; i++)	
        {
            // sockfd用于接收满足事件的fd
            sockfd = ep[i].data.fd;

            // 如果等于lfd,那就说明有客户端要来连接,就去accept
            if (sockfd == lfd)	
            {
                client_addr_len = sizeof(client_addr);
                cfd = accept(lfd, (struct sockaddr *)&client_addr, &client_addr_len);

				// 将cfd设置为非阻塞
                int flag = fcntl(cfd, F_GETFL);
                flag |= O_NONBLOCK;
                fcntl(cfd, F_SETFL, flag);
	
                // 把新的cfd加入树中
                tep.events = EPOLLIN | EPOLLET;
                tep.data.fd = cfd;
                ret = epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &tep);
                if (ret < 0)
                {
                    perror("epoll_ctl cfd error");
                    exit(1);
                }
            }
            // 如果不是lfd,那就说明有读事件发生(读数据)
            else
            {
                len = read(sockfd, buf, sizeof(buf));
                if (len == 0) // 说明对方关闭连接(从树上摘下 & close)
                {
                    epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);
                    close(sockfd);
                }
                else if (len == -1)
                {
                    perror("read error");
                    exit(1);
                }
                else // 读写数据
                {
                    for (i = 0; i < len; i++)
                        buf[i] = toupper(buf[i]);

                    write(sockfd, buf, len);
                    write(STDIN_FILENO, buf, len);
                }
            }
        }
    }

    return 0;
}

gcc multi_epoll_sever.c -o multi_epoll_sever
//也可以使用和其他多路IO一样的封装的warp.h warp.c的版本
//就不贴warp.c和warp.c了,大家有需要可以去select和poll的博客中查看
#include"warp.h"
#define SERV_PORT 9527
#define OPEN_MAX 20
int main(int argc,char * argv[])
{
	int i,listenfd,connfd,efd,res,nready,sockfd;
	int n,num=0;
	char buf[4096],str[INET_ADDRSTRLEN];
	socklen_t clien;
	listenfd=Socket(AF_INET,SOCK_STREAM,0);	
	int opt=1;
	setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
	struct sockaddr_in servaddr,cliaddr;
	struct epoll_event tep,ep[OPEN_MAX];

	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(SERV_PORT);
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

	Bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
	Listen(listenfd,128);
	
	efd=epoll_create(OPEN_MAX);
	if(efd==-1)
		perr_exit("epoll_create error");

	tep.events=EPOLLIN;
	tep.data.fd=listenfd;
	res=epoll_ctl(efd,EPOLL_CTL_ADD,listenfd,&tep);
	if(res==-1)
		perr_exit("epoll_ctl error");
	
	for(;;)
	{
		nready=epoll_wait(efd,ep,OPEN_MAX,-1);
		if(nready==-1)
		perr_exit("epoll_wait error");
		for(i=0;i<nready;i++)
		{
			if(!ep[i].events&EPOLLIN)
				continue;
			if(ep[i].data.fd==listenfd)
			{
				clien=sizeof(cliaddr);
				connfd=Accept(listenfd,(struct sockaddr *)&cliaddr,&clien);

				printf("received from %s at PORT %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));
				printf("cfd %d---client %d\n",connfd,++num);
				tep.events=EPOLLIN;
				tep.data.fd=connfd;
				res=epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep);
				if(res==-1)
					perr_exit("epoll_ctl error");
			}
			else
			{
				sockfd=ep[i].data.fd;
				n=Read(sockfd,buf,4096);

				if(n==0)
				{
					res=epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL);
					if(res==-1)
						perr_exit("epoll_ctl error");
					Close(sockfd);
					printf("client[%d] closed connection\n",sockfd);
				}
				else if(n<0)
				{
					perror("read n<0 error:");
					res=epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL);
					Close(sockfd);
				}
				else
				{
					for(i=0;i<n;i++)
						buf[i]=toupper(buf[i]);
					Write(STDOUT_FILENO,buf,n);
					Write(sockfd,buf,n);
				}
			}
		}
	}

	Close(listenfd);
	Close(efd);
	return 0;
}

运行图

两个客户端访问服务器端

image-20241213104728787

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261924.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java全体系精华(上):从基础到框架,构建坚实开发技能

爱的故事.上集 1. Java 基础1.1 常用集合数据结构 Array List Map Set Tree1.1.1 常用集合在JDK中的结构1.1.2 List 底层是数组1.1.3 Map键值对结存储结构1.1.3.1 为什么HashMap的Key、Value都允许为 null1.1.3.2 为什么ConcurrentHashMap的Key、Value都不允许为null1.1.3.3 Ha…

基于Clinical BERT的医疗知识图谱自动化构建方法,双层对比框架

基于Clinical BERT的医疗知识图谱自动化构建方法&#xff0c;双层对比框架 论文大纲理解1. 确认目标2. 目标-手段分析3. 实现步骤4. 金手指分析 全流程核心模式核心模式提取压缩后的系统描述核心创新点 数据分析第一步&#xff1a;数据收集第二步&#xff1a;规律挖掘第三步&am…

ctfshow--web入门之爆破篇

知识点&#xff1a; 暴力破解原理 暴力破解实际就是疯狂的输入密码进行尝试登录&#xff0c;针对有的人喜欢用一些个人信息当做密码&#xff0c;有的人喜欢用一些很简单的低强度密码&#xff0c;我们就可以针对性的生成一个字典&#xff0c;用脚本或者工具挨个去尝试登录。 …

Web项目图片视频加载缓慢/首屏加载白屏

Web项目图片视频加载缓慢/首屏加载白屏 文章目录 Web项目图片视频加载缓慢/首屏加载白屏一、原因二、 解决方案2.1、 图片和视频的优化2.1.1、压缩图片或视频2.1.2、 选择合适的图片或视频格式2.1.3、 使用图片或视频 CDN 加速2.1.4、Nginx中开启gzip 三、压缩工具推荐 一、原因…

Go 语言与时间拳击理论下的结对编程:开启高效研发编程之旅

一、引言 结对编程作为一种软件开发方法&#xff0c;在提高代码质量、增强团队协作等方面具有显著优势。而时间拳击理论为结对编程带来了新的思考角度。本文将以 Go 语言为中心&#xff0c;深入探讨时间拳击理论下的结对编程。 在当今软件开发领域&#xff0c;高效的开发方法和…

SpringBoot集成ENC对配置文件进行加密

在线MD5生成工具 配置文件加密&#xff0c;集成ENC 引入POM依赖 <!-- ENC配置文件加密 --><dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-boot-starter</artifactId><version>2.1.2</ver…

el-table 多表头+跨行跨列案例

效果&#xff1a; 代码&#xff1a; index.vue <template><div class"my-table"><el-tablev-loading"table.loading":data"table.data"bordersize"mini":header-cell-style"headerCellStyle":span-method&qu…

基线检查:Windows安全基线.【手动 || 自动】

基线定义 基线通常指配置和管理系统的详细描述&#xff0c;或者说是最低的安全要求&#xff0c;它包括服务和应用程序设置、操作系统组件的配置、权限和权利分配、管理规则等。 基线检查内容 主要包括账号配置安全、口令配置安全、授权配置、日志配置、IP通信配置等方面内容&…

【electron】electron forge + vite + vue + electron-release-server 自动更新客户端

基本信息 electron forge vue页面&#xff08;中文&#xff09;&#xff1a;https://forge.electron.js.cn/guides/framework-integration/vue-3 electron forge vue页面&#xff08;英文&#xff0c;中文版下面的tab无法点击&#xff09;&#xff1a;https://www.electronfor…

ubuntu+ros新手笔记(二):古月·ROS2入门21讲学习笔记

系统ubuntu22.04 ros2 humble 按照如下视频教程学习的&#xff1a;【古月居】古月ROS2入门21讲 | 带你认识一个全新的机器人操作系统 此处仅记录我报错的地方&#xff0c;以及相应的解决方案&#xff0c;没有出错的略过&#xff01; 对应的古月居ROS2入门21讲源码下载地址&a…

IDEA 可视化使用 git rebase 合并分支步骤 使git分支树保持整洁

模拟环境 dev 分支开发完一个功能&#xff0c;需要合并到 master 分支&#xff0c;如果现在直接 merge 合并的话 git分支树会出现杂乱分叉&#xff0c;先把 master 分支 rebase 到 dev git分支树就会是整洁的一条直线 git rebase介绍 rebase:翻译成中文是重新设定&#xff0c;…

Windows环境 (Ubuntu 24.04.1 LTS ) 国内镜像,用apt-get命令安装RabbitMQ,java代码样例

一、环境 Windows11 WSL(Ubuntu 24.04.1) 二、思路 1 用Windows中的Ubuntu安装RabbitMQ&#xff0c;贴近Linux的线上环境&#xff1b; 2 RabbitMQ用erlang语言编写的&#xff0c;先安装erlang的运行环境&#xff1b; 2 用Linux的apt-get命令安装&#xff0c;解决软件依赖…

使用ElasticSearch实现全文检索

文章目录 全文检索任务描述技术难点任务目标实现过程1. java读取Json文件&#xff0c;并导入MySQL数据库中2. 利用Logstah完成MySQL到ES的数据同步3. 开始编写功能接口3.1 全文检索接口3.2 查询详情 4. 前端调用 全文检索 任务描述 在获取到数据之后如何在ES中进行数据建模&a…

CTFHUB-web(SSRF)

内网访问 点击进入环境&#xff0c;输入 http://127.0.0.1/flag.php 伪协议读取文件 /?urlfile:///var/www/html/flag.php 右击查看页面源代码 端口扫描 1.根据题目提示我们知道端口号在8000-9000之间,使用bp抓包并进行爆破 POST请求 点击环境&#xff0c;访问flag.php 查看页…

数据结构 ——前缀树查词典的实现

数据结构 ——前缀树查词典的实现 一、前缀树的概念 前缀树是一种多叉树结构&#xff0c;主要用于存储字符串。每个节点代表一个字符&#xff0c;路径从根节点到叶节点表示一个完整的字符串。前缀树的关键特征是 共享前缀&#xff0c;也就是说&#xff0c;如果两个字符串有相…

React里循环tab列表,并实现点击切换class

介绍 在 React 框架里&#xff0c;通过循环去显示 tab列表的标题&#xff0c;并且添加点击事件&#xff0c;当前点击的tab高亮显示。就像 vue 里 通过 v-for 显示列表并且点击时添加 activeClass 一样。 实现效果 代码 主要通过 map方法来实现列表的循环显示&#xff0c;然后…

selenium 在已打开浏览器上继续调试

关闭浏览器&#xff0c;终端执行如下指令&#xff0c;--user-data-dir换成自己的User Data路径 chrome.exe --remote-debugging-port9222 --user-data-dir"C:\Users\xxx\AppData\Local\Google\Chrome\User Data" 会打开浏览器&#xff0c;打开百度&#xff0c;如下状…

Pytest-Bdd vs Behave:选择最适合的 Python BDD 框架

Pytest-Bdd vs Behave&#xff1a;选择最适合的 Python BDD 框架 Pytest BDD vs Behave&#xff1a;选择最适合的 Python BDD 框架BDD 介绍Python BDD 框架列表Python BehavePytest BDDPytest BDD vs Behave&#xff1a;关键区别Pytest BDD vs Behave&#xff1a;最佳应用场景结…

B站bilibili视频转文字字幕下载方法

本文将讲述介绍一种使用本地工具如何快速的下载B站的字幕为本地文本文件的方法。 通常获取B站字幕需要在浏览器中安装第三方插件&#xff0c;通过插件获取字幕。随着大模型&#xff0c;生成式AI&#xff0c;ChatGPT的应用&#xff0c;B站也提供了AI小助手对视频的内容进行总结…

ElasticSearch的自动补全功能(拼音分词器、自定义分词器、DSL实现自动补全查询、RestAPI实现自动补全查询)

文章目录 1. 什么是自动补全2. 拼音分词器2.1 初识拼音分词器2.2 下载拼音分词器2.3 安装拼音分词器2.4 测试拼音分词器 3. 自定义分词器3.1 拼音分词器存在的问题3.2 分词器&#xff08;analyzer&#xff09;的组成3.3 如何自定义分词器3.4 拼音分词器的可选参数3.5 配置自定义…