CS144-Lab2
实验架构
除了写入传入流之外,TCPReceiver
还负责通知 sender
两件事:
- “
First unassembled
” 字节的索引,称为“acknowledgment
”或 “ackno
”。这是接收方需要来自发送方的第一个字节。 - “
first unassembled
” 索引和“first unacceptable
”索引之间的距离。这称为“window size
”。
总的来说,ackno
和 window size
述了 TCPreceiver
的窗口:TCPsender
被允许发送一系列索引。使用该窗口,TCPreceiver
可以做到流量控制,使发送方限制它发送的数量,直到接收方准备好更多的数据。有时,我们将 ackno
称为窗口的“左边”( TCPRecsigner
的最小索引),而 ackno + window size
则称为“右边缘”(略大于 TCPReceiver
的最大索引)。
在编写 Stream Reassembler
和 Byte Stream
时,您已经完成了实现 TCP Receiver
所涉及的大部分算法工作;本实验是将这些通用类连接到 TCP
的细节。最困难的部分将涉及考虑 TCP
将如何表示每个字节在流中的位置——称为“sequence number
”。
我们将要实现的 TCPReceiver
需要完成的功能:
- 接收
TCP segment
- 重新组装
ByteStream
- 发送
ackno
和window size
回TCP sender
,以进行流量控制和数据确认
环境配置
当前我们的实验代码位于 master
分支,而在完成 Lab 之前需要合并一些依赖代码,因此执行以下命令:
git merge origin/lab2-startercode
之后重新 make
编译即可。
The TCP Receiver
在 Lab2,我们将实现一个 TCPReceiver,用以接收传入的 TCP segment 并将其转换成用户可读的数据流。
TCPReceiver 除了将读入的数据写入至 ByteStream 中以外,它还需要告诉发送者两个属性:
- 第一个未组装的字节索引,称为确认号ackno,它是接收者需要的第一个字节的索引。
- 第一个未组装的字节索引和第一个不可接受的字节索引之间的距离,称为 窗口长度window size。
ackno 和 window size 共同描述了接收者当前的接收窗口。接收窗口是 发送者允许发送数据的一个范围,通常 TCP 接收方使用接收窗口来进行流量控制,限制发送方发送数据。
总的来说,我们将要实现的 TCPReceiver 需要做以下几件事情:
- 接收TCP segment
- 重新组装字节流(包括EOF)
- 确定应该发回给发送者的信号,以进行数据确认和流量控制
1. Translating between 64-bit indexes and 32-bit seqnos
作为热身,我们需要实现TCP表示索引的方式。上周您创建了一个StreamReassembler,它重组子字符串,其中每个字节都有一个64位流索引,流中的第一个字节总是索引为0。64位索引足够大,我们可以将其视为永不溢出。然而,在TCP报头中,空间是宝贵的,流中的每个字节的索引不是用64位的索引表示的,而是用32位的“序列号”或“seqno”表示的。这增加了三个复杂性:
- 您的实现需要为32位整数进行规划:TCP中的流可以是任意长的——对于可以通过TCP发送的字节流的长度没有限制。但是232字节只有4GiB,并不是很大。一旦一个32位的序列号计数到232−1,流中的下一个字节的序列号将为0。
- TCP序列号从一个随机值开始:为了提高安全性,并避免被属于同一端点之间早期连接的旧段所混淆,TCP试图确保序列号不会被猜测,并且不太可能重复。所以流的序列号不是从0开始的。流中的第一个序列号是一个随机的32位数字,称为初始序列号(Initial sequence number, ISN)。这是表示SYN(流的开始)的序列号。其余的序列号在此之后正常运行:数据的第一个字节将有ISN+1 (mod 232)的序列号,第二个字节将有ISN+2 (mod 232),等等。
- 每个逻辑开始和结束占用一个序列号:除了确保接收到所有字节的数据外,TCP还确保可靠地接收流的开始和结束。因此,在TCP中SYN (start -ofstream)和FIN (end- stream)控制标志被分配了序列号。每一个都占用一个序列号。(SYN标志占用的序列号是ISN。)流中的每个数据字节也占用一个序列号。请记住,SYN和FIN不是流本身的一部分,也不是“字节”——它们表示字节流本身的开始和结束。
#ifndef SPONGE_LIBSPONGE_WRAPPING_INTEGERS_HH
#define SPONGE_LIBSPONGE_WRAPPING_INTEGERS_HH#include <cstdint>
#include <ostream>//! \brief A 32-bit integer, expressed relative to an arbitrary initial sequence number (ISN)
//! \note This is used to express TCP sequence numbers (seqno) and acknowledgment numbers (ackno)
class WrappingInt32 {private:uint32_t _raw_value; //!< The raw 32-bit stored integerpublic://! Construct from a raw 32-bit unsigned integerexplicit WrappingInt32(uint32_t raw_value) : _raw_value(raw_value) {}uint32_t raw_value() const { return _raw_value; } //!< Access raw stored value
};//! Transform a 64-bit absolute sequence number (zero-indexed) into a 32-bit relative sequence number
//! \param n the absolute sequence number
//! \param isn the initial sequence number
//! \returns the relative sequence number
WrappingInt32 wrap(uint64_t n, WrappingInt32 isn);//! Transform a 32-bit relative sequence number into a 64-bit absolute sequence number (zero-indexed)
//! \param n The relative sequence number
//! \param isn The initial sequence number
//! \param checkpoint A recent absolute sequence number
//! \returns the absolute sequence number that wraps to `n` and is closest to `checkpoint`
//!
//! \note Each of the two streams of the TCP connection has its own ISN. One stream
//! runs from the local TCPSender to the remote TCPReceiver and has one ISN,
//! and the other stream runs from the remote TCPSender to the local TCPReceiver and
//! has a different ISN.
uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint);//! \name Helper functions
//!@{//! \brief The offset of `a` relative to `b`
//! \param b the starting point
//! \param a the ending point
//! \returns the number of increments needed to get from `b` to `a`,
//! negative if the number of decrements needed is less than or equal to
//! the number of increments
inline int32_t operator-(WrappingInt32 a, WrappingInt32 b) { return a.raw_value() - b.raw_value(); }//! \brief Whether the two integers are equal.
inline bool operator==(WrappingInt32 a, WrappingInt32 b) { return a.raw_value() == b.raw_value(); }//! \brief Whether the two integers are not equal.
inline bool operator!=(WrappingInt32 a, WrappingInt32 b) { return !(a == b); }//! \brief Serializes the wrapping integer, `a`.
inline std::ostream &operator<<(std::ostream &os, WrappingInt32 a) { return os << a.raw_value(); }//! \brief The point `b` steps past `a`.
inline WrappingInt32 operator+(WrappingInt32 a, uint32_t b) { return WrappingInt32{a.raw_value() + b}; }//! \brief The point `b` steps before `a`.
inline WrappingInt32 operator-(WrappingInt32 a, uint32_t b) { return a + -b; }
//!@}#endif // SPONGE_LIBSPONGE_WRAPPING_INTEGERS_HH
这段代码定义了一个名为WrappingInt32
的类,表示相对于一个任意初始序列号(ISN)的32位整数。它被用来表示TCP序列号(seqno)和确认号(ackno)。此外,还定义了一些帮助函数,用于转换绝对序列号和相对序列号之间的关系。
具体来说,这里的WrappingInt32
类只有一个私有成员变量_raw_value
,它表示一个32位无符号整数的原始值。类中定义了一个公有构造函数explicit WrappingInt32(uint32_t raw_value)
,用于将一个无符号整数转换为WrappingInt32
对象。类还提供了一个公有成员函数uint32_t raw_value() const
,用于访问对象的原始值。
此外,还定义了两个函数wrap
和unwrap
,分别用于将绝对序列号转换为相对序列号,以及将相对序列号转换为绝对序列号。wrap
函数的输入参数为一个64位无符号整数n
和一个WrappingInt32
对象isn
,输出为一个WrappingInt32
对象,表示n
相对于isn
的相对序列号。unwrap
函数的输入参数为一个WrappingInt32
对象n
,一个WrappingInt32
对象isn
,以及一个64位无符号整数checkpoint
,输出为一个64位无符号整数,表示最接近checkpoint
并相对于isn
的序列号值为n
的绝对序列号。
最后,还定义了一些辅助函数,如operator-
,用于计算两个WrappingInt32
对象之间的差值,operator==
和operator!=
用于比较两个WrappingInt32
对象是否相等,operator<<
用于将一个WrappingInt32
对象输出到流中,以及operator+
和operator-
,分别用于将一个WrappingInt32
对象向前或向后移动一定的距离。
//! Transform an "absolute" 64-bit sequence number (zero-indexed) into a WrappingInt32
//! \param n The input absolute 64-bit sequence number
//! \param isn The initial sequence number
WrappingInt32 wrap(uint64_t n, WrappingInt32 isn) {return isn + n;
}//! Transform a WrappingInt32 into an "absolute" 64-bit sequence number (zero-indexed)
//! \param n The relative sequence number
//! \param isn The initial sequence number
//! \param checkpoint A recent absolute 64-bit sequence number
//! \returns the 64-bit sequence number that wraps to `n` and is closest to `checkpoint`
//!
//! \note Each of the two streams of the TCP connection has its own ISN. One stream
//! runs from the local TCPSender to the remote TCPReceiver and has one ISN,
//! and the other stream runs from the remote TCPSender to the local TCPReceiver and
//! has a different ISN.
uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) {uint32_t offset = n.raw_value() - wrap(checkpoint, isn).raw_value();uint64_t res = checkpoint + offset;if (offset > (0x80000000) && res >= (0x100000000)) {res -= (0x100000000);}return res;
}
这段代码包含了两个函数的实现,一个是 wrap
,另一个是 unwrap
。
wrap
函数将一个 64 位的绝对序列号转换为一个 32 位的相对序列号,以 isn
为初始序列号。
unwrap
函数将一个 32 位的相对序列号转换为一个 64 位的绝对序列号,以 isn
为初始序列号,并指定一个最近的绝对序列号 checkpoint
,函数的返回值是一个绝对序列号,它等于相对序列号 n
对应的绝对序列号,同时又尽可能接近于 checkpoint
。
其中 wrap
函数的实现比较简单,直接将输入的绝对序列号 n
加上初始序列号 isn
,得到一个相对序列号即可。
而 unwrap
函数的实现稍微复杂一些。首先计算 n
和 checkpoint
之间的偏移量 offset
,即 n
对应的绝对序列号与 checkpoint
的绝对序列号之差。然后将 offset
加到 checkpoint
上,就得到了 n
对应的绝对序列号 res
。
这里需要注意一个问题,当 offset
的值比较大时(即大于等于 0x80000000),此时 n
对应的序列号可能要“绕一圈”,从而超过了 32 位的表示范围。因此,如果 res
大于等于 0x100000000,则需要减去 0x100000000,即从头开始计数,避免超出 32 位的表示范围。
Implementing the TCP receiver
//! Receives and reassembles segments into a ByteStream, and computes
//! the acknowledgment number and window size to advertise back to the
//! remote TCPSender.
class TCPReceiver {//! Our data structure for re-assembling bytes.StreamReassembler _reassembler;//! The maximum number of bytes we'll store.size_t _capacity;std::optional<WrappingInt32> _ackno{};WrappingInt32 _isn{0};WrappingInt32 _seq{0};// the index of the last reassembled byteuint64_t _checkpt{0};public://! \brief Construct a TCP receiver//!//! \param capacity the maximum number of bytes that the receiver will//! store in its buffers at any give time.TCPReceiver(const size_t capacity) : _reassembler(capacity), _capacity(capacity) {}//! \name Accessors to provide feedback to the remote TCPSender//!@{//! \brief The ackno that should be sent to the peer//! \returns empty if no SYN has been received//!//! This is the beginning of the receiver's window, or in other words, the sequence number//! of the first byte in the stream that the receiver hasn't received.std::optional<WrappingInt32> ackno() const;//! \brief The window size that should be sent to the peer//!//! Operationally: the capacity minus the number of bytes that the//! TCPReceiver is holding in its byte stream (those that have been//! reassembled, but not consumed).//!//! Formally: the difference between (a) the sequence number of//! the first byte that falls after the window (and will not be//! accepted by the receiver) and (b) the sequence number of the//! beginning of the window (the ackno).size_t window_size() const;//!@}//! \brief number of bytes stored but not yet reassembledsize_t unassembled_bytes() const { return _reassembler.unassembled_bytes(); }//! \brief handle an inbound segmentvoid segment_received(const TCPSegment &seg);//! \name "Output" interface for the reader//!@{ByteStream &stream_out() { return _reassembler.stream_out(); }const ByteStream &stream_out() const { return _reassembler.stream_out(); }//!@}
};
TCPReceiver
的解释如下:
TCP 接收器用于接收和重新组装 TCP Segment,并计算确认号和窗口大小以返回给远程发送器。
主要成员变量和函数包括:
StreamReassembler _reassembler
:用于重新组装字节的数据结构;size_t _capacity
:TCP 接收器可以同时存储的最大字节数;std::optional<WrappingInt32> _ackno{}
:应该发送到对等端的ackno
,如果没有接收到SYN
则为空;WrappingInt32 _isn{0}
:初始序列号(ISN
);WrappingInt32 _seq{0}
:接收到的最后一个 TCP 分段的序列号;uint64_t _checkpt{0}
:最后一个重新组装的字节的索引(绝对序列号);ackno()
:返回应该发送到对等端的 ackno,如果没有接收到SYN
则为空;window_size()
:返回应该发送到对等端的窗口大小;unassembled_bytes()
:返回已存储但尚未重新组装的字节数;segment_received()
:处理传入的 TCP 分段。
该类的主要功能是:
- 接收传入的 TCP 分段,并将其发送到
StreamReassembler
进行重新组装; - 计算应该发送给远程
TCPSender
的ackno
和窗口大小,以便TCPSender
知道还有多少可用的空间; - 提供
stream_out()
函数,用于读取已重新组装的数据。
TCP receiver 在连接生命周期中的状态转移
void TCPReceiver::segment_received(const TCPSegment &seg) {/*** @note Listen state* @def not ackno().has_value() */if (!_ackno.has_value()) {// Handshakeif (seg.header().syn) {auto rd = get_random_generator();_isn = WrappingInt32(rd());_seq = seg.header().seqno;_reassembler.push_substring(move(seg.payload().copy()), 0, seg.header().fin);// SYN or FIN make _ackno+1auto ctrl = seg.length_in_sequence_space() - seg.payload().size();_ackno = WrappingInt32(move(_seq)) + ctrl + _reassembler.first_unassembled();}return;} /*** @note FIN_RECV state* @def stream_out.input_ended()*/if (_ackno.has_value() && !stream_out().input_ended()) {/*** @note SYN_RECV state* @def ackno.has_value() and not stream_out.input_ended()* @code 48 - 54*/auto index = unwrap(move(seg.header().seqno), move(_seq + 1), _checkpt); // "+ 1" for the "SYN"// data too far, considered out of dataif (index > _checkpt && ((index - _checkpt) & 0x80000000)) {return;}// data too far, considered out of dataif (index < _checkpt && ((_checkpt - index) & 0x80000000)) {return;}_reassembler.push_substring(move(Buffer(move(seg.payload().copy()))), index, seg.header().fin);_ackno = _ackno.value() + _reassembler.first_unassembled() - _checkpt;// FIN should make _ackno + 1if (stream_out().input_ended()) {_ackno = _ackno.value() + 1;}_checkpt = _reassembler.first_unassembled();}
}
这段代码实现了TCP接收端的主要逻辑,即处理TCP段并将它们重新组装成字节流。代码中有几个状态,分别对应TCP协议中的连接建立、连接终止和正常数据传输的不同阶段。
首先,如果接收端还没有收到 SYN
,就判断接收到的 TCP
段是否是 SYN
,如果是,则在随机生成一个初始序列号后,将接收到的数据传递给 StreamReassembler
进行重新组装,并更新需要发送给发送方的确认序列号 _ackno
。如果还没有收到 SYN
,则直接返回。
如果接收端已经收到了 SYN
,但还没有收到 FIN
,则在接收到的TCP段中查找相对于已经接收的字节数偏移量,并使用 StreamReassembler
对接收到的数据进行重新组装。在重新组装之后,需要更新确认序列号 _ackno
,并且如果数据流已经结束,需要将确认序列号加一。如果接收到的数据已经过期或已经在之前的数据段中处理过,则不需要重新组装数据,直接返回即可。
如果接收端已经收到了FIN,则不再接受更多的数据,并将 _ackno
加 1 以告诉发送端已经收到了所有数据。
完整代码
- wrapping_integers.hh
- wrapping_integers.cc
- tcp_receiver.hh
- tcp_receiver.cc
相关文章:

CS144-Lab2
实验架构 除了写入传入流之外,TCPReceiver 还负责通知 sender 两件事: “First unassembled” 字节的索引,称为“acknowledgment”或 “ackno”。这是接收方需要来自发送方的第一个字节。“first unassembled ” 索引和“first unacceptable…...

Linux内核驱动之efi-rtc
Linux内核驱动之efi-rtc1. UEFI与BIOS概述1.1. BIOS 概述1.1.1. BIOS缺点:1.1.2. BIOS的启动流程1.2 UEFI 概述1.2.1 Boot Sevices:1.2.2. Runtime Service:1.2.3. UEFI优点:1.2.4. UEFI启动过程:1.3 Legacy和UEFI1.4 …...

Java 字符串
文章目录一、API二、String1. String 构造方法2. String 对象的特点3. 字符串的比较4. 用户登录案例5. 遍历字符串6. 统计字符次数7. 拼接字符串8. 字符串反转三、StringBuilder1. 构造方法2. 添加及反转方法3. 与 String 相互转换4. 拼接字符串升级版5. 字符串反转升级版一、A…...

麦克风阵列波束基本概念理解
波束形成 本质上是设计合适的滤波器,对于一类固定滤波器系数的阵列来说,无论输入信号或者噪声信号的统计特征如何,其滤波器系数固定不变,此类波束形成叫Fixed Beamforming,固定波束形成好比传统数字信号处理里面的经典…...

JAVA保姆式JDBC数据库免费教程之02-连接池技术
连接池 连接池概念 概念:其实就是一个容器(集合),存放数据库连接的容器。 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完…...

视频片段怎么做成gif图?快试试这2种方法
动态gif图片作为当下非常常用的表情包,其丰富的内容生动的画面深受大众喜爱。那么,当我们想要将电影或是电视剧中的某一片段做成gif动态图片的时候,要如何操作呢?接下来,给大家分享两招视频转化gif的小窍门–使用【GIF…...

2.20计算机如何工作
一.计算机组成1.冯诺依曼体系CPU 中央处理器: 进行算术运算和逻辑判断.存储器: 分为外存和内存, 用于存储数据(使用二进制方式存储)输入设备: 用户给计算机发号施令的设备.输出设备: 计算机个用户汇报结果的设备内存和外存的区别(面试)访问速度:内存快,外存慢存储空间:内存小,外…...
[golang gin框架] 5.Cookie以及Session
1.Cookie(1).介绍HTTP 是无状态协议,简单地说,当浏览了一个页面,然后转到同一个网站的另一个页面,服务器无法认识到这是同一个浏览器在访问同一个网站,每一次的访问,都是没有任何关系的,如果要实现多个页面之间共享数据的话就可以…...

【牛客刷题专栏】0x0B:JZ3 数组中重复的数字(C语言编程题)
前言 个人推荐在牛客网刷题(点击可以跳转),它登陆后会保存刷题记录进度,重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏:个人CSDN牛客刷题专栏。 题目来自:牛客/题库 / 在线编程 / 剑指offer: 目录前言问题…...

js中的隐式类型转换有哪些
目录一、隐式类型转换条件二、 的隐式类型转换三、 的隐式类型转换四、object 的隐式类型转换探讨 object 的隐式转换执行顺序探讨 Symbol.toPrimitive 属性如何将对象转换为原始值在前端js这门动态弱类型语言中,不仅存在着显示类型转换,还存在许多隐式类…...
WuThreat身份安全云-TVD每日漏洞情报-2023-02-17
漏洞名称:IBM Aspera Faspex 预身份验证 RCE 漏洞 漏洞级别:高危 漏洞编号:CVE-2022-47986 相关涉及:IBM Aspera Faspex 漏洞状态:POC 参考链接:https://tvd.wuthreat.com/#/listDetail?TVD_IDTVD-2023-02805 漏洞名称:Kardex Mlog MCC PATH 遍历 漏洞级别:严重 漏洞编号:CVE…...
掌握MySQL分库分表(三)水平分库分表常见策略range、hash
文章目录range策略Range策略延伸基于Range范围分库分表业务场景hash取模案例规则水平分库分表,根据什么规则进行划分? range策略 自增id,根据ID范围进行分表(左闭右开) 规则案例: 1~1,000,000 是 table…...

CTFer成长之路之CTF中的SQL注入
CTF中的SQL注入CTF SQL注入 SQL注入-1 题目描述: 暂无 docker-compose.yml version: 3.2services:web:image: registry.cn-hangzhou.aliyuncs.com/n1book/web-sql-1:latestports:- 80:80启动方式 docker-compose up -d 题目Flag n1book{union_select_is_so_cool} Wri…...
python snap7读写PLC
主要在DB块里操作 读DB块 import snap7 import structdef plc_connection():PLC_IP 192.168.10.10PLC snap7.client.Client()PLC.connect(PLC_IP, rack0, slot1)return PLCPLC plc_connection()PLC.read_area()方法从PLC中读取指定区域的数据。 1200表示DB块的编号&#x…...

使用物联网进行智能能源管理的10大优势
如今,物联网推动了许多行业的自动化流程和运营效率,而物联网在能源领域的应用尤其受到消费者、企业甚至政府的关注。除了对电力供应链的诸多好处之外,物联网能源管理系统还让位于新的智能电网,并有望实现更高的安全性和效率。基于…...

工业4.0和工业物联网如何协同工作
虽然许多公司已经接受了工业物联网,但他们现在必须接受工业4.0对数据驱动的数字化转型的承诺。随着制造业、能源、公用事业和供应链应用迅速采用工业物联网(IIoT),这些行业的新现实正在形成。工业物联网提供了企业管理数千个活动部件所需的数据类型&…...

Python机器学习入门笔记(3)—— 线性回归
目录 线性回归 算法简述 LinearRegression() API SGDRegressor API LinearRegression() 和 SGDRegressor对比 过拟合与欠拟合 岭回归 应用场景 线性回归 算法简述 线性回归是一种基本的机器学习算法,它用于建立自变量和因变量之间的线性关系模型。它假设…...

Java:顶级Java应用程序服务器 — Tomcat、Jetty、GlassFish、WildFly
如果你想编写Java web应用程序,首先需要做出一个艰难的决定:选择运行应用程序的Java应用程序服务器。什么是应用服务器?一般来说,应用程序服务器执行Java应用程序。在操作系统中启动它们,然后将应用程序部署到其中。将应用程序服…...

如何在SpringBoot项目上让接口返回数据脱敏,一个注解即可
1 背景需求是某些接口返回的信息,涉及到敏感数据的必须进行脱敏操作2 思路①要做成可配置多策略的脱敏操作,要不然一个个接口进行脱敏操作,重复的工作量太多,很显然违背了“多写一行算我输”的程序员规范。思来想去,定…...

python 之 海龟绘图(turtle)
注:从个人博客园移植而来 使用简介 python 2.6引入的一个简单的绘图工具,俗称为海龟绘图。3.x以上使用的话,可通过pip进行安装,命令为: pip/pip3 install turtle如果出现如下错误: 解决方式: …...

并发编程实战(生产者消费者模型)
在并发编程中使用生产者和消费者模式能够解决绝大多数的并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度。 生产者和消费者模式: 在线程的世界中生产者就是产生数据的线程,而消费者则是消费数据的线程。在多线程开…...

亚远景科技助力东风日产通过ASPICE CL2评估
热烈祝贺东风日产通过ASPICE CL2评估 近日,东风日产PK1B VCM热管理项目成功通过ASPICE CL2级能力评估,标志着东风日产在汽车电子软件研发管理体系及技术创新能力上已达到国际领先水平,为其全球化布局注入强劲动能。 ASPICE:国际竞…...
C#文件压缩与解压缩全攻略:使用ZipFile与ZipArchive实现高效操作
C#文件压缩与解压缩全攻略:使用ZipFile与ZipArchive实现高效操作 在.NET 开发中,文件压缩与解压缩是常见的需求。无论是减少存储空间、加速网络传输,还是实现数据备份,System.IO.Compression命名空间都提供了强大的工具。本文将结…...
数论——同余问题全家桶3 __int128和同余方程组
数论——同余问题全家桶3 __int128和同余方程组 快速读写和__int128快速读写__int128 中国剩余定理和线性同余方程组中国剩余定理(CRT)中国剩余定理OJ示例模板题曹冲养猪 - 洛谷模板题猜数字 - 洛谷 扩展中国剩余定理扩展中国剩余定理OJ示例模板题扩展中国剩余定理(…...
机器学习算法时间复杂度解析:为什么它如此重要?
时间复杂度的重要性 虽然scikit-learn等库让机器学习算法的实现变得异常简单(通常只需2-3行代码),但这种便利性往往导致使用者忽视两个关键方面: 算法核心原理的理解缺失 忽视算法的数据适用条件 典型算法的时间复杂度陷阱 SV…...

智能手表健康监测系统的PSRAM存储芯片CSS6404LS-LI—高带宽、耐高温、微尺寸的三重突破
一、直击智能手表三大核心痛点 痛点场景风险传统方案缺陷连续生物数据流存储100Hz PPG信号产生82MB/s数据洪峰SPI NOR Flash带宽不足(≤50MB/s)高温环境稳定性腕表表面温度达50℃(烈日/运动场景)商用级存储器件(85℃)易触发数据错误极限空间约束PCB面积…...
TeamCity Agent 配置完整教程(配合 Docker Compose 快速部署)
在《使用 Docker Compose 从零部署 TeamCity PostgreSQL(详细新手教程)》中,我们成功部署了 TeamCity Server 和数据库服务。但要真正运行构建任务,还需要至少一个 Build Agent(构建代理)。 本教程将继续…...

敏捷开发中如何避免过度加班
在敏捷开发过程中避免过度加班,需要明确敏捷原则、合理规划迭代任务、加强团队沟通、优化流程效率、设定合理的工作负荷、注重团队士气和成员健康。明确敏捷原则,即保证可持续发展的步调,避免频繁地变更需求、过度承诺任务量。合理规划迭代任…...
【PhysUnits】16.1 完善Var 结构体及其运算(variable.rs)
一、源码 这段代码定义了一个泛型结构体 Var,并为它实现了各种数学运算。 /** 变量结构体 Var* 该结构体泛型参数 T 需满足 Numeric 约束*/use core::ops::{Neg, Add, Sub, Mul}; use crate::constant::Integer; /// 定义 Numeric trait,约束 T 必须实…...
C/C++ 面试复习笔记(4)
1.在多线程的 Linux 程序中,调用系统函数(如pthread_create 创建线程、pthread_mutex_lock 锁定互斥锁等)可能会返回错误码。 与单线程环境相比,多线程环境下的错误处理有哪些需要特别注意的地方?请举例说明如何在多线…...