TCP simultaneous open测试

news2025/5/23 8:18:57

源代码

/*************************************************************************
    > File Name: common.h
    > Author: hsz
    > Brief:
    > Created Time: 2024年10月23日 星期三 09时47分51秒
 ************************************************************************/

#pragma once

#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <memory>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>

struct PeerMessage
{
    uint16_t    is_server;
    char        host[32];
    uint16_t    port;
};

auto size = sizeof(PeerMessage);

bool ReusePortAddr(int32_t sock, int32_t reuse = 1)
{
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char*)&reuse, sizeof(reuse))) {
        perror("setsockopt(SO_REUSEPORT) error");
        return false;
    }

    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse))) {
        perror("setsockopt(SO_REUSEPORT) error");
        return false;
    }

    return true;
}

bool TimeoutConnect(int32_t sockfd, sockaddr_in addr, uint32_t timeoutMS)
{
    int flags = fcntl(sockfd, F_GETFL, 0);
    if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) {
        perror("fcntl error");
    }

    std::shared_ptr<void>(nullptr, [&](void *) {
        // 恢复套接字为阻塞模式
        fcntl(sockfd, F_SETFL, flags);
    });

    // 连接到目标地址
    int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if (ret < 0 && errno != EINPROGRESS) { // 连接失败且不是EINPROGRESS
        // EINPROGRESS 表示仍在进行连接
        printf("Failed to connect: [%d:%s]\n", errno, strerror(errno));
        return false;
    }

    // 在非阻塞模式下,需要使用 select() 或 epoll() 等函数来等待连接完成
    fd_set fdset;
    FD_ZERO(&fdset);
    FD_SET(sockfd, &fdset);
    struct timeval timeout;
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    ret = select(sockfd + 1, nullptr, &fdset, nullptr, &timeout);
    if (ret < 0) {
        printf("Failed to select: [%d:%s]\n", errno, strerror(errno));
        return false;
    } else if (ret == 0) { // 超时了
        printf("Connection timed out\n");
        return false;
    } else {  // 连接成功或失败
        int valopt = -1;
        socklen_t optlen = sizeof(valopt);
        getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&valopt, &optlen);  // 获取连接结果
        if (valopt != 0) {  // 连接失败
            printf("Failed to connect: [%d:%s]\n", valopt, strerror(valopt));
            return false;
        }
    }

    return true;
}

/*************************************************************************
    > File Name: tcp_simultaneous_open.cc
    > Author: hsz
    > Brief:
    > Created Time: 2024年10月25日 星期五 15时35分09秒
 ************************************************************************/

#include <iostream>
#include <string>
#include <cstring>
#include <thread>
#include <map>
#include <vector>
#include <thread>
#include <functional>

#include <getopt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>

#include "common.h"

#include <log/log.h>

#define LOG_TAG "TCP simultaneous open"

int32_t Bind(const char *local_host, uint16_t local_port)
{
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        printf("socket error, errno: %d, errstr: %s\n", errno, strerror(errno));
        return -1;
    }

    int32_t flag = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, (char*)&flag, sizeof(flag))) {
        perror("setsockopt(SO_REUSEPORT) error");
        close(sockfd);
        return -1;
    }

    sockaddr_in local_addr;
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = inet_addr(local_host);
    local_addr.sin_port = htons(local_port);

    if (bind(sockfd, (struct sockaddr*)&local_addr, sizeof(local_addr)) < 0) {
        perror("bind error");
        close(sockfd);
        return -1;
    }

    return sockfd;
}

void thread_func(int32_t sock, const char *peer_host, uint16_t peer_port)
{
    sockaddr_in remote_addr;
    memset(&remote_addr, 0, sizeof(remote_addr));
    remote_addr.sin_family = AF_INET;
    remote_addr.sin_addr.s_addr = inet_addr(peer_host);
    remote_addr.sin_port = htons(peer_port);

    if (peer_port == 12999) {
        usleep(10); // usleep(1000 * 1000);
    }

    LOGI("begin");
    if (TimeoutConnect(sock, remote_addr, 1000)) {
        send(sock, "hello", 6, 0);
        char buf[16] = { '\0' };
        recv(sock, buf, sizeof(buf), 0);
        LOGI("recv: %s", buf);
    }
    LOGI("end");
}

int main(int argc, char **argv)
{
    const char *bind_host = "192.168.3.10";
    int32_t firstSock = Bind(bind_host, 12999);
    int32_t secondSock = Bind(bind_host, 13999);
    if (firstSock < 0 || secondSock < 0) {
        return 0;
    }

    std::thread th1(std::bind(&thread_func, firstSock, bind_host, 13999));
    std::thread th2(std::bind(&thread_func, secondSock, bind_host, 12999));

    th1.join();
    th2.join();
    return 0;
}

测试结果

概率正常连接
抓包结果

延迟测试

由于两个套接字是客户端性质,必须保证同时处于SYN-SENT状态。
流程梳理:客户端A (127.0.0.1:12999)、客户端B(127.0.0.1:13999)
1、A向B发送SYN,此时B必须处于SYN-SENT状态,否则就会因为是客户端性质而收到RST信号
2、A收到RST信号后进入CLOSED 状态,此时B在延迟1ms后发送SYN,将同样收到RST信号

延迟探测

经过多次测试,延迟 5 - 20 微秒的概率会增大,如tcpdump的图,12999发送SYN的时间点是16:19:20.330111
收到SYN-ACK的时间点是16:19:20.330134,间隔了 23 微秒

P2P之TCP穿透

以上测试仅限本地。
公网环境存在传输时间,如果同时发送理论上能连接上的概率更大。但是考虑到现有的Nat设备,将会使概率降到很小。
以华为AX3Pro路由器为例,端口限制型,其自带的一个功能就是拒绝不请自来的SYN连接,遇到连接将会回复RST。
注:此路由器有个小特性,一般路由器经过nat后的端口是随机的,但是AX3Pro就是内网使用的端口
在这里插入图片描述

16:00:49.298805时间点1288813888发送了SYN
16:00:49.298830时间点1388812888发送了SYN
此时12888和13888都处于SYN-SENT状态,理论上相互SYN将会成功建立连接
但是在时间点16:00:49.29927216:00:49.299377 1288813888都收到了RST回复
此回复并不是内核发送,因为tcpdump从未捕获到来自对方的SYN请求
这就说明了路由器存在拒绝不请自来的SYN请求的功能

所以P2P中TCP穿透需要考虑的事情需要更多。

参考

http://www.tcpipguide.com/free/t_TCPConnectionEstablishmentProcessTheThreeWayHandsh-4.htm

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

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

相关文章

windows录屏软件工具推荐!!

如今&#xff0c;科技的进步&#xff0c;互联网的普及&#xff0c;使我们的生活越来越便利&#xff0c;录屏工具的出现&#xff0c;大大提高我们的工作效率。如果你经常需要录制屏幕上的内容&#xff0c;比如制作教学视频、游戏实况记录、演示文稿等等&#xff0c;那这几款软件…

“令牌化”革命:数据货币化如何重塑企业竞争格局

在科技日新月异的今天&#xff0c;英伟达CEO黄仁勋在Gartner IT研讨会/XPO大会上的主题演讲无疑为企业创业者们提供了一场思想的盛宴。作为科技行业的领军企业&#xff0c;英伟达不仅在图形处理器&#xff08;GPU&#xff09;领域取得了巨大成功&#xff0c;更在人工智能&#…

前端新人手册:入职第一天的环境配置秘籍

在前端开发的世界里&#xff0c;一个高效、稳定的开发环境是高效工作的基石。它不仅能够提升你的工作效率&#xff0c;还能帮助你更快地适应团队的工作节奏。本文将详细介绍前端开发需要具备的环境及工具。 开发环境 Node.js 通常我们的前端项目都是依赖Node.js环境的&#…

JavaScript入门中-流程控制语句

本文转载自&#xff1a;https://fangcaicoding.cn/article/52 大家好&#xff01;我是方才&#xff0c;目前是8人后端研发团队的负责人&#xff0c;拥有6年后端经验&3年团队管理经验&#xff0c;截止目前面试过近200位候选人&#xff0c;主导过单表上10亿、累计上100亿数据…

C++ 日志管理 spdlog 使用笔记

文章目录 Part.I IntroductionChap.I 预备知识Chap.II 常用语句 Part.II 使用Chap.I 简单使用Chap.II 自定义日志格式 Part.III 问题&解决方案Chap.I 如果文件存在则删除 Reference Part.I Introduction spdlog 是一个开源的 C 日志管理工具&#xff0c;Git 上面的地址为 …

Ovis原理解读: 多模态大语言模型的结构嵌入对齐

论文&#xff1a;https://arxiv.org/pdf/2405.20797 github:https://github.com/AIDC-AI/Ovis 在多模态大语言模型 (MLLM) 中&#xff0c;不同的嵌入策略有显著的区别。以下是使用基于连接器的方法与 Ovis 方法的比较&#xff1a; 基于连接器的方法-优缺点(connector-based …

WPF+MVVM案例实战(十)- 水波纹按钮实现与控件封装

文章目录 1、运行效果1、封装用户控件1、创建文件2、依赖属性实现 2、使用封装的按钮控件1.主界面引用2.按钮属性设置 3 总结 1、运行效果 1、封装用户控件 1、创建文件 打开 Wpf_Examples 项目&#xff0c;在 UserControlLib 用户控件库中创建按钮文件 WaterRipplesButton.x…

产品结构设计(五):结构设计原则

1. 产品结构设计总原则 1.1 合理选用材料 1、根据产品应用场所来选择 如果为日常消费类电子产品&#xff0c;产品材料就应选用强度好、表面容易处理、不容易氧化生锈、不容易磨伤、易成型的材料&#xff0c;如塑胶材料选用 PC、ABS、PCABS 等&#xff0c;金属材料选用不锈钢、…

一些待机电流波形特征

一、待机电流波形 最干净的待机电流波形应该只有paging&#xff0c;不过需要注意2点&#xff1a; 每个paging的间隔&#xff0c;不同网络可能不一样&#xff0c;有可能是320ms, 640ms 待机网络 paging 间隔 1分钟的耗电量 单个耗电量 单个待机电流 单个波形时长 4G 64…

你了解kafka消息队列么?

消息队列概述 一. 消息队列组件二. 消息队列通信模式2.1 点对点模式2.2 发布/订阅模式 三. 消息队列的优缺点3.1 消息队列的优点3.2 消息队列的缺点 四. 总结 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&…

uniapp使用easyinput文本框显示输入的字数和限制的字数

uniapp使用easyinput文本框显示输入的字数和限制的字数 先上效果图&#xff1a; 整体代码如下&#xff1a; <template><view class"nameInfoContent"><uni-easyinput class"uni-mt-5" suffixIcon"checkmarkempty" v-model&quo…

Linux云计算 |【第五阶段】CLOUD-DAY4

主要内容&#xff1a; Linux容器基础、安装Docker、镜像管理、容器管理、容器部署应用 一、容器介绍 容器&#xff08;Container&#xff09; 是一种轻量级的虚拟化技术&#xff0c;用于在操作系统级别隔离应用程序及其依赖项。容器允许开发者在同一台主机上运行多个独立的应…

MaskGCT,AI语音克隆大模型本地部署(Windows11),基于Python3.11,TTS,文字转语音

前几天&#xff0c;又一款非自回归的文字转语音的AI模型&#xff1a;MaskGCT&#xff0c;开放了源码&#xff0c;和同样非自回归的F5-TTS模型一样&#xff0c;MaskGCT模型也是基于10万小时数据集Emilia训练而来的&#xff0c;精通中英日韩法德6种语言的跨语种合成。数据集Emili…

《数字图像处理基础》学习03-图像的采样

在之前的学习中我已经知道了图像的分类&#xff1a;物理图像和虚拟图像。《数字图像处理基础》学习01-数字图像处理的相关基础知识_图像处理 数字-CSDN博客 目录 一&#xff0c;连续图像和离散图像的概念 二&#xff0c;图像的采样 1&#xff0c; 不同采样频率采样同一张图…

SSA-CNN-LSTM-MATT多头注意力机制多特征分类预测

SSA-CNN-LSTM-MATT多头注意力机制多特征分类预测 目录 SSA-CNN-LSTM-MATT多头注意力机制多特征分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现SSA-CNN-LSTM-MATT麻雀算法优化卷积神经网络-长短期记忆神经网络融合多头注意力机制多特征分类预测&…

ComfyUI - 视觉基础任务 检测(Detection) 和 分割(Segmentation) 的 Impact-Pack 流程 教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/141140498 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 在 Com…

【音视频 | ADPCM】音频编码ADPCM详细介绍及例子

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

租房市场新动力:基于Spring Boot的管理系统

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

深入理解数据链路层:以太网帧格式、MAC地址、交换机、MTU及ARP协议详解与ARP欺骗探究

&#x1f351;个人主页&#xff1a;Jupiter. &#x1f680; 所属专栏&#xff1a;Linux从入门到进阶 欢迎大家点赞收藏评论&#x1f60a; 目录 数据链路层 认识以太网以太网帧格式 认识 MAC 地址交换机与碰撞域的划分认识 MTUMTU 对 IP 协议的影响MTU 对 UDP 协议的影响 MTU 对…

SolidWorks 导出 URDF 中的惯性矩阵错误问题

系列文章目录 前言 一、 dsubhasish09于2021年5月23日发表评论 在装配体中定义由多个零件组成的 link 时&#xff0c;单个零件质心处各自的惯性值&#xff08;在使用相似性变换使其与关节坐标系平行后&#xff09;会直接相加&#xff0c;从而得到净惯性矩阵&#xff0c;而不是…