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

记录ssl epoll的tcp socket服务端在客户端断开时崩溃的问题

文章目录

  • 当客户端关闭后,Epoll 的 TCP socket 服务端会收到两次断开事件可能有以下原因及解决方法:
    • 原因分析
    • 解决方法
  • 问题ssl socket服务端代码
  • 出错现象
  • 第一次尝试修改
  • 正确改法
  • 附上客户端代码

记录ssl epoll的tcp socket服务端在客户端断开时接收到多次disconnect事件导致崩溃的问题.
流程:在linux服务器上跑socke服务, 客户端连接后发送数据,服务端回复, 客户端接收到回复后关闭.

当客户端关闭后,Epoll 的 TCP socket 服务端会收到两次断开事件可能有以下原因及解决方法:

原因分析

  1. 网络延迟或数据包重传
    网络环境不稳定可能导致数据包延迟到达或重传。当客户端关闭连接时,发送的断开信号可能由于网络延迟在服务端被接收多次。
    这种情况下,服务端可能会误认为有多个断开事件发生。
  2. 服务端处理逻辑问题
    服务端在处理断开事件时,如果没有正确地标记已处理的事件或者没有及时清理相关资源,可能会导致后续再次触发相同的断开事件。
    例如,在处理第一个断开事件后,服务端可能没有将对应的文件描述符从 Epoll 实例中移除,导致下一次轮询时再次触发该事件。
  3. SSL 层的影响
    如果使用了 SSL 加密,SSL 层的关闭过程可能会与底层的 TCP 连接关闭产生交互问题,导致服务端收到多次断开事件。
    SSL 的关闭握手过程可能会产生额外的数据包,被服务端误认为是新的断开事件。

解决方法

  1. 处理网络问题
    可以通过优化网络环境、增加超时时间等方式来减少网络延迟和数据包重传的影响。
    对于关键的应用,可以考虑使用更稳定的网络连接或者采用网络冗余技术。
  2. 优化服务端处理逻辑
    在服务端处理断开事件时,确保正确地标记已处理的事件,并及时清理相关资源。
    例如,在处理断开事件后,立即将对应的文件描述符从 Epoll 实例中移除,避免再次触发相同的事件。

问题ssl socket服务端代码

这段代码是用AI工具自动生成的, 拿来调试后发现有问题

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <openssl/ssl.h>
#include <openssl/err.h>#define MAX_EVENTS 10
#define BUFFER_SIZE 1024void handle_error(const char* msg) {perror(msg);exit(EXIT_FAILURE);
}SSL_CTX* init_ssl_context() {SSL_library_init();OpenSSL_add_all_algorithms();SSL_load_error_strings();SSL_CTX* ctx = SSL_CTX_new(TLS_server_method());if (!ctx) {ERR_print_errors_fp(stderr);exit(EXIT_FAILURE);}if (SSL_CTX_use_certificate_file(ctx, "server.crt", SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stderr);exit(EXIT_FAILURE);}if (SSL_CTX_use_PrivateKey_file(ctx, "server.key", SSL_FILETYPE_PEM) <= 0) {ERR_print_errors_fp(stderr);exit(EXIT_FAILURE);}return ctx;
}void handle_client(SSL* ssl, int epollfd, int client_socket) {char buffer[BUFFER_SIZE];int bytes_read = SSL_read(ssl, buffer, BUFFER_SIZE - 1);if (bytes_read > 0) {buffer[bytes_read] = '\0';printf("Received: %s", buffer);SSL_write(ssl, buffer, bytes_read);} else {if (bytes_read == 0) {printf("Connection closed by client\n");} else {ERR_print_errors_fp(stderr);}close(client_socket);SSL_shutdown(ssl);SSL_free(ssl);epoll_ctl(epollfd, EPOLL_CTL_DEL, client_socket, NULL);}
}int main() {int server_socket, client_socket;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len = sizeof(client_addr);SSL_CTX* ssl_ctx = init_ssl_context();server_socket = socket(AF_INET, SOCK_STREAM, 0);if (server_socket == -1) {handle_error("socket");}memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY;server_addr.sin_port = htons(12345);if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {handle_error("bind");}if (listen(server_socket, 10) == -1) {handle_error("listen");}int epollfd = epoll_create1(0);if (epollfd == -1) {handle_error("epoll_create1");}struct epoll_event event;event.events = EPOLLIN;event.data.fd = server_socket;if (epoll_ctl(epollfd, EPOLL_CTL_ADD, server_socket, &event) == -1) {handle_error("epoll_ctl");}printf("EPOLL_CTL_ADD:%d\n", server_socket);struct epoll_event events[MAX_EVENTS];while (1) {int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);if (nfds == -1) {handle_error("epoll_wait");}for (int i = 0; i < nfds; i++) {if (events[i].data.fd == server_socket) {client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_len);if (client_socket == -1) {handle_error("accept");}SSL* ssl = SSL_new(ssl_ctx);SSL_set_fd(ssl, client_socket);if (SSL_accept(ssl) <= 0) {ERR_print_errors_fp(stderr);close(client_socket);continue;}event.events = EPOLLIN;event.data.ptr = ssl; // 重点是这两行, 后面会分析//event.data.fd = client_socket;if (epoll_ctl(epollfd, EPOLL_CTL_ADD, client_socket, &event) == -1) {handle_error("epoll_ctl");}printf("New client connected, EPOLL_CTL_ADD:%d, ssl:%p\n",  client_socket, ssl);} else {// 这里用了data.ptr 与data.fdSSL* ssl = (SSL*)events[i].data.ptr;printf("data fd:%d ssl:%p\n", events[i].data.fd, ssl);handle_client(ssl, epollfd, events[i].data.fd);}}}close(server_socket);SSL_CTX_free(ssl_ctx);return 0;
}

出错现象

EPOLL_CTL_ADD:3
New client connected, EPOLL_CTL_ADD:5
Received: 1
Connection closed by client
Connection closed by client
段错误 (核心已转储)

经过调试发现,每次客户端close后,服务端会接收到两次的disconnect事件, 导致重复关闭从而崩溃.

第一次尝试修改

handle_client(SSL* ssl, int epollfd, int client_socket)中会调用close(client_socket), 发现client_socket值没传.

                event.data.ptr = ssl; // 重点是这两行, 后面会分析event.data.fd = client_socket;// 把这行注释打开,把client_socket传递到event中

这么改后,还是会崩溃.
通过man查看event的结构体epoll_data原型, 发现data是union类型, ptr与fd不能同时赋值, 所以第一次改法失败.
epoll_data结构如下

           typedef union epoll_data {void        *ptr;int          fd;uint32_t     u32;uint64_t     u64;} epoll_data_t;struct epoll_event {uint32_t     events;      /* Epoll events */epoll_data_t data;        /* User data variable */};

正确改法

client_socket从ssl中获取,不需要额外传递, 如图, 左边是问题代码, 右边是改正过的代码
在这里插入图片描述

附上客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <arpa/inet.h>#define BUFFER_SIZE 1024void handle_error(const char* msg) {perror(msg);exit(EXIT_FAILURE);
}SSL_CTX* init_ssl_context() {SSL_library_init();OpenSSL_add_all_algorithms();SSL_load_error_strings();SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());if (!ctx) {ERR_print_errors_fp(stderr);exit(EXIT_FAILURE);}return ctx;
}int main() {int client_socket;struct sockaddr_in server_addr;SSL_CTX* ssl_ctx = init_ssl_context();client_socket = socket(AF_INET, SOCK_STREAM, 0);if (client_socket == -1) {handle_error("socket");}memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");server_addr.sin_port = htons(12345);if (connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {handle_error("connect");}SSL* ssl = SSL_new(ssl_ctx);SSL_set_fd(ssl, client_socket);if (SSL_connect(ssl) <= 0) {ERR_print_errors_fp(stderr);close(client_socket);exit(EXIT_FAILURE);}char buffer[BUFFER_SIZE];printf("Enter a message to send to the server: ");fgets(buffer, BUFFER_SIZE, stdin);SSL_write(ssl, buffer, strlen(buffer));int bytes_read = SSL_read(ssl, buffer, BUFFER_SIZE - 1);if (bytes_read > 0) {buffer[bytes_read] = '\0';printf("Received from server: %s", buffer);} else {ERR_print_errors_fp(stderr);}SSL_shutdown(ssl);SSL_free(ssl);close(client_socket);SSL_CTX_free(ssl_ctx);return 0;
}

作者:帅得不敢出门 原创文章谢绝转载

相关文章:

记录ssl epoll的tcp socket服务端在客户端断开时崩溃的问题

文章目录 当客户端关闭后&#xff0c;Epoll 的 TCP socket 服务端会收到两次断开事件可能有以下原因及解决方法&#xff1a;原因分析解决方法 问题ssl socket服务端代码出错现象第一次尝试修改正确改法附上客户端代码 记录ssl epoll的tcp socket服务端在客户端断开时接收到多次…...

ubuntu任何版本 卡死 解决办法

首先&#xff0c;我们一定要记得ubuntu一定不要强制关机&#xff0c;一定&#xff0c;一定 因为90% 的可能你的电脑从此就会黑屏开不了机了&#xff0c;然后你就可以按照我的方法去卸载&#xff0c;重装ubuntu系统了。/(ㄒoㄒ)/~~ &#xff08;如果能解决您的问题&#xff0c…...

算法-合并区间(56)

这道题可以用列表来写&#xff0c;首先对所有的数组区间依据第一个数字进行排序&#xff0c;然后创建一个数组列表存放合并重叠后的结果。 如果列表为空&#xff0c;或者当前区间的起始位置大于列表中区间的最后一个位置&#xff0c;则不重叠直接插入列表&#xff0c;否则合并…...

港科夜闻 | 叶玉如校长出席2024科技+新质生产力高峰论坛发表专题演讲,贡献国家科技强国战略...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、叶玉如校长出席“2024科技新质生产力高峰论坛”&#xff0c;做了题为“三个创新&#xff1a;培育和发展新质生产力、贡献国家科技强国战略”的主题演讲。该论坛于9月2日在香港召开。论坛围绕夯实基础科研、推动源头创新、…...

一文读懂IPv6v6地址的配置方式

在数字化浪潮的推动下&#xff0c;互联网正以前所未有的速度发展&#xff0c;而IPv6作为新一代网络协议&#xff0c;以其庞大的地址空间、增强的安全性和灵活的地址配置机制&#xff0c;成为了支撑这一发展的关键技术。本文将深入解析这两种配置方式的工作原理、应用场景及操作…...

【设计模式】设计模式的八大原则

设计模式的八个原则&#xff1a; 依赖倒置原则&#xff1a; 高层次的代码&#xff08;稳定&#xff09;不应该依赖低层次的代码&#xff08;变化&#xff09;抽象的代码不应该依赖具体的代码。 开放封闭原则&#xff1a;类模块应该开放扩展的&#xff0c;而其原先的代码尽量封…...

【Github项目推荐】DataLoom

项目推荐 - DataLoom 项目背景 在数据驱动的时代&#xff0c;越来越多的企业和个人用户需要从复杂的数据中提取出高价值的信息。然而&#xff0c;传统的数据处理和分析流程复杂且耗时&#xff0c;需要技术人员的深度参与。那么&#xff0c;有没有一种工具能够简化这一过程&am…...

重磅!布拉德皮特移居法国?据称与他和安吉丽娜朱莉生养的6个孩子有关!皮特一直为自己与孩子们如此亲近却又如此遥远而苦恼

对于那些不知道的人来说&#xff0c;布拉德皮特似乎已经正式离开洛杉矶前往欧洲。虽然这有很多原因&#xff0c;我们将逐一介绍&#xff0c;但主要原因据称与他和前前妻安吉丽娜朱莉所生养的六个疏远的孩子有关。 知情人士告诉《InTouch Weekly》&#xff0c;皮特搬到了法国&a…...

.net MAUI应用生命周期

.NET Multi-platform App UI (.NET MAUI) 应用通常有四种执行状态&#xff1a;“未运行”、“运行中”、“已停用”和“已停止”。 当应用从未运行状态转换为运行状态、从运行状态转换为已停用状态、从已停用状态转换为已停止状态、从已停止状态转换为运行状态&#xff0c;以及…...

Nginx 安装教程

一、安装编译工具及库文件 yum -y install make zlib zlib-devel gcc-c libtool openssl openssl-devel 二、首先要安装 PCRE PCRE 作用是让 Nginx 支持 Rewrite 功能。 1、下载 PCRE 安装包&#xff0c;下载地址&#xff1a; http://downloads.sourceforge.net/project/p…...

vue axios发送post请求跨域解决

跨越解决有两种方案&#xff0c;后端解决&#xff0c;前端解决。后端解决参考Django跨域解决-CSDN博客 该方法之前试着可以的&#xff0c;但是复制到其他电脑上报错&#xff0c;所以改用前端解决 1、main.js做增加如下配置 import axios from axios Vue.prototype.$axios a…...

MIT线性代数

本文链接的原创作者为 浊酒南街https://blog.csdn.net/weixin_43597208 第1讲 MIT_线性代数笔记&#xff1a;第 01 讲 行图像和列图像-CSDN博客 第2讲 MIT_线性代数笔记&#xff1a;第 02 讲 矩阵消元_矩阵first pivot-CSDN博客 第3讲 MIT_线性代数笔记&#xff1a;第 03…...

打工人自救指南!2024年数据恢复工具,清空回收站也能秒回数据

嘿&#xff0c;各位上班的朋友们&#xff0c;有没有手抖或者不小心&#xff0c;把重要的东西给拖到回收站&#xff0c;然后又不小心把回收站清空了&#xff1f;别慌&#xff0c;今天我就来给你们支招&#xff0c;用数据恢复工具&#xff0c;让你们在丢数据的倒霉时候也能找到救…...

MyBatis 缓存机制

分布式系统中不建议使用mybatis缓存机制&#xff0c;建议通过redis来解决数据缓存。 MyBatis 缓存机制包括一级缓存和二级缓存&#xff0c;它们在提高数据库查询效率方面发挥着重要作用。 1. **一级缓存**&#xff08;Local Cache&#xff09;&#xff1a; - 一级缓存是 M…...

10个神级Python自动化脚本助力轻松工作

大家好&#xff0c;Python自动化简化了日常编程任务&#xff0c;同时更激发了探索创新解决方案的热情。本文将介绍10个精心挑选的自动化脚本&#xff0c;帮助大家提升工作效率和优化操作流程。 1.Speakify Speakify 是专注于语音交互的人工智能助手,可以帮助用户通过语音进行…...

EasyExcel模板导出与公式计算(下)

目录 环境要求 功能预览 需求分析 导入依赖 制作模板 编写代码 格式优化 最终效果 总结 在上一篇 EasyExcel模板导出与公式计算&#xff08;上&#xff09;-CSDN博客 文章中我们知道了在若依中使用自带的Excel注解来实现表格数据的导出&#xff0c;并且通过重写相关接…...

Golang序言全面学习-前序

最近看了很多与Golang有关的教程与书籍&#xff0c;也包括bilibili上的教程&#xff0c;各位老师讲解非常详细的&#xff0c;基本涉及了基础篇的方方面面&#xff0c;但总是感觉缺少了一些实战&#xff0c;以及实际经验的传授。实际项目会用到的日志框架、配置管理框架&#xf…...

Python世界:文件自动化备份实践

Python世界&#xff1a;文件自动化备份实践 背景任务实现思路坑点小结 背景任务 问题来自《简明Python教程》中的解决问题一章&#xff0c;提出实现&#xff1a;对指定目录做定期自动化备份。 最重要的改进方向是不使用 os.system 方法来创建归档文件&#xff0c; 而是使用 zip…...

PTA 6-10 阶乘计算升级版(详讲)

6-10 阶乘计算升级版 - 基础编程题目集 (pintia.cn)https://pintia.cn/problem-sets/14/exam/problems/type/6?problemSetProblemId742&page0 首先这道题不能用我们之前学过的阶乘计算方法来解决&#xff0c;比如下面这段代码就无法通过全部的样例 void Print_Factorial…...

软件开发人员从0到1实现物联网项目:项目架构的思考

文章目录 前言单体应用足矣摒弃传统的微信对接后期的维护投入上真正的“云”&#xff1a;云托管0服务器免运维免费的CDN和DDoS防护 技术架构小结 前言 因为种种原因&#xff0c;《软件开发人员从0到1实现物联网项目》这个项目的进度停滞了将近一个月。 鉴于该项目的前期开发和…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机&#xff0c;交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息&#xff0c;系统版本&#xff1a;Ubuntu22.04.5 LTS&#xff1b;内核版本…...

从实验室到产业:IndexTTS 在六大核心场景的落地实践

一、内容创作&#xff1a;重构数字内容生产范式 在短视频创作领域&#xff0c;IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色&#xff0c;生成的 “各位吴彦祖们大家好” 语音相似度达 97%&#xff0c;单条视频播放量突破百万…...

路由基础-路由表

本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中&#xff0c;往往存在多个不同的IP网段&#xff0c;数据在不同的IP网段之间交互是需要借助三层设备的&#xff0c;这些设备具备路由能力&#xff0c;能够实现数据的跨网段转发。 路由是数据通信网络中最基…...

【1】跨越技术栈鸿沟:字节跳动开源TRAE AI编程IDE的实战体验

2024年初&#xff0c;人工智能编程工具领域发生了一次静默的变革。当字节跳动宣布退出其TRAE项目&#xff08;一款融合大型语言模型能力的云端AI编程IDE&#xff09;时&#xff0c;技术社区曾短暂叹息。然而这一退场并非终点——通过开源社区的接力&#xff0c;TRAE在WayToAGI等…...