在 Rust 中实现 TCP : 3. TCP连接四元组

连接四元组

我们的项目已经取得了很大的进展——接下来能够开始解决 TCP 协议的实现问题。下面将讨论 TCP 的一些行为及其各种状态。

在这里插入图片描述

在多任务操作系统中,各种应用程序(例如 Web 服务器、电子邮件客户端等)需要同时进行网络访问。为了区分这些不同的网络活动,每个应用程序将自己绑定到一个唯一的端口号。端口号与主机的 IP地址的组合形成(在非常基本的级别)所谓的“套接字” - 内核管理的抽象对象。为了建立连接,需要使用一对这样的套接字——一个用于发送端机器,一个用于接收端机器。而套接字对是唯一的,可以有效地标识一个连接。

与 TCP 连接端点 相关的非常重要的数据结构是 传输控制块 (TCB)。 TCB 充当与已建立或挂起的连接 相关的所有参数和变量的 存储库。 TCB 包含从套接字地址和端口号到序列号和窗口大小的所有内容,是 TCP 操作逻辑的基础。

每个 TCB 都是 某个特定连接 的 TCP 状态的封装。它存储“传输控制协议”中“可靠性”所必需的详细信息,例如未确认的数据、流量控制参数和下一个预期序列号。本质上,TCB充当TCP操作的控制中心,实时维护和更新连接状态。 TCB 充当 TCP 连接的内核账簿,存储从端口号和 IP 地址到流量控制参数和待处理数据的所有内容。本质上,TCB 是 TCP 协议栈需要维护的所有指标和变量的存储库,以便可靠有效地处理连接。

现在,考虑一台涉及多个 TCP 连接的机器。它如何区分它们并辨别哪个传入数据包属于哪个连接?这就是“TCP连接四元组”概念 ,它至关重要。连接四元组由具有四个元素组成:源 IP 地址、源端口、目标 IP 地址和目标端口。该元组充当每个 活动TCP连接 的唯一标识符。连接四元组 作为 TCB 哈希表 的索引很实用。当数据包到达时,TCP 栈使用连接四元组来查找相应的 TCB,进而查找相关的状态机。这确保了根据封装在该 TCB 中的状态机所定义的正确的规则和变量集来处理数据包。 TCP 创建并维护传输控制块 (TCB) 的哈希表来存储每个 TCP 连接的数据。每个活动连接的控制块都会添加到hash表中。连接关闭后不久,控制块就会被删除。

连接在其生命周期内会经历一系列状态。这些状态包括:LISTEN、SYN-SENT、SYN-RECEIVED、ESTABLISHED、FIN-WAIT-1、FIN-WAIT-2、CLOSE-WAIT、CLOSING、LAST-ACK、TIME-WAIT 和虚构状态 CLOSED。这些状态保存在传输控制块 (TCB) 中,充当指导每个连接点的连接行为的关键标记。从最初的握手到数据传输和最终的断开,连接的状态都会被仔细跟踪,以保证可靠和有序的数据交换。

初始状态 - CLOSEDLISTEN - 作为连接的起点。当处于 CLOSED 状态时,不存在传输控制块(TCB),本质上是连接不存在。它类似于未初始化的变量;在此状态下收到的任何数据包都将被忽略。将此与 LISTEN 状态进行对比,在该状态下系统正在主动等待传入的连接请求。一旦收到 SYN 数据包,状态就会转换为 SYN-RECEIVED ,启动 TCP 握手。连接在握手后达到 稳定的 ESTABLISHED 状态,此时发生大部分数据交换。

连接结束时,它会级联一系列 FIN-WAITCLOSE-WAIT 状态,最终达到 TIME-WAITLAST-ACK ,具体取决于拆除动作。这些终端状态确保网络中任何延迟的数据包都得到考虑,从而提供优雅的连接拆除。系统最终恢复到 CLOSED 状态,释放TCB和相关资源以用于将来的连接。

                    +---------+ ---------\      active OPEN
                              |  CLOSED |            \    -----------
                              +---------+<---------\   \   create TCB
                                |     ^              \   \  snd SYN
                   passive OPEN |     |   CLOSE        \   \
                   ------------ |     | ----------       \   \
                    create TCB  |     | delete TCB         \   \
                                V     |                      \   \
                              +---------+            CLOSE    |    \
                              |  LISTEN |          ---------- |     |
                              +---------+          delete TCB |     |
                   rcv SYN      |     |     SEND              |     |
                  -----------   |     |    -------            |     V
 +---------+      snd SYN,ACK  /       \   snd SYN          +---------+
 |         |<-----------------           ------------------>|         |
 |   SYN   |                    rcv SYN                     |   SYN   |
 |   RCVD  |<-----------------------------------------------|   SENT  |
 |         |                    snd ACK                     |         |
 |         |------------------           -------------------|         |
 +---------+   rcv ACK of SYN  \       /  rcv SYN,ACK       +---------+
   |           --------------   |     |   -----------
   |                  x         |     |     snd ACK
   |                            V     V
   |  CLOSE                   +---------+
   | -------                  |  ESTAB  |
   | snd FIN                  +---------+
   |                   CLOSE    |     |    rcv FIN
   V                  -------   |     |    -------
 +---------+          snd FIN  /       \   snd ACK          +---------+
 |  FIN    |<-----------------           ------------------>|  CLOSE  |
 | WAIT-1  |------------------                              |   WAIT  |
 +---------+          rcv FIN  \                            +---------+
   | rcv ACK of FIN   -------   |                            CLOSE  |
   | --------------   snd ACK   |                           ------- |
   V        x                   V                           snd FIN V
 +---------+                  +---------+                   +---------+
 |FINWAIT-2|                  | CLOSING |                   | LAST-ACK|
 +---------+                  +---------+                   +---------+
   |                rcv ACK of FIN |                 rcv ACK of FIN |
   |  rcv FIN       -------------- |    Timeout=2MSL -------------- |
   |  -------              x       V    ------------        x       V
    \ snd ACK                 +---------+delete TCB         +---------+
     ------------------------>|TIME WAIT|------------------>| CLOSED  |
                              +---------+                   +---------+

TCP 连接状态图 图 6。如 RFC 793 中所示。

说得够多了,让我们写一些代码吧!首先,将使用枚举对 状态转换图 进行编码。为了使事情更加模块化,我们可以创建一个名为 TCP 的新模块,并在 TCP.rs 中放置一些代码。与 TCP 状态机相关的 Enum 和其他逻辑将保留在此处。

// tcp.rs
// Defining possible TCP states. 
// Each state represents a specific stage in the TCP connection.
pub enum State { 
/// The connection is closed and no active connection exists. 
Closed, 
/// The endpoint is waiting for a connection attempt from a remote endpoint. 
Listen, 
/// The endpoint has received a SYN (synchronize) segment and has sent a SYN-ACK /// (synchronize-acknowledgment) segment in response. It is awaiting an ACK (acknowledgment) /// segment from the remote endpoint. 
SynRcvd, 
/// The connection is established, and both endpoints can send and receive data. Estab, 
}


// Implementing the Default trait for State. // Sets the default TCP state to 'Listen'. 
impl Default for State { 
	fn default() -> Self { 
		State::Listen 
	} 
}

// Implementing methods for State. 
impl State {
// Method to handle incoming TCP packets. 
// 'iph' contains the parsed IPv4 header, 'tcph' contains the parsed TCP header, and 'data' contains the TCP payload. 
pub fn on_packet<'a>( 
	&mut self, 
	iph: etherparse::Ipv4HeaderSlice<'a>, 
	tcph: etherparse::TcpHeaderSlice<'a>, 
	data: &'a [u8], 
	) { 
	// Log the source and destination IP addresses and ports, as well as the payload length. 
		eprintln!( 
			"{}:{} -> {}:{} {}b of TCP", 
			iph.source_addr(), 
			tcph.source_port(), 
			iph.destination_addr(), 
			tcph.destination_port(), 
			data.len() 
		); 
	} 
}
//main.rs
// Importing necessary modules and packages.
use std::io;
use std::collections::HashMap;
use std::net::Ipv4Addr;

// Defining the Quad struct that holds information about both the source and destination IP address and port.
// This uniquely identifies a TCP connection and will be used as a key in the HashMap.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
struct Quad {
    src: (Ipv4Addr, u16),
    dst: (Ipv4Addr, u16),
}

fn main() -> io::Result<()> {
    // Initialize a HashMap to store the TCP connection state against the Quad.
    // Quad is the key and the TCP state (from tcp.rs) is the value.
    let mut connections: HashMap<Quad, tcp::State> = Default::default();
    
    // Initialize the network interface.
    let nic = tun_tap::Iface::new("tun0", tun_tap::Mode::Tun)?;
    
    // Buffer to store the incoming packets.
    let mut buf = [0u8; 1504];

    loop {
    
			-----SKIP-----

        // Attempt to parse the IPv4 header.
        match etherparse::Ipv4HeaderSlice::from_slice(&buf[4..nbytes]) {
            Ok(iph) => {
					-----SKIP-----

                // Attempt to parse the TCP header.
                match etherparse::TcpHeaderSlice::from_slice(&buf[4 + iph.slice().len()..nbytes]) {
                    Ok(tcph) => {
                        // Calculate the start index of the actual data in the packet.
                        let datai = 4 + iph.slice().len() + tcph.slice().len();
                        
                        // Look for or create a new entry in the HashMap for this connection.
  
			// Check if the connection already exists in the HashMap, otherwise create a new entry 
			match connections.entry(Quad { 
			src: (src, tcph.source_port()), 
			dst: (dst, tcph.destination_port()), 
			}) { 
			Entry::Occupied(mut c) => { 
			c.get_mut().on_packet(&mut nic, iph, tcph, &buf[datai..nbytes])?; 
			} 
			Entry::Vacant(e) => { 
			if let Some(c) = tcp::Connection::on_accept(&mut nic, iph, tcph, &buf[datai..nbytes])? { 
			e.insert(c); 
			} } } }
                    }
                    Err(e) => {
                        // Handle TCP header parsing errors.
                        eprintln!("An error occurred while parsing the TCP packet: {:?}", e);
                    }
                }
            }
            Err(e) => {
                // Handle IPv4 header parsing errors.
                eprintln!("An error occurred while parsing the IP packet: {:?}", e);
            }
        }
    }
}

在上面代码中,通过定义 TCP 连接所需的关键数据结构和函数来建立 TCP 协议栈的基础。该架构的核心是 Quad 结构,它充当名为 connectionsHashMap 中每个条目的 唯一标识符。此 HashMap 充当 活动或正在建立的 TCP 连接的内存中注册表。 HashMap 中的每个条目都包含映射到其当前 TCP 连接的 Quad 实例。该状态由 tcp.rs 中定义的枚举类型表示,包含四种 TCP 连接状态之一: ClosedListenSynRcvd ,或 Estab

我们引入两个主要的处理数据包方法: on_accepton_packeton_accept 方法负责处理发起 新连接的传入数据包。相反, on_packet 方法管理 现有连接的数据包。这两种方法都会记录基本信息,例如源和目标 IP 地址和端口以及有效负载长度。最后,在 main.rs 中,我们根据传入数据包利用模式匹配来区分新连接和现有连接。

我们正在稳步取得进展。到目前为止,已经确保接收到正确的 IPv4 数据包,并且已经实现了一种机制,将传入数据包与其各自的状态相关联,并由唯一的连接四元组作为键。我们的下一个目标是专注于实现 TCP 握手,这是保证客户端和服务器之间建立可靠连接的关键步骤。客户端通过发送 SYN(同步)数据包来启动此过程,而服务器则监听这些传入请求。此握手是一个 三阶段过程,涉及 SYN 数据包、随后的 SYN-ACK(同步确认)数据包和最后的 ACK(确认)数据包。在此阶段,我们将增强 accept 方法来管理这些不同类型的握手数据包。

// main.rs
// Required imports
use std::io;
mod tcp;  // Importing the tcp module (defined below)
use std::collections::HashMap;
use std::net::Ipv4Addr;

// Define the Quad struct to represent the 4-tuple of source IP, source port, destination IP, and destination port.
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
struct Quad {
    src: (Ipv4Addr, u16),  // Source IP and port
    dst: (Ipv4Addr, u16),  // Destination IP and port
}

fn main() -> io::Result<()> {
    // Create a hashmap to manage TCP connections
    let mut connections: HashMap<Quad, tcp::Connection> = Default::default();

    // Create a new virtual interface in TUN mode.
    // TUN mode allows to work with IP packets directly, while TAP mode works at Ethernet frame level.
    let mut nic = tun_tap::Iface::without_packet_info("tun0", tun_tap::Mode::Tun)?;
    
    // Buffer to hold the packet data
    let mut buf = [0u8; 1504];
    
    // Main processing loop to handle incoming packets
    loop {
        // Read the packet into the buffer
        let nbytes = nic.recv(&mut buf[..])?;

        // Parse the IP header from the packet. If successful, it gives a slice of the IP header.
        match etherparse::Ipv4HeaderSlice::from_slice(&buf[..nbytes]) {
            Ok(iph) => {
                // Parsing of the IP header was successful. Further processing happens here.
                // -----SKIP----- (omitting some IP processing steps as indicated)

                // Attempt to parse the TCP header from the packet.
                match etherparse::TcpHeaderSlice::from_slice(&buf[iph.slice().len()..nbytes]) {
                    Ok(tcph) => {
                        // Parsing of the TCP header was successful. Continue processing.
                        // Determine the index where the actual data starts in the packet (after IP and TCP headers).
                        let datai = iph.slice().len() + tcph.slice().len();
                        
                        // Lookup the connection using the Quad (4-tuple) as the key.
                        match connections.entry(Quad {
                            src: (src, tcph.source_port()),
                            dst: (dst, tcph.destination_port()),
                        }) {
                            // If a connection already exists for this Quad:
                            Entry::Occupied(mut c) => {
                                c.get_mut()
                                    .on_packet(&mut nic, iph, tcph, &buf[datai..nbytes])?;
                            }
                            // If there's no connection yet for this Quad:
                            Entry::Vacant(mut e) => {
                                // Attempt to establish a new connection.
                                if let Some(c) = tcp::Connection::accept(
                                    &mut nic,
                                    iph,
                                    tcph,
                                    &buf[datai..nbytes],
                                )? {
                                    e.insert(c);
                                }
                            }
                        }
                    }
                    Err(e) => {
                        // Handle TCP parsing errors.
                        eprintln!("An error occurred while parsing TCP packet {:?}", e);
                    }
                }
            }
            Err(e) => {
                // Handle IP parsing errors.
                eprintln!("An error occurred while parsing IP packet {:?}", e);
            }
        }
    }
}

//tcp.rs
use std::io;
use std::io::prelude::*;

/// The possible states a TCP connection can be in, based on the TCP state machine.
/// It's a subset of the states available in the full TCP state diagram.
pub enum State {
    Closed,
    Listen,
    SynRcvd,
    Estab,
}

pub struct Connection {
    /// The current state of the TCP connection.
    state: State,
    /// The sequence space for sent data. It keeps track of various sequence numbers for data we've sent.
    send: SendSequenceSpace,
    /// The sequence space for received data. It keeps track of sequence numbers for data we're receiving.
    recv: RecvSequenceSpace,
    ip: etherparse::Ipv4Header
    tcp: etherparse::TcpHeader
}

/// Representation of the Send Sequence Space as described in RFC 793 Section 3.2 Figure 4.
/// It provides a visual representation of various sequence numbers associated with data that's being sent.
///
/// ```
///            1         2          3          4
///       ----------|----------|----------|----------
///              SND.UNA    SND.NXT    SND.UNA
///                                   +SND.WND
///
/// 1 - Old sequence numbers which have been acknowledged by the receiver.
/// 2 - Sequence numbers of unacknowledged data that has been sent.
/// 3 - Sequence numbers allowed for transmitting new data.
/// 4 - Future sequence numbers that are not allowed for transmission yet.
/// ```
struct SendSequenceSpace {
    /// SND.UNA: Oldest sequence number not yet acknowledged by the receiver.
    una: u32,
    /// SND.NXT: Next sequence number to be used for new data for transmission.
    nxt: u32,
    /// SND.WND: The window size or the number of bytes that are allowed to be outstanding (unacknowledged).
    wnd: u16,
    /// Indicates if the URG control bit is set. If true, then the sequence number in the urgent pointer field is in effect.
    up: bool,
    /// Sequence number of the segment used for the last window update. 
    wl1: usize,
    /// Acknowledgment number used for the last window update.
    wl2: usize,
    /// Initial send sequence number. It's the first sequence number used when the connection was established.
    iss: u32,
}

/// Representation of the Receive Sequence Space as described in RFC 793 Section 3.2 Figure 5.
/// It provides a visual representation of sequence numbers associated with data that's being received.
///
/// ```
///                1          2          3
///            ----------|----------|----------
///                   RCV.NXT    RCV.NXT
///                             +RCV.WND
///
/// 1 - Old sequence numbers which have been acknowledged.
/// 2 - Sequence numbers allowed for receiving new data.
/// 3 - Future sequence numbers which are not allowed for reception yet.
/// ```
struct RecvSequenceSpace {
    /// RCV.NXT: Next expected sequence number that the receiver is expecting.
    nxt: u32,
    /// RCV.WND: The number of bytes that the receiver is willing to accept.
    wnd: u16,
    /// Indicates if the URG control bit is set on received data.
    up: bool,
    /// Initial receive sequence number. The sequence number of the first byte received.
    irs: u32,
}

/// Default state for a TCP connection is set to `Listen`.
impl Default for State {
    fn default() -> Self {
        State::Listen
    }
}

impl Connection {
    /// Handles an incoming TCP packet for establishing a connection.
    /// If the incoming packet is a SYN packet, it prepares and sends a SYN-ACK packet in response.
    /// Otherwise, it ignores the packet.
    ///
    /// Parameters:
    /// - `nic`: The network interface to use for sending the SYN-ACK packet.
    /// - `iph`: The IPv4 header of the incoming packet.
    /// - `tcph`: The TCP header of the incoming packet.
    /// - `data`: The payload of the incoming packet.
    ///
    /// Returns:
    /// A new `Connection` in the `SynRcvd` state if the incoming packet was a SYN packet.
    /// Otherwise, returns `None`.
    pub fn accept<'a>(
        nic: &mut tun_tap::Iface,
        iph: etherparse::Ipv4HeaderSlice<'a>,
        tcph: etherparse::TcpHeaderSlice<'a>,
        data: &'a [u8],
    ) -> io::Result<Option<Self>> {
        let mut buf = [0u8; 1500];
        if !tcph.syn() {
            // Ignore packets that aren't SYN packets.
            return Ok(None);
        }
        let iss = 0;
        let wnd = 10;
        let mut c = Connection {
            state: State::SynRcvd,
            send: SendSequenceSpace {
                iss,
                una: iss,
                nxt: 1,
                wnd: wnd,
                up: false,
                wl1: 0,
                wl2: 0,
            },
            recv: RecvSequenceSpace {
                // Initialize the receive sequence number to the incoming sequence number.
                irs: tcph.sequence_number(),
                // Expect the next byte after the incoming sequence number.
                nxt: tcph.sequence_number() + 1,
                // Use the incoming packet's window size for our receive window.
                wnd: tcph.window_size(),
                up: false,
            },
        // TODO: Consider keeping track of sender info for future use.
        // Prepare a SYN-ACK packet in response to the SYN packet.
        tcp: etherparse::TcpHeader::new(
            tcph.destination_port(),
            tcph.source_port(),
            iss,
            wnd,
		),
            ip: etherparse::Ipv4Header::new(
            syn_ack.header_len(),
            64,
            etherparse::IpNumber::Tcp as u8,
            [
                iph.destination()[0],
                iph.destination()[1],
                iph.destination()[2],
                iph.destination()[3],
            ],
            [
                iph.source()[0],
                iph.source()[1],
                iph.source()[2],
                iph.source()[3],
            ],
        )
        };

        c.tcp.acknowledgment_number = c.recv.nxt;
        c.tcp.syn = true;
        c.tcp.ack = true;
        
        c.ip.set_payload_len(c.tcp.header_len() as usize + 0);

        // Calculate and set the checksum for the SYN-ACK packet.
        c.tcp.checksum = c.tcp
            .calc_checksum_ipv4(&c.ip, &[])
            .expect("Failed to compute checksum");
        
        // Write out the TCP and IP headers to a buffer to be sent.
        let unwritten = {
            let mut unwritten = &mut buf[..];
            ip.write(&mut unwritten);
            syn_ack.write(&mut unwritten);
            unwritten.len()
        };

        // Send the SYN-ACK packet.
        nic.send(&buf[..unwritten])?;
        Ok(Some(c))
    }

    /// TODO: Implement a function to handle incoming packets for an established connection.


    // Function to handle incoming packets once a connection is established.
    pub fn on_packet<'a>(
        &mut self, 
        nic: &mut tun_tap::Iface,                  // The network interface
        iph: etherparse::Ipv4HeaderSlice<'a>,       // The parsed IP header
        tcph: etherparse::TcpHeaderSlice<'a>,       // The parsed TCP header
        data: &'a [u8],                            // The actual data from the packet
    ) -> io::Result<()> {
        // Process the packet based on its flags and the current state of the connection.
        // The code is omitted, but would involve handling the different possible states and flags 
        // (e.g., ACK, FIN, RST) as per the TCP state machine.
        // ----SKIP----
        Ok(())
    }

    // Function to send a SYN-ACK packet.

}

在从包含实现框架的代码过渡到建立 TCP 握手的充实版本的过程中,经历了重大的架构演变。该架构的核心是 State 枚举和 tcp.rs 中的 Connection 结构体的扩展。之前, State 枚举是四种可能的 TCP 状态的简单表示。在新的实现中,重点转移到建立更强大的连接状态表示。 Connection 结构已扩展为现在将 SendSequenceSpaceRecvSequenceSpace 的实例作为其属性。这些结构对于 TCP 序列号和确认号的登记 很有帮助,这对于数据的可靠传输至关重要。让我们深入了解一下具体情况。

The SendSequenceSpace struct encapsulates several variables crucial for the sending side of our TCP connection. Key among them are:
SendSequenceSpace 结构体封装了几个对于 TCP 连接发送端至关重要的变量。其中关键是:

  • una (send unacknowledged): The earliest sequence number in the send buffer that has yet to receive an acknowledgment.
    una (发送未确认):发送缓冲区中尚未收到确认的最早序列号。

  • nxt (send next): The next sequence number to be used for new data.
    nxt (发送下一个):用于新数据的下一个序列号。

  • wnd (send window): Specifies the range of acceptable sequence numbers for the receiver. These fields are critical in managing flow control, ensuring reliable data transfer, and implementing features like sliding windows.
    wnd (发送窗口):指定接收方可接受的序列号范围。这些字段对于管理流量控制、确保可靠的数据传输以及实现滑动窗口等功能至关重要。
    Complementing this, the RecvSequenceSpace struct handles the receiving end. It possesses fields like:
    作为补充, RecvSequenceSpace 结构处理接收端。它拥有以下字段:

  • nxt (receive next): The sequence number expected for the next incoming packet.
    nxt (接收下一个):下一个传入数据包的预期序列号。

  • wnd (receive window): The window size advertising the range of acceptable sequence numbers for the sender.
    wnd (接收窗口):通告发送方可接受的序列号范围的窗口大小。

值得注意的是, SendSequenceSpace 和 RecvSequenceSpace 都包含初始序列号(分别为 iss 和 irs )。这些在连接建立阶段非常关键,特别是在 TCP 三向握手过程中。

我们开始实现 accept 方法,该方法现已增强以处理 SYN 数据包,从而通过将 SYN-ACK 数据包分派回客户端来启动三向握手。为了实现这一目标,使用 etherparse 库函数来构建和发送 TCP/IP 头,使我们的实现与协议规范紧密结合。

顺便说一句 - 值得注意的是,在当前的实现中, connections HashMap 容易受到 SYN 洪水攻击。在此类攻击中,攻击者可以发送大量 TCP SYN(同步)数据包,每个数据包具有不同的源地址和端口,但都针对相同的目的地。由于 connections HashMap 自动为每个唯一的 Connection 创建一个新条目,因此攻击者可以通过使用大量虚假条目填充 HashMap 来轻松耗尽系统内存。这可能会导致资源耗尽,并最终导致拒绝服务 (DoS)。

在生产级 TCP 实施中,通常会采取其他措施来减轻此类风险。这可以包括使用 syn cookie - 一种无状态方法,其中服务器在握手完成之前不会为 SYN 请求分配资源。然而,对于这个项目,我们不会采取任何措施来防止 syn 洪水攻击。

此时,您可能会好奇并看到我们编写的这么长的代码的实际效果。我们应该继续测试应用程序。首先,通过运行 run.sh 脚本来启动程序,该脚本将构建并执行二进制文件并为其提供必要的提升网络访问权限,接下来,将使用 Netcat 尝试与应用程序建立 TCP 连接,最后为了可视化,将使用 tshark 通过运行 tshark -I tun0 来监视和捕获 tun0 接口上的数据包。

1   0.000000   fe80::a2b3:c4d5:e6f7 -> ff02::2  ICMPv6 110 Router Solicitation from a2:b3:c4:d5:e6:f7
2   0.002123   fe80::1:1 -> fe80::a2b3:c4d5:e6f7  ICMPv6 150 Router Advertisement from 00:11:22:33:44:55 (MTU: 1500)
3   0.004567   fe80::a2b3:c4d5:e6f7 -> fe80::1:1   TCP 86 51234->80 [SYN] Seq=0 Win=42800 Len=0 MSS=1440 WS=16 SACK_PERM=1
4   0.006789   fe80::1:1 -> fe80::a2b3:c4d5:e6f7   TCP 86 80->51234 [SYN, ACK] Seq=0 Ack=1 Win=64000 Len=0 MSS=1440 SACK_PERM=1 WS=16
5   0.006999   fe80::a2b3:c4d5:e6f7 -> fe80::1:1   TCP 66 51234->80 [ACK] Seq=1 Ack=1 Win=43000 Len=0

如果一切顺利,应该会看到与于上面看到的类似的内容。这表明主机首先请求路由器信息,并从路由器获得答复。之后,将看到建立 TCP 连接的步骤。

下一步计划是什么?如果您留意的话,您会注意到我们已经解决了 TCP 三向握手的最初两个步骤。当来自客户端的 SYN 到达我们这里时,服务器会立即用 SYN-ACK 进行回复。发送 SYN-ACK 后,服务器优雅地进入 SynRcvd 状态,准备好等待客户端的 ACK 来 完成握手协议。当捕获并处理这个 ACK​​ 时,理想情况下会将连接转换到 Established 状态,标志着一个成熟的 TCP 连接的诞生。然而,这里缺少一块拼图:我们的代码仍在等待中,尚未处理客户端的 ACK。这是我们下一个目标。

参考

  • 对应代码

原文地址

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

openGauss学习笔记-236 openGauss性能调优-SQL调优-Query执行流程

文章目录 openGauss学习笔记-236 openGauss性能调优-SQL调优-Query执行流程236.1 Query执行流程236.1.1 调优手段之统计信息236.1.2 调优手段之GUC参数236.1.3 调优手段之底层存储236.1.4 调优手段之SQL重写 openGauss学习笔记-236 openGauss性能调优-SQL调优-Query执行流程 S…

如何选择程序员职业赛道

目录 前言1 个人技能分析1.1 技术栈评估1.2 经验积累1.3 数据科学能力 2 兴趣与价值观2.1 用户交互与界面设计2.2 复杂问题解决与系统优化 3 长期目标规划4 市场需求分析4.1 人工智能和云计算4.2 前沿技术趋势 5 就业前景5.1 前端在创意性公司中的应用5.2 后端在大型企业中的广…

全面分析vcruntime140_1.dll无法继续执行代码的处理方法,3分钟修复vcruntime140_1.dll

如果系统弹出一个错误警告&#xff0c;指出“vcruntime140_1.dll无法继续执行代码”&#xff0c;这通常意味着您的Windows系统中缺失了一个关键的文件&#xff0c;或者该文件已损坏。​vcruntime140_1.dll​是随Visual C Redistributable for Visual Studio 2015, 2017和2019提…

【更新2022】各省数字经济水平测算 原始数据+结果 2011-2022

数据说明&#xff1a;参照赵涛等&#xff08;2020&#xff09;的文章&#xff0c;利用熵值法和主成分对省市数字经济水平进行测算&#xff0c;原始数据来自第五期北大数字普惠金融指数&#xff0c;含原始数据&#xff0c;以及熵值法、主成分两种测算结果。一、数据介绍 数据名…

无人机/飞控--ArduPilot、PX4学习历程记录(1)

本篇博客用来记录个人学习记录&#xff0c;存放各种文章链接、视频链接、学习历程、实验过程和结果等等.... 最近在整无人机项目&#xff0c;接触一下从来没有接触过的飞控...(听着就头晕)&#xff0c;本人纯小白。 目录 PX4、Pixhawk、APM、ArduPilot、Dronecode Dronekit…

Linux 设置快捷命令

以ll命令为例&#xff1a; 在 Linux 系统上&#xff0c;ll 命令通常不是一个独立的程序&#xff0c;而是 ls 命令的一个别名。 这个别名通常在用户的 shell 配置文件中定义&#xff0c;比如 .bashrc 或 .bash_aliases 文件中。 要在 Debian 上启用 ll 命令&#xff0c;你可以按…

鸿蒙 Stage模型-AbilityStage、Context、Want

前提&#xff1a;基于官网3.1/4.0文档。参考官网文档 基于Android开发体系来进行比较和思考。&#xff08;或有偏颇&#xff0c;自行斟酌&#xff09; 一、 AbilityStage 1.概念 AbilityStage是一个Module级别的组件容器&#xff0c;应用的HAP在首次加载时会创建一个AbilitySt…

怎么写苹果群控核心功能的源代码!

随着移动设备的普及和技术的不断发展&#xff0c;苹果设备群控技术成为了许多开发者关注的焦点&#xff0c;苹果群控技术允许开发者通过编写源代码&#xff0c;实现对多台苹果设备的集中管理和控制。 一、了解苹果群控技术的基本原理 在编写苹果群控核心功能的源代码之前&…

书生浦语全链路开源体系

推荐阅读论文 A Survey Of Large Language Models 书生浦语开源的模型 从模型到应用 书生浦语开源体系 书生万卷开源数据集 除此之外还有OpenDataLab国内数据集下载网站。 预训练框架InterLM-Train 微调框架XTuner 评测工具体系 国内外常见的大语言模型评测基准&#xff1a…

YOLOv8独家原创改进:特征融合涨点篇 | 广义高效层聚合网络(GELAN) | YOLOv9

💡💡💡本文独家改进:即结合用梯度路径规划(CSPNet)和(ELAN)设计了一种广义的高效层聚合网络(GELAN),高效结合YOLOv8,实现涨点。 将GELAN添加在backbone和head处,提供多个yaml改进方法 💡💡💡在多个私有数据集和公开数据集VisDrone2019、PASCAL VOC实现…

基于华为atlas的unet分割模型探索

Unet模型使用官方基于kaggle Carvana Image Masking Challenge数据集训练的模型。 模型输入为572*572*3&#xff0c;输出为572*572*2。分割目标分别为&#xff0c;0&#xff1a;背景&#xff0c;1&#xff1a;汽车。 Pytorch的pth模型转化onnx模型&#xff1a; import torchf…

bun 单元测试

bun test Bun 附带了一个快速、内置、兼容 Jest 的测试运行程序。测试使用 Bun 运行时执行&#xff0c;并支持以下功能。 TypeScript 和 JSX生命周期 hooks快照测试UI 和 DOM 测试使用 --watch 的监视模式使用 --preload 预加载脚本 Bun 旨在与 Jest 兼容&#xff0c;但并非所…

北京Excel表格线下培训班

Excel培训目标 熟练掌握职场中Excel所需的公式函数计算&#xff0c;数据处理分析&#xff0c;各种商务图表制作、动态仪表盘的制作、熟练使用Excel进行数据分析&#xff0c;处理&#xff0c;从复杂的数据表中把数据进行提取汇总 Excel培训形式 线下面授5人以内小班&#xff…

分享Web.dev.cn中国开发者可以正常访问

谷歌开发者很高兴地宣布&#xff0c;web.dev 和 Chrome for Developers 现在都可以通过 .cn 域名访问&#xff0c;这将帮助中国的开发者更加容易获取我们的内容。 在 .cn 域名上&#xff0c;我们已向您提供所有镜像后的内容&#xff0c;并提供支持的语言版本。 Web.dev 中国开…

uipath调用js代码

1&#xff0c;调用js代码&#xff0c;不带参数&#xff0c;没有返回值 为了去掉按钮的disabled属性 function(){ document.getElementsByClassName(submitBtn)[0].removeAttribute(disabled); } 2&#xff0c;调用js代码&#xff0c;带参数&#xff0c;没有返回值 输入参数&a…

el-dialog封装组件

父页面 <template><div><el-button type"primary" click"visible true">展示弹窗</el-button><!-- 弹窗组件 --><PlayVideo v-if"visible" :visible.syncvisible /></div> </template><sc…

Python-Numpy-计算向量间的欧式距离

两个向量间的欧式距离公式&#xff1a; a np.array([[2, 2], [4, 5], [6, 7]]) b np.array([[1, 1]]) # 使用L2范数计算 dev1 np.linalg.norm(a - b, ord2, axis1) # 使用公式计算 dev2 np.sqrt(np.sum((a - b) ** 2, axis1)) print(dev1.reshape((-1, 1)), dev2.reshape((…

掌握WhatsApp手机号质量评分:增加信息可达性

WhatsApp手机号质量评分是用于衡量用户手机号与平台互动的健康度&#xff0c;确保用户通讯时的合规性和安全性。在实掌握WhatsApp手机号质量评分实际应用中&#xff0c;这个评分会影响用户的消息发送的可达性。高质量的评分意味着用户的账户被视为可信赖的&#xff0c;其发送的…

2024最新ChatGPT网站源码, AI绘画系统

一、前言说明 R5Ai创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧。已支持GP…

平替电容笔推荐:2024五大高口碑电容笔机型别错过!

现在电容笔已成为许多人工作、学习和创作的重要配件之一&#xff0c;它可以很好的提高我们的书写、绘画效率&#xff0c;无纸化学习也能减轻我们书本重量&#xff0c;让学习更加高效&#xff0c;然而&#xff0c;市场上电容笔种类繁&#xff0c;也少不了一些质量不佳的产品&…