网络编程的基石:POSIX API 与协议栈

目录

一、基本概念

1、POSIX(Portable Operating System Interface)

客户端和服务器相关API

客户端

服务端

 epoll相关函数

二、详细解释

1、连接过程

1)socket()

文件描述符(File Descriptor, fd)

传输控制块(Transmission Control Block, TCB)

2)bind()

bind() 函数的工作原理

查找套接字结构:

设置地址和端口:

更新 TCB:

 bind() 函数调用过程

总结

3)listen()

listen() 函数的工作原理

listen() 函数的调用过程

listen() 的作用

listen() 第二参数backlog 的作用

总结

4)accept()

accept 函数的工作原理

具体实现步骤

总结

5)三次握手的过程

客户端

服务器

三次握手的详细过程

三次握手过程的具体步骤

全连接队列和半连接队列

半连接队列(Syn Queue)

全连接队列(Accept Queue)

6)相关问题

1.TCP连接的生命周期

2.第三次握手的数据包在全连接队列中的查找过程

3.SYN泛洪攻击

4.防御SYN泛洪攻击

总结

2、数据传输

1)慢启动(Slow Start)

2)拥塞控制(Congestion Control)

3)滑动窗口(Sliding Window)

4)延迟确认(Delayed ACK)

5)超时重传(Timeout Retransmission)

6)总结

3、断开连接

1)调用 close 函数关闭连接和四次挥手

2)四次挥手的过程

3)状态转换

4)调用 close 函数

5)详细的状态解释

6)同时关闭的四次挥手过程

7)状态转换

4、tcp的p2p实现

1)P2P连接的基本概念

2)代码

节点代码(同时作为客户端和服务器)

解释

如何运行

3)总结


一、基本概念

1、POSIX(Portable Operating System Interface)

        POSIX(Portable Operating System Interface)是一个定义了一系列API(应用程序编程接口)的标准,旨在提高不同操作系统之间的兼容性和可移植性。POSIX API涵盖了文件和目录操作、进程控制、线程管理、信号处理、内存管理、网络通信等多方面的功能。

客户端和服务器相关API

客户端
  • 使用socket()函数创建一个套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
    
  • 使用connect()函数连接到服务器
    connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    
  • 使用send()和recv()函数进行数据通信
    send(sockfd, message, strlen(message), 0);
    recv(sockfd, buffer, sizeof(buffer), 0);
    
  • 使用close()函数关闭套接字
    close(sockfd);
    
服务端
  • 使用socket()函数创建一个套接字
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    
  • 使用bind()函数绑定套接字到特定的IP地址和端口号
    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);
    bind(server_fd, (struct sockaddr *)&address, sizeof(address));
    
  • 使用listen()函数使套接字进入监听状态
    listen(server_fd, 3);
    
  • 使用accept()函数接受客户端连接
    int new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
    
  • 使用send()和recv()函数进行数据通信
    recv(new_socket, buffer, sizeof(buffer), 0);
    send(new_socket, response, strlen(response), 0);
    
  • 使用close()函数关闭套接字
    close(new_socket);
    close(server_fd);
    
 epoll相关函数
  • epoll_create():创建一个 epoll 实例
    int epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }
    
  • epoll_ctl():控制 epoll 实例,注册、修改或删除感兴趣的事件
    struct epoll_event event;
    event.events = EPOLLIN; // 监控读事件
    event.data.fd = fd; // 需要监控的文件描述符
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) == -1) {
        perror("epoll_ctl");
        exit(EXIT_FAILURE);
    }
    
  • epoll_wait():等待事件的发生
    struct epoll_event events[MAX_EVENTS];
    int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
    if (nfds == -1) {
        perror("epoll_wait");
        exit(EXIT_FAILURE);
    }
    for (int i = 0; i < nfds; ++i) {
        if (events[i].events & EPOLLIN) {
            // 处理可读事件
        }
    }
    

二、详细解释

1、连接过程

1)socket()

在网络编程中,socket 的实现确实涉及多个部分,其中主要包括文件描述符(File Descriptor, fd)和传输控制块(Transmission Control Block, TCB)。以下是对这两个部分及其相互作用的详细解释:

文件描述符(File Descriptor, fd)
  • 文件描述符分配:
    • 当你调用 socket() 函数创建一个新的套接字时,操作系统会分配一个文件描述符。文件描述符是一个整数,用于唯一标识该套接字。
    • 文件描述符在内核中作为索引,用于查找与该套接字相关的数据结构和状态信息。
传输控制块(Transmission Control Block, TCB)
  • 传输控制块的作用:
    • TCB 是一个数据结构,用于存储与 TCP 连接相关的所有状态信息。对于每个 TCP 连接,操作系统内核会维护一个 TCB。
    • TCB 包含的信息包括:
      • 本地和远程 IP 地址
      • 本地和远程端口号
      • 连接状态(如 LISTEN、SYN_SENT、ESTABLISHED 等)
      • 发送和接收缓冲区
      • 序列号和确认号
      • 拥塞控制和流量控制参数

2)bind()

bind() 函数通过文件描述符(fd)找到对应的套接字结构,然后将IP地址和端口号设置进去,从而在传输控制块(TCB)中反映这些信息。下面是详细的解释:

bind() 函数的工作原理
  1. 查找套接字结构:
    1. 当你调用 bind() 函数时,内核会根据传入的文件描述符(fd)查找对应的套接字结构(socket structure)。这个套接字结构包含了与该套接字相关的各种信息,包括指向 TCB 的指针。
      int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
      
  2. 设置地址和端口:
    1. bind() 函数将传入的IP地址和端口号设置到套接字结构中。在TCP协议中,这些信息最终会存储到对应的传输控制块(TCB)中。
    2. 具体来说,bind() 函数将 sockaddr 结构体中的 sin_addr 和 sin_port 字段的值设置到套接字结构中。
      struct sockaddr_in address;
      address.sin_family = AF_INET;
      address.sin_addr.s_addr = INADDR_ANY;  // IP address
      address.sin_port = htons(PORT);        // Port number
      
  3. 更新 TCB:
    1. 当套接字结构中的 IP 地址和端口号被设置后,这些信息也会反映到相应的 TCB 中。TCB 包含连接状态、IP地址、端口号等关键信息,用于管理和维护 TCP 连接。
    2. 内核会确保在调用 bind() 函数时,套接字的地址和端口号信息得到正确更新,从而在后续的连接建立和数据传输过程中使用。
 bind() 函数调用过程
  1. 应用程序调用 bind():
    1. 应用程序调用 bind() 函数,并传入文件描述符、IP地址和端口号。
      int sockfd = socket(AF_INET, SOCK_STREAM, 0);
      struct sockaddr_in address;
      address.sin_family = AF_INET;
      address.sin_addr.s_addr = INADDR_ANY;
      address.sin_port = htons(PORT);
      bind(sockfd, (struct sockaddr *)&address, sizeof(address));
      
  2. 内核处理 bind() 请求:
    1. 内核接收到 bind() 请求后,根据文件描述符查找对应的套接字结构。
    2. 内核将传入的 sockaddr 结构体中的IP地址和端口号设置到套接字结构中。
    3. 内核更新套接字结构中的信息,同时确保这些信息反映到相应的 TCB 中。
  3. 确认地址和端口设置成功:
    1. 内核完成地址和端口的设置后,返回成功状态给应用程序,表示 bind() 操作成功。
总结
  1. 文件描述符(fd):标识套接字。
  2. 套接字结构(socket structure):包含套接字相关的信息,包括指向 TCB 的指针。
  3. 传输控制块(TCB):存储连接状态、IP地址、端口号等关键信息。

bind() 函数通过文件描述符查找套接字结构,然后将IP地址和端口号设置进去,最终在TCB中反映这些信息。这确保了在TCP连接建立和数据传输过程中使用正确的地址和端口。

3)listen()

listen() 函数将传输控制块(TCB)中的状态设置为 LISTEN 状态。以下是 listen() 函数的详细工作原理及其在 TCP 连接管理中的作用:

listen() 函数的工作原理
  1. 函数原型:
    int listen(int sockfd, int backlog);
    
    1. sockfd:由 socket() 创建并绑定了地址的套接字文件描述符。
    2. backlog:全连接队列的最大长度。
  2. 主要操作:
    1. 当你调用 listen() 函数时,内核会根据传入的文件描述符(fd)查找对应的套接字结构。
    2. 内核会将该套接字的状态设置为 LISTEN,并将其标记为一个被动套接字,表示它将接受传入的连接请求。
  3. 状态转换:
    1. 在 TCP 协议中,套接字的状态会从 CLOSED 转换为 LISTEN。这是 TCP 状态机中的一个重要状态,用于表示服务器套接字正在监听传入的连接请求。
listen() 函数的调用过程
  1. 应用程序调用 listen():
    1. 应用程序调用 listen() 函数,并传入套接字文件描述符和待处理连接请求的最大队列长度(backlog)。
  2. 内核处理 listen() 请求:
    1. 内核接收到 listen() 请求后,根据文件描述符查找对应的套接字结构。
    2. 内核将该套接字的状态设置为 LISTEN,并更新相应的 TCB。
    3. TCB 中的状态字段被更新为 LISTEN,表示该套接字正在等待传入的连接请求。
  3. 队列管理:
    1. 内核会为该监听套接字维护一个连接请求队列。backlog 参数指定了该队列的最大长度。
    2. 当新的连接请求到达时,内核会将这些请求放入队列中,等待应用程序调用 accept() 函数处理。
listen() 的作用
  1. 将套接字状态设置为 LISTEN:表示该套接字正在监听传入的连接请求。
  2. 创建连接请求队列:为传入的连接请求维护一个队列,等待应用程序处理。
listen() 第二参数backlog 的作用
  1. 定义全连接队列长度:
    1. backlog 参数指定了全连接队列(Accept Queue)的最大长度,即服务器在 accept() 之前可以存储的完全建立的连接请求的数量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值