linux网络编程5——Posix API和网络协议栈,使用TCP实现P2P通信

news2024/11/24 8:59:12

文章目录

  • Posix API和网络协议栈,使用TCP实现P2P通信
    • 1. socket()
    • 2. bind()
    • 3. listen()
    • 4. connect()
    • 5. accept()
    • 6. read()/write(), recv()/send()
    • 7. 内核tcp数据传输
      • 7.1 TCP流量控制
      • 7.2 TCP拥塞控制——慢启动/拥塞避免/快速恢复/快速重传
    • 8. shutdown()
    • 9. close()
      • 9.1 正常情况
      • 9.2 主动关闭方在fin_wait_1状态下先收到FIN
      • 9.3 双方同时收到FIN报文
    • 10. 使用TCP协议实现点对点通信
    • 学习参考

Posix API和网络协议栈,使用TCP实现P2P通信

本文介绍了linux Posix API涉及网络编程的常用函数,并解释其原理和涉及的网络协议栈。最后,利用TCP三次握手中同时打开建立连接的情况实现了P2P通信。

TCP状态转换图

1. socket()

socke()的主要作用是分配fd和建立tcb(tcp control block)

  • 分配fd,内部通过一个bitmap标记一个fd是否已被使用。
  • 建立tcb,alloc(),此时还没有分配接受缓冲区和发送缓冲区。

TCB 是操作系统内核中用于跟踪每个 TCP 连接的核心数据结构,包含了与连接相关的状态信息,比如源地址、目标地址、端口号、窗口大小、序列号等。

2. bind()

将本地ip和和端口绑定到fd对应的tcb中。客户端fd如果bind了的话,则本地端口就会固定。

3. listen()

  1. 将监听套接字tcb中的status设置为TCP_STATUS_LISTEN。
  2. 为监听套接字tcb分配两个队列:半连接队列syn_queue和全连接队列accept_queue
    • syn_queue,存储还未完成三次握手的请求。Linux 中的 tcp_max_syn_backlog决定了 syn_queue 的最大容量。
    • accpet_quque,存储已经完成三次握手,等待应用层调用accpet()的请求。如 Linux 中的 somaxconn决定了其容量。
int listen(int sockfd, int backlog);

其中backlog最早70年代指的是syn_queue队列的长度,也就是收到SYN但是还没有完成三次握手的请求的队列长度。中间指的是两个队列的长度之和的最大值。现在指的是accpet队列最大长度。这样应用程序可以主要关注待处理连接请求的数量。

tcp连接的生命周期,从什么时候开始?

从收到第一个SYN报文的时候开始,开始创建tcb,连接开始建立。

第三次握手的数据包,如何从半连接队列查找匹配的节点?

每一个TCP报文段中都包含源ip、目的ip、源端口等五元组信息,据此可以查找匹配。

如何解决SYN泛洪/DDOS攻击?

限制半连接队列的最大长度。

4. connect()

connect()用于客户端主动建立与对端的连接,可能有两种情况。

三次握手主要是为了通信的同步,交换序号信息,保证之后的通信不丢失、不乱序。

  1. 正常主动打开
image-20241025125049588
  1. 同时打开

适用于P2P通信,需要双方同时调用connect()。

image-20241025130536095

5. accept()

用于从全连接队列中取出一个连接,并分配fd。

水平触发

每次只接受一个连接,效率较低。

边缘触发 + 非阻塞IO

使用一个循环,如果accept返回-1并且errno为EAGAIN或者EWOULDBLOCK,则退出循环。

while (1)
{
	fd = accept(listen_fd, &clnt_addr, &clnt_addr_len);
	if (fd == -1)
	{
		if (errno == EAGAIN || errno == EWOULDBLOCK)
			break;
	}
	// 处理新连接
}

6. read()/write(), recv()/send()

这些IO函数都是与系统内的接收缓冲区和发送缓冲区之间传输数据,而非直接与网卡。tcp报文段的接受与发送和这些IO函数没有一对一的关系。

7. 内核tcp数据传输

TCP协议头

在这里插入图片描述

7.1 TCP流量控制

滑动窗口

TCP使用了滑动窗口中的回退n自动重复请求机制。原理是当某个数据段未被正确接收,则接收方不会确认其后续的数据段;当发送方发现超时或者收到重复的ACK时,会回退到未被确认的第一个数据段继续发送。

延迟确认

如果数据乱序到达,可以等待一段时间,在进行确认。

超时重传

7.2 TCP拥塞控制——慢启动/拥塞避免/快速恢复/快速重传

慢启动是 TCP 在建立连接或从网络拥塞中恢复时使用的拥塞控制算法,旨在探测网络的可用带宽,并逐步增加发送速率,避免在一开始就超载网络。

发送端在收到一个ACK之前,发送窗口的大小是由接收端接收窗口大小和本端拥塞窗口的大小决定的。

慢启动算法是指发送端拥塞窗口在到达慢启动阈值之前(称之为慢启动姐阶段)进行指数级增长。到达ssthresh(slow start threshhold)后进入拥塞避免阶段,拥塞窗口进行线性增长。当超时重传时,ssthresh变为当前拥塞窗口的一半,并重新开始慢启动阶段。

快速恢复用于在丢包被检测到(连续收到三个重复ACK)后,不必将拥塞窗口完全重置为 1 MSS,而是通过调整 cwndssthresh,更快地恢复到稳定传输的状态。

连续收到三个重复ACK,说明这与超时不同,表明网络可能并未完全拥塞,仍然存在一定的可用带宽。

具体来说,当检测到丢包时,发送方将 慢启动阈值 (ssthresh) 设置为当前 cwnd 的一半,然后发送方将 cwnd 减小到 ssthresh 的大小,同时跳过慢启动阶段,直接进入拥塞避免阶段。收到连续三个重复ACK时,也会对数据进行快速重传,而非等到超时。

8. shutdown()

进行半关闭,关闭写端。不推荐使用,因为它会使代码逻辑变复杂。

9. close()

当通信两端有调用close()来关闭tcp连接时,可能会发生以下三种交互情况。

9.1 正常情况

image-20241023201936418

9.2 主动关闭方在fin_wait_1状态下先收到FIN

image-20241023202659220

9.3 双方同时收到FIN报文

image-20241023202946556

10. 使用TCP协议实现点对点通信

参考4. connect()中的同时打开模型。让双方同时(在超时周期之内)调用connect即可。演示代码如下:

client1.c

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


#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    struct sockaddr_in local_addr = {0}, peer_addr = {0};
    int sock_fd = socket(PF_INET, SOCK_STREAM, 0);

    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    local_addr.sin_port = htons(2000);


    peer_addr.sin_family = AF_INET;
    peer_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    peer_addr.sin_port = htons(2001);

    if (bind(sock_fd, (struct sockaddr *)&local_addr, sizeof local_addr) == -1)
    {
        perror("bind()");
        close(sock_fd);
        return 1;
    }

    char message[100] = "hello I am KAKA";

    while (1)
    {
        if (connect(sock_fd, (struct sockaddr *)&peer_addr, sizeof peer_addr) == -1)
        {
            usleep(50);
            continue;
        }


        write(sock_fd, message, strlen(message));

        close(sock_fd);
        break;
    }


    return 0;
}

client2.c

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


#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(int argc, char const *argv[])
{
    struct sockaddr_in local_addr = {0}, peer_addr = {0};
    int sock_fd = socket(PF_INET, SOCK_STREAM, 0);

    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    local_addr.sin_port = htons(2001);

    if (bind(sock_fd, (struct sockaddr *)&local_addr, sizeof local_addr) == -1)
    {
        perror("bind()");
        close(sock_fd);
        return 1;
    }

    peer_addr.sin_family = AF_INET;
    peer_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    peer_addr.sin_port = htons(2000);

    char buf[100];
    int received;

    while (1)
    {
        if (connect(sock_fd, (struct sockaddr *)&peer_addr, sizeof peer_addr) == -1)
        {
            usleep(50);
            continue;
        }


        if ((received = read(sock_fd, buf, sizeof buf - 1)) != 0)
        {
            if (received == -1)
            {
                perror("read()");
                close(sock_fd);
                return 1;
            }

            buf[received] = 0;
            
            printf("from peer:\n%s\n", buf);
        }

        close(sock_fd);
        break;
    }

    return 0;
}

学习参考

学习更多相关知识请参考零声 github。

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

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

相关文章

Jvm中的堆和栈

JVM中的堆和栈分别存放不同的数据类型和内容。 ‌栈&#xff08;Stack&#xff09;‌&#xff1a; 存储基本数据类型&#xff08;如int, char, boolean等&#xff09;和对象的引用。存储局部变量、方法调用、程序运行状态、方法返回值等。每个线程都有一个独立的线程栈&#…

【 IC每日一题】

IC每日一题 1 八股题&#xff1a;低功耗设计方法1.1 功耗类型1.1.1 动态功耗1.1.2 静态功耗 1.2 SoC低功耗设计方法1.2.1 基于时钟clock的低功耗设计1.2.2 基于电压域voltage的低功耗设计1.2.3 多阈值库1.2.4 RTL低功耗设计 2 手撕题&#xff1a;序列检测2.1 移位寄存器写法2.2…

ClickHouse 3节点集群安装

ClickHouse 简介 ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。 官方网站&#xff1a;https://clickhouse.com/ 项目地址&#xff1a;https://github.com/ClickHouse/ClickHouse 横向扩展集群介绍 此示例架构旨在提供可扩展性。它包括三个节点&#xff…

ffmpeg视频滤镜: 色温- colortemperature

滤镜简述 colortemperature 官网链接 》 FFmpeg Filters Documentation 这个滤镜可以调节图片的色温&#xff0c;色温值越大显得越冷&#xff0c;可以参考一下下图&#xff1a; 咱们装修的时候可能会用到&#xff0c;比如选择灯还有地板的颜色的时候&#xff0c;选暖色调还是…

提升产品竞争力之--IPD产品成本篇

在汉捷的咨询过程中&#xff0c;很多企业老总交流时都会提起这个抱怨&#xff1a;“现在产品竞争太激烈了&#xff0c;客户买产品首先看价格&#xff0c;你价格高一点就买别家的啦……” 汉捷咨询在前文谈到“通过定义产品包需求&#xff0c;来提升产品竞争力。差异化开发&…

Maven - Assembly实战

文章目录 PreAssembly插件基本配置使用示例示例1&#xff1a;创建包含依赖的JAR包示例2&#xff1a;自定义描述符示例3&#xff1a;多模块项目打包 实战 _qiwenfile结构pom.xml触发脚本 实战 _nacos输出 zip / tar.gz常见问题及解决方案 Pre Spring Boot - 瘦身大作战&#xf…

Go 版本升级 | 统计 Github 社区 Go 版本分布情况

背景 因为最近三年用的 Go 版本是 1.16&#xff0c;但最新的版本升级到了 1.23&#xff0c;很多依赖的三方包最新文件都已经升级&#xff0c;使用了泛型以及 GO 新版本的特性&#xff0c;导致我只能适配 Go1.16 的三方包旧版本&#xff0c;但这种问题发生的频率多了后&#xf…

解决运行jar错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序

报错 众所周知jdk8以上都没有Javafx java -jar target/myyscan-1.0-SNAPSHOT.jar 错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序解决 https://gluonhq.com/products/javafx/ 去下载/javafx/到本地&#xff0c;选择自己的型号 然后记得指定路径 java --m…

React类组件详解

React类组件是通过创建class继承React.Component来创建的&#xff0c;是React中用于构建用户界面的重要部分。以下是对React类组件的详细解释&#xff1a; 一、定义与基本结构 类组件使用ES6的class语法定义&#xff0c;并继承自React.Component。它们具有更复杂的功能&#xf…

R语言机器学习算法实战系列(十二)线性判别分析分类算法 (Linear Discriminant Analysis)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍LDA的原理LDA的步骤教程下载数据加载R包导入数据数据预处理数据描述数据切割构建模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve保存模型总结优点:缺…

《性能之巅:洞悉系统、企业与云计算》-观测工具-笔记

《性能之巅&#xff1a;洞悉系统、企业与云计算》第一章&#xff08;绪论&#xff09;和第二章&#xff08;方法&#xff09;的笔记&#xff0c;请参考Part 1&#xff0c;第三章&#xff08;操作系统&#xff09;的笔记&#xff0c;请参考Part 2&#xff0c;本文是第四章——观…

鸿蒙开发融云demo发送文本消息

鸿蒙开发融云demo发送文本消息 融云鸿蒙版是不带UI的&#xff0c;得自己一步步搭建。 这次说如何发送文本消息&#xff0c;并且显示文本消息 一、思路 发送用&#xff1a;IMEngine.getInstance().sendMessage 显示文本&#xff1a; Text(ImUtils.dealMyTextContent(this.ms…

Linux:磁盘深潜:探索文件系统、连接之道与库的奥秘

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一 磁盘 1.1 磁盘的物理结构 1.2 磁盘的存储结构 1.3 磁盘的逻辑结构 ​编辑二 文件系统 2.1 简单了解 2.2 inode 2.3 文件描述符&#xff0c;进程与文件系统的关系 ​编辑2…

格姗知识圈博客网站开源了!

格姗知识圈博客 一个基于 Spring Boot、Spring Security、Vue3、Element Plus 的前后端分离的博客网站&#xff01;本项目基本上是小格子一个人开发&#xff0c;由于工作和个人能力原因&#xff0c;部分技术都是边学习边开发&#xff0c;特别是前端&#xff08;工作中是后端开…

模型选择拟合

1.通过多项式拟合交互探索概念 import math import numpy as np import torch from torch import nn from d2l import torch as d2l 2.使用三阶多项式来生成训练和测试数据的标签 max_degree 20 # 多项式的最大阶数 n_train, n_test 100, 100 # 训练和测试数据集大小 true…

SQL Server 当前日期及其未来三天的日期

当前日期及其未来三天的日期&#xff0c;并分别以 YYYY-MM-DD 和 yyyyMMdd 的格式展示 1、当前日期及其未来三天的日期&#xff0c;以 YYYY-MM-DD的格式展示 WITH CurrentDate AS (SELECT GETDATE() AS 当前日期 ) -- 使用 CONVERT 函数 SELECTCONVERT(VARCHAR(10), 当前日期,…

【论文笔记】MLSLT: Towards Multilingual Sign Language Translation

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: MLSLT: Towards Multiling…

springboot医疗物品采购系统-计算机设计毕业源码10210

摘 要 本文基于Spring Boot框架&#xff0c;设计并实现了一个医疗物品采购系统。该系统旨在解决医疗物品采购中的管理和信息化问题&#xff0c;提供便捷的服务和支持。通过系统的设计与实现&#xff0c;实现了医疗物品的供应商家管理、物品类型管理、物品仓库管理、采购计划管…

小渡Ai论文写作:文献综述黑科技LitMaps30s找到所有文献

✨Litmaps 究竟是什么呢&#xff1f; Litmaps 是一款极为强大的文献搜索与追踪工具&#xff01; 它不但能够助力你找到相关的研究文献&#xff0c;还能够依据你提供的文献生成一张学术地图&#xff0c;清晰地展现文献之间的引用链以及研究发展趋势。 恰似一张璀璨的星空图&a…

Linux中安装配置SQLite3,并实现C语言与SQLite3的交互。

前言 SQLite 是一个软件库&#xff0c;实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是在世界上最广泛部署的 SQL 数据库引擎。本次实验介绍在Linux上实现C语言和SQLite3的交互&#xff0c;利用C语言编写相关语句&#xff0c;连接数据库、操作数…