UNIX网络编程 - Chapter 4 基本TCP套接字编程
Chapter 4 基本TCP套接字编程
1 |
|
1 | #include "unp.h" |
IPv4(sockaddr_in), 定义在<netinet/in.h>
POSIX定义
1 | struct in_addr { |
POSIX数据类型为:

IPv4地址和TCP或UDP端口在套接字地质结构中总是以网络字节序来存储
32位IPv4地址存在两种不同的访问方法。举例来说,如果serv定义为某个网际套接字地址结构,那么serv.sin_addr将按in_addr结构引用其中的32位IPv4地址,而serv.sin_addr.s_addr将按in_addr_t(通常是一个无符号的32位整数)引用同一个32位IPv4地址。因此,我们必须正确地使用IPv4地址,尤其是在将它作为函数的参数时,因为编译器对传递结构和传递整数的处理是完全不同的。
Rust在编译时避免未定义行为而不是在运行时
使用Box来在堆中定义变量:
1 | let a = Box::new([0; 1_000_000]); |
fn dereferencing() {
let mut x: Box<i32> = Box::new(1);
println!("x = {}", x);
let a: i32 = *x;
*x += 1;
let r1: &Box<i32> = &x; // r1 points to x on the stack
/**
* println!("{}", variable):用于用户友好的显示。它会通过 Display trait 自动解引用指针和引用,直到找到一个具体的值来显示。
* println!("{:p}", variable):用于调试和底层观察。它会打印出引用或指针变量本身存储的那个内存地址。
*/
println!("r1 = {:p}", r1);
/**
* 下面是错误的, 因为rust为了内存安全规定: 一个值在同一时间只能有一个owner;
*/
// let b: Box<i32> = *r1;
let b: i32 = **r1;
println!("b = {}", b);
let r2: &i32 = &*x; // &*x 确实得到了 *x 所在位置的“地址”,但 Rust 把它包装成了一个更安全、更受限制的类型,叫做“引用 (&)”
println!("r2 = {}", r2);
let c = *r2;
println!("c = {}", c);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
```mermaid
graph LR
subgraph "内存区域"
subgraph "栈 (Stack Memory)"
direction TB
addr_x["@ 0x7ffc_A0"] -- "变量 x" --> val_x["<b>值: 0x55ef_B0</b><br/>(类型: Box<i32>)"]
addr_a["@ 0x7ffc_A8"] -- "变量 a" --> val_a["值: 1<br/>(类型: i32)"]
addr_r1["@ 0x7ffc_AC"] -- "变量 r1" --> val_r1["<b>值: 0x7ffc_A0</b><br/>(类型: &Box<i32>)"]
addr_b["@ 0x7ffc_B4"] -- "变量 b" --> val_b["值: 2<br/>(类型: i32)"]
addr_r2["@ 0x7ffc_B8"] -- "变量 r2" --> val_r2["<b>值: 0x55ef_B0</b><br/>(类型: &i32)"]
addr_c["@ 0x7ffc_C0"] -- "变量 c" --> val_c["值: 2<br/>(类型: i32)"]
end
subgraph "堆 (Heap Memory)"
addr_heap["@ 0x55ef_B0"] -- "Box<T>指向的数据" --> val_heap["值: 2<br/>(类型: i32)"]
end
end
%% 箭头表示“指向”关系
val_x -- "指向堆数据" --> addr_heap
val_r1 -- "引用栈上的 x" --> addr_x
val_r2 -- "引用堆上的数据" --> addr_heap
%% 样式
style val_x fill:#f9f,stroke:#333
style val_r1 fill:#ccf,stroke:#333
style val_r2 fill:#9cf,stroke:#333
style addr_heap fill:#f96,stroke:#333,stroke-width:2px
一些误区:
1
2
let x = Box::new(0);
let y = Box:new(&x);
这时候`***y` 才指向值`0`, `*y`是`x`的引用(`&x`)类型是`&Box<i32>`, `**y`指向`x`本身(`x: Box<i32>`), `***y`才指向`*x`(即x指向的堆的值`0`).
Pointer Safety Principle: data should never be aliased and mutated at the same time.
数据不允许同时别名和修改
以Vec为例:
1 | let mut v: Vec<i32> = vec![1, 2, 3]; |
Vec的结构是这样的:
, Vec 是一个在栈上的结构体,它包含三个部分:一个指向堆内存的指针 (pointer)、长度 (length) 和容量 (capacity). 其中len代表Vec中实际有多少个元素, capacity代表v的容量是多少(可以放多少元素), 如果v.push之后超过了capacity, buf中的指针就会重新开辟一块内存转移数据并且释放掉原先的内存.
那么如果进行下面的操作
1 | let mut v: Vec<i32> = vec![1, 2, 3]; // 这里使用vec!宏来初始化Vec容器, 那么默认len = capacity = 3 |
Permission:
Place: 任何可以放在赋值符号左侧的都可以是place, 比如: Variables, Dereferences of places, Array accesses of places, Field of places like tuples or a.field for structs, any combination of the above
place默认有R(ead), O(wn)的权限, 如果赋值的时候使用mut符号, 那么还会增加W权限
1 | let v: Vec<i32> = vec![1, 2, 3]; // v 只有R和O权限 |
**首先要明确, 如果place离开作用域, 权限就会被收回, eg: **
1 | fn main() { |
一个解决方法:
1 | // 第一个解决方法 |
**immutable references(shared references): ** 可以在不移动所有权的情况下提供临时的访问, 但是不能修改数据. 同一时间可以有很多Immutable references
**mutable references(unique references): ** 可以对数据进行修改, 但是同一时间只能有一个mutable reference
1 | let mut v: Vec<i32> = vec![1, 2, 3]; |
O (Own) 权限永远属于数据的所有者(在这里是 v)。引用 (& 或 &mut) 只是临时“借用”了 R 或 R+W 权限。*num 作为一个“位置 (place)”,它允许被读取和写入,但对这块数据的“生杀大权”(移动或销毁)仍然在 v 手里。
some questions:
Q&A:
1
2
3
4
5
6
7 fn main() {
let mut s = String::from("Hello");
let t = &mut s;
/* here */ // s在这里没有任何权限, 因为t是mutable reference, 所以当t生命周期没有结束的时候, s失去了所有权限
t.push_str(" world");
println!("{}", s);
}
直接来看例子:
1 | fn main() { |
因为编译器无法看到所有函数的周期, 他只能看到函数的signature(输入和输出), 所以如果引用从函数中返回的时候是安全的, 就会获得F权限
F(low)权限: 视为 “出口许可证”。 只有当编译器能够证明该引用从其当前上下文中“流出”(例如从函数返回)是安全的时,引用才会获得 F 权限。
1 | fn first(strings: &Vec<String>) -> &String { |
不安全的行为:
1 | /** |
另一个例子:
1 | fn main() { |
修改后:
1 | fn first_or2<'a>(strings: &'a Vec<String>, default: &'a String) -> &'a String { |
'a。strings 必须至少活得和 'a' 一样长。default 也必须至少活得和 'a' 一样长。'a' 一样长。这里的 'a 实际上代表了 strings 和 default 中较短的那个生命周期。
编译器看到调用 first_or(&strings, &default)。
它比较了 strings(生命周期短)和 default(生命周期长)的有效期。
它确定了那个更短的有效期,并将其作为这次调用的 'a。
函数返回的 final_ref 因此也获得了这个较短的生命周期 'a。
所以,final_ref 的有效范围被严格限定在了那个内部 {} 之中。
当代码试图在 {} 外部使用 println!("{}", final_ref) 时,审查员会立刻发现:final_ref 的生命周期已经结束了,它已经失效了!
审查员报告一个新的、明确的错误:“你正在使用一个已经过期的引用。”
重点: ** 当一个函数返回的引用可能来自多个不同的输入时,编译器会因为无法判断这个引用的“保质期”而拒绝编译。生命周期 'a 就是你给编译器提供的一个明确的“保质期”标签**,你向它承诺,返回的引用的有效期不会超过所有相关输入中最先失效的那个。这样,编译器就能确保安全了。
Q1:
1
2
3
4
5
6
7
8 fn incr(n: &mut i32) {
*n += 1;
}
fn main() {
let mut n = 1;
incr(&n);
println!("{n}");
}上面这个程序不会通过编译, 因为
incr(&n)传递的是immutable reference, 应该修改为incr(&mut n)Q2:
1
2
3
4
5
6
7 fn main() {
let mut s = String::from("hello");
let s2 = &s;
let s3 = &mut s;
s3.push_str(" world");
println!("{s2}");
}这个程序也无法通过编译, 因为s2是immutable reference,所以在s2生命周期还没有结束的时候, 对s3操作是不合法的:
借用规则:“你不能在存在一个活跃的不可变借用的同时,再创建一个可变借用。”Q3:
Consider this Rust function that pushes a number onto the end of a vector, and then removes and returns the number from the front of the vector:
1
2
3
4 fn give_and_take(v: &Vec<i32>, n: i32) -> i32 {
v.push(n);
v.remove(0)
}Normally, if you try to compile this function, the compiler returns the following error:
1 error[E0596]: cannot borrow `*v` as mutable, as it is behind a `&` reference --> test.rs:2:5 |1 | fn give_and_take(v: &Vec<i32>, n: i32) -> i32 { | --------- help: consider changing this to be a mutable reference: `&mut Vec<i32>`2 | v.push(n); | ^^^^^^^^^ `v` is a `&` reference, so the data it refers to cannot be borrowed as mutableAssume that the compiler did NOT reject this function. Select each (if any) of the following programs that could possibly cause undefined behavior if executed. If none of these programs could cause undefined behavior, then check “None of these programs” .
**A: **这里主要的错误是
Vecpush的时候可能超过capacity, 导致v内部值的地址改变, 最终导致悬垂指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14 let v = vec![1, 2, 3];
let n = &v[0];
give_and_take(&v, 4);
println!("{}", n); // 这里就会导致错误, 此时n为悬垂指针
let v = vec![1, 2, 3];
let n = &v[0];
let k = give_and_take(&v, 4); // 这里结束之后, n为悬垂指针了, 但是n在后续并没有使用(生命周期已经结束), 所以编译器不会报错
println!("{}", k);
let v = vec![1, 2, 3];
let v2 = &v;
give_and_take(&v, 4); // 这里改变的是v内部buf中ptr的值, 而不是v结构体的地址,所以当内部地址改变的时候v2内部也会发生改变, 不会报错
println!("{}", v2[0]);
References provide the ability to read and write data without consuming ownership of it. References are created with borrows (& and &mut) and used with dereferences (*), often implicitly.
However, references can be easily misused. Rust’s borrow checker enforces a system of permissions that ensures references are used safely:
Rust中的变量默认是不可变的, 但是可以添加mut关键字使之可变
Rust有单次赋值(Single assignment)原则, 尽管Rust变量默认是不可变的, 但是它允许你在声明变量后延迟赋值一次, 只要编译器能确定该变量只会被赋值一次即可.
1 | // 下面的语句是可以通过编译的 |
常量声明方式:
一般crate使用use导入, 但是Rust会自动导入prelude,里面包含了基本的功能, 详见standard library documentation.
Rust中的变量默认是不可变的, 可以添加mut关键字使之改变
read_line读取用户输入,并将其添加到字符串尾端(append), 而不是覆盖, 不同于cin
read_line会返回一个Result值
Result是一个枚举类型,其中有Ok和Err, 所以可以使用read_line(&mut guess).expect("Failed to read") expect函数来处理错误读入, 任何类型的值都有其方法
正常情况下,expect()方法会返回用户输入的字节数
println!() 可以使用{}来输出变量, 但是如果变量名称不存在就会报错
crate是一组Rust源代码文件,用户构建的项目是一个二进制crate, 它是一个可执行文件. rand crate是一个库crate, 它包含的代码用于其他程序, 并不能单独运行
此分类(The Rust Programming Language)采用brown university的实验版本, 对细节进行解释同时进行学习记录

rustup 来管理机器上的Rust版本
rustc main.rs // rustc的用法, rustfmt也可以这么使用
rustfmt main.rs // 使用rust的语法格式化文档
./main
cargo init: 自动获取cargo.toml文件\
cargo.lock文件追踪准确的版本依赖
cargo run 只有代码发生改变的时候才会重新编译运行, 否则直接运行
cargo build --release会在target/release下生成目标文件, 会让Rust代码运行更快,没有debug. 如果要衡量代码效率就是用此命令编译
本系统: Debian 12
如何下载源码并编译运行
官方源码地址:https://github.com/unpbook/unpv13e
git clone https://github.com/unpbook/unpv13e.git
克隆到本地之后,根据README文件一步一步make配置
接下来可以直接 make 想要运行的源码 然后执行即可
最近在开发项目的时候碰到了领域模型,但是并没有对其中的各层关系以及各层传递数据方式理解清晰。在此记录下分层领域模型数据的传递过程
所谓领域模型,其实就是C++类,再详细一点就是有作用范围的实体类。
其核心目标是业务抽象:将现实业务中的实体、流程和规则转化为可被代码理解和操作的结构。
其中的领域对象描述是这样的:
DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。 BO(Business Object):业务对象,可以由 Service 层输出的封装业务逻辑的对象。Query:数据查询对象,各层接收上层的查询请求。VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。