突破编程_C++_网络编程(TCPIP 四层模型(传输层))

1 传输层的功能与作用

在 TCP/IP 四层模型中,传输层位于网络层之上和应用层之下,负责在源主机和目标主机之间提供端到端的可靠数据传输服务。传输层的主要功能与作用体现在以下几个方面:

  • 分段与重组:由于网络层的数据包大小有限制(如 IP 数据包的最大长度为 65535 字节),而应用层的数据可能远大于这个限制,因此传输层需要将应用层的数据分割成较小的数据段(或称为数据报、报文段),并添加适当的头部信息以便在网络中传输。在目标主机上,传输层会负责将这些数据段重新组合成原始的应用层数据。

  • 流量控制:传输层通过一系列机制来控制数据的发送速率,以避免发送方发送数据过快而导致接收方缓冲区溢出。这通常通过滑动窗口、拥塞控制等算法来实现,确保数据的平稳传输。

  • 差错控制:传输层使用各种技术来检测和纠正数据在传输过程中可能出现的错误。例如,使用校验和来检测数据包的完整性,以及通过确认和重传机制来纠正丢失或损坏的数据包。TCP协议提供了强大的差错控制功能,而 UDP 协议则相对简单,不提供重传机制。

  • 复用与分用:复用是指多个应用层进程可以同时使用同一个传输层协议进行通信,而分用则是将接收到的传输层数据交付给正确的应用层进程。这通常通过端口号来实现,每个应用层进程都有一个唯一的端口号作为标识。

  • 连接管理:对于面向连接的协议(如 TCP),传输层负责建立、维护和终止连接。在建立连接时,双方会进行一系列握手过程来确认对方的存在和参数设置;在数据传输过程中,会保持连接状态并进行必要的流量控制和差错控制;在数据传输完毕后,会释放连接资源。

2 TCP 协议

2.1 TCP 的特点与工作原理

TCP(Transmission Control Protocol,传输控制协议)提供了面向连接的、可靠的、字节流的服务。

(1)TCP的特点

面向连接:TCP 是一个面向连接的协议,这意味着在数据发送之前,通信双方必须先建立连接。这种连接是全双工的,即数据可以在两个方向上同时传输。
可靠性:TCP 通过一系列机制确保数据的可靠传输。它使用序列号对发送的数据包进行编号,以便接收方能够按正确的顺序重组数据。此外,TCP 还使用确认和重传机制来处理丢失或损坏的数据包。
流量控制:TCP 通过滑动窗口机制实现流量控制,防止发送方发送数据过快而导致接收方缓冲区溢出。
拥塞控制:TCP 还具有拥塞控制功能,能够根据网络状况动态调整发送速率,避免网络拥塞。
字节流服务:TCP 将应用层的数据视为无结构的字节流,它负责将数据分割成合适大小的数据包进行传输,并在接收端将数据包重新组合成原始的字节流。

(2)TCP的工作原理

建立连接(三次握手):

  • SYN:客户端向服务器发送一个 SYN 包,并等待服务器确认。
  • SYN-ACK:服务器收到 SYN 包后,向客户端发送一个SYN-ACK包作为应答,此包同时包含服务器的初始序列号。
  • ACK:客户端收到 SYN-ACK 包后,向服务器发送一个ACK包作为应答,此包包含客户端的确认序列号。至此,TCP 连接建立完成。
  • 数据传输:在连接建立后,双方可以开始发送和接收数据。发送方将数据分割成适当大小的数据包,并为每个数据包添加 TCP 头部信息(包括序列号、确认号等),然后通过网络层发送出去。接收方收到数据包后,根据序列号将数据重新组合成原始的字节流,并发送确认包给发送方。

关闭连接(四次挥手):

  • FIN:当一方想要关闭连接时,它会发送一个 FIN 包给对方。
  • ACK:对方收到 FIN 包后,发送一个 ACK 包作为应答。
  • FIN:一段时间后,对方也发送一个 FIN 包来表示自己也想关闭连接。
  • ACK:收到对方的 FIN 包后,发送一个 ACK 包作为应答。至此,TCP 连接关闭完成。

在整个过程中,TCP 通过序列号、确认号、重传机制等确保数据的可靠传输。同时,它还根据网络状况动态调整发送速率,实现流量控制和拥塞控制。

2.2 TCP 流量控制与拥塞控制

TCP 协议不仅提供了面向连接的、可靠的数据传输服务,还通过流量控制和拥塞控制机制来确保网络的高效和稳定运行。下面我将详细讲解TCP的流量控制与拥塞控制。

(1)TCP 流量控制

TCP 流量控制的主要目的是防止发送方发送数据过快而导致接收方缓冲区溢出。TCP 通过一种称为“滑动窗口”的机制来实现流量控制。

滑动窗口实际上表示的是接收方的接收能力,即当前还有多少空间可以接收新的数据。发送方会根据这个窗口大小来决定发送多少数据。每次发送数据时,发送方都会在TCP头部中携带一个窗口大小字段,告诉接收方自己的当前窗口大小。接收方在收到数据后,会根据自己的缓冲区情况更新这个窗口大小,并发送给发送方。发送方根据接收到的窗口大小来调整自己的发送速率,确保不会发送过多数据导致接收方缓冲区溢出。

这种流量控制机制使得TCP能够根据网络的实际状况动态调整发送速率,实现网络资源的有效利用。

(2)TCP拥塞控制

TCP 拥塞控制的主要目的是防止过多的数据注入网络,避免网络出现拥塞现象。TCP 通过一系列算法来实现拥塞控制,包括慢开始、拥塞避免、快重传和快恢复等。

  • 慢开始:一开始向网络中注入的报文段较少,主要是为了避免突然发送大量数据导致网络拥塞。发送方维护一个叫做拥塞窗口(cwnd)的状态变量,其值取决于网络的拥塞程度。在慢开始阶段,拥塞窗口的大小会逐渐增大,直到达到一个门限值(ssthresh)。
  • 拥塞避免:当拥塞窗口大小超过门限值时,TCP 进入拥塞避免阶段。在这个阶段,拥塞窗口的增长速度会减慢,以避免网络拥塞的发生。
  • 快重传:当接收方收到一个失序的报文段时,它会立即发送一个重复确认报文给发送方,告诉发送方有一个报文段丢失了。发送方在连续收到多个重复确认报文后,会立即重传丢失的报文段,而不需要等待超时定时器超时。这种机制可以快速地恢复丢失的数据包,提高传输效率。
  • 快恢复:与快重传配合使用的算法。当发送方收到多个重复确认报文时,它会调整拥塞窗口的大小和门限值,并进入快恢复阶段。在这个阶段,发送方会尝试快速恢复网络的拥塞状况,避免进一步的拥塞发生。

通过这些拥塞控制算法,TCP 能够动态地调整发送速率,以适应网络的变化,避免网络拥塞的发生,从而确保数据的高效和可靠传输。

2.3 示例

下面是一个简单的 、TCP 客户端和服务器的示例。这个示例将创建一个简单的 TCP 服务器,它接受客户端的连接,并接收从客户端发送的字符串消息。然后,服务器会将消息发送回客户端。

首先,创建一个 TCP 服务器:

#include <iostream>  
#include <cstring>  
#include <sys/socket.h>  
#include <arpa/inet.h>  
#include <unistd.h>  const int PORT = 8080;  
const int BACKLOG = 10;  
const int BUFFER_SIZE = 1024;  int main() {  int server_fd, client_fd;  struct sockaddr_in server_addr, client_addr;  socklen_t client_len = sizeof(client_addr);  char buffer[BUFFER_SIZE];  // 创建socket  if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {  perror("socket failed");  exit(EXIT_FAILURE);  }  // 设置服务器地址信息  memset(&server_addr, 0, sizeof(server_addr));  server_addr.sin_family = AF_INET;  server_addr.sin_addr.s_addr = INADDR_ANY;  server_addr.sin_port = htons(PORT);  // 绑定socket到地址  if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {  perror("bind failed");  exit(EXIT_FAILURE);  }  // 监听连接  if (listen(server_fd, BACKLOG) < 0) {  perror("listen");  exit(EXIT_FAILURE);  }  // 接受客户端连接  if ((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len)) < 0) {  perror("accept");  exit(EXIT_FAILURE);  }  // 读取客户端发送的消息  memset(buffer, 0, BUFFER_SIZE);  int valread = read(client_fd, buffer, BUFFER_SIZE);  std::cout << "Message received from client: " << buffer << std::endl;  // 发送消息回客户端  send(client_fd, buffer, strlen(buffer), 0);  // 关闭连接  close(client_fd);  close(server_fd);  return 0;  
}

接下来,创建一个 TCP 客户端:

#include <iostream>  
#include <cstring>  
#include <sys/socket.h>  
#include <arpa/inet.h>  
#include <unistd.h>  const char *SERVER_IP = "127.0.0.1";  
const int PORT = 8080;  
const int BUFFER_SIZE = 1024;  int main() {  int sock = 0;  struct sockaddr_in serv_addr;  char buffer[BUFFER_SIZE] = "Hello from client";  // 创建socket  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {  std::cerr << "Socket creation error" << std::endl;  return -1;  }  // 设置服务器地址信息  memset(&serv_addr, '0', sizeof(serv_addr));  serv_addr.sin_family = AF_INET;  serv_addr.sin_port = htons(PORT);  // 将IP地址从点分十进制转换为网络字节序  if (inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr) <= 0) {  std::cerr << "Invalid address/Address not supported" << std::endl;  return -1;  }  // 连接到服务器  if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {  std::cerr << "Connection failed" << std::endl;  return -1;  }  // 发送消息到服务器  send(sock, buffer, strlen(buffer), 0);  // 接收来自服务器的消息  memset(buffer, 0, BUFFER_SIZE);  int valread = read(sock, buffer, BUFFER_SIZE);if (valread < 0) {std::cerr << "Read failed" << std::endl;return -1;}std::cout << "Message received from server: " << buffer << std::endl;// 关闭socket  close(sock);  return 0;}

3 UDP 协议

3.1 UDP 的特点与工作原理

传输层中的 UDP(User Datagram Protocol,用户数据报协议)是一种无连接的传输层协议。与 TCP(Transmission Control Protocol,传输控制协议)不同,UDP不提供数据包的排序、流量控制或错误检查与重传机制,因此 UDP 协议具有轻量级和高效的特性。

(1)UDP 的特点

  • 无连接:UDP 是一种无连接的协议,即发送端和接收端在传输数据前不需要建立连接。这意味着 UDP 发送数据前不需要进行三次握手,从而减少了传输延迟,提高了效率。但这也意味着 UDP 不提供可靠性保证,数据包的传输可能会丢失、乱序或重复。

  • 不可靠性:由于 UDP 不提供数据包的排序、错误检查与重传机制,因此它被认为是一种不可靠的传输协议。在需要保证数据完整性和顺序性的场景中,通常不会选择 UDP。

  • 面向报文:UDP 是面向报文的协议,即 UDP 将应用层交下来的报文看成一个个独立的报文,不拆分,也不合并。这样,UDP 报文在传输过程中是保持不变的,因此 UDP 传输是简单的、快速的。

  • 支持多播和广播:UDP 支持多播和广播功能,这使得 UDP 在一些特定的应用场景(如视频会议、实时音频传输等)中非常有用。

  • 头部开销小:UDP 的头部只有 8 个字节,比 TCP 的 20 个字节头部要小很多,因此 UDP 的传输开销较小,适用于对数据传输效率要求较高的场景。

(2)UDP 的工作原理

  • 数据封装:在应用层将数据封装成报文后,UDP 协议会在报文前加上 UDP 头部,形成 UDP 数据报。UDP 头部包含了源端口号、目的端口号、数据长度和校验和等信息。

  • 数据传输:UDP 数据报通过 IP 协议进行路由选择和数据传输。由于 UDP 是无连接的,因此发送端和接收端不需要进行连接建立过程,可以直接发送数据。

  • 数据接收:接收端的 UDP 协议会解析收到的 IP 数据报,提取出 UDP 数据报,并根据目的端口号将数据报传递给相应的应用程序。

  • 错误处理:由于 UDP 不提供错误检查和重传机制,如果数据在传输过程中发生错误或丢失,UDP 不会进行任何处理。接收端的应用程序需要自己处理可能出现的错误或数据不完整的情况。

(3)应用场景

UDP 因其简单、高效的特点,在一些特定的应用场景中非常有用。例如,实时音视频传输、在线游戏、DNS 解析等场景通常使用 UDP 协议。在这些场景中,数据的实时性和效率比数据的完整性和可靠性更重要。

3.2 示例

下面是一个简单的 UDP 客户端和服务器的示例。在这个示例中,服务器会监听特定的端口,等待客户端发送数据。客户端会向服务器发送一条消息,然后接收服务器的响应。

首先是 UDP 服务器的代码:

#include <iostream>  
#include <cstring>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <unistd.h>  
#include <arpa/inet.h>  const int PORT = 8080;  
const int BUFFER_SIZE = 1024;  int main() {  int sockfd;  struct sockaddr_in server_addr, client_addr;  socklen_t client_len = sizeof(client_addr);  char buffer[BUFFER_SIZE];  // 创建UDP socket  if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {  std::cerr << "Error creating socket" << std::endl;  return -1;  }  // 设置服务器地址信息  memset(&server_addr, 0, sizeof(server_addr));  server_addr.sin_family = AF_INET;  server_addr.sin_addr.s_addr = INADDR_ANY;  server_addr.sin_port = htons(PORT);  // 绑定socket到地址  if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {  std::cerr << "Error binding socket" << std::endl;  return -1;  }  std::cout << "Server listening on port " << PORT << std::endl;  // 循环接收数据  while (true) {  // 接收客户端发送的数据  memset(buffer, 0, BUFFER_SIZE);  int valread = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client_addr, &client_len);  std::cout << "Received message from client: " << buffer << std::endl;  // 发送响应给客户端  std::string response = "Hello from server!";  sendto(sockfd, response.c_str(), response.length(), 0, (struct sockaddr *)&client_addr, client_len);  }  // 关闭socket  close(sockfd);  return 0;  
}

接下来是 UDP 客户端的代码:

#include <iostream>  
#include <cstring>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <unistd.h>  
#include <arpa/inet.h>  const char *SERVER_IP = "127.0.0.1";  
const int PORT = 8080;  
const int BUFFER_SIZE = 1024;  int main() {  int sockfd;  struct sockaddr_in server_addr;  char buffer[BUFFER_SIZE];  // 创建UDP socket  if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {  std::cerr << "Error creating socket" << std::endl;  return -1;  }  // 设置服务器地址信息  memset(&server_addr, 0, sizeof(server_addr));  server_addr.sin_family = AF_INET;  server_addr.sin_port = htons(PORT);  if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) <= 0) {  std::cerr << "Invalid address/Address not supported" << std::endl;  return -1;  }  // 发送数据到服务器  std::string message = "Hello from client!";  sendto(sockfd, message.c_str(), message.length(), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));  // 接收来自服务器的响应  memset(buffer, 0, BUFFER_SIZE);  int valread = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, NULL, 0);  std::cout << "Received message from server: " << buffer << std::endl;  // 关闭socket  close(sockfd);  return 0;  
}

这个示例展示了 UDP 的基本工作原理:客户端和服务器通过 socket 进行通信,发送和接收数据报。由于 UDP 是无连接的,因此不需要建立连接过程,可以直接发送和接收数据。然而,这也意味着 UDP 不提供可靠性保证,数据可能会丢失、乱序或重复,因此在使用 UDP 时需要自行处理这些问题。

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

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

相关文章

内网穿透的应用-如何在Android Termux上部署MySQL数据库并实现无公网IP远程访问

文章目录 前言1.安装MariaDB2.安装cpolar内网穿透工具3. 创建安全隧道映射mysql4. 公网远程连接5. 固定远程连接地址 前言 Android作为移动设备&#xff0c;尽管最初并非设计为服务器&#xff0c;但是随着技术的进步我们可以将Android配置为生产力工具&#xff0c;变成一个随身…

labview如何创建2D多曲线XY图和3D图

1如何使用labview创建2D多曲线图 使用“索引与捆绑簇数组”函数将多个一维数组捆绑成一个簇的数组&#xff0c;然后将结果赋值给XY图&#xff0c;这样一个多曲线XY图就生成了。也可以自己去手动索引&#xff0c;手动捆绑并生成数组&#xff0c;结果是一样的 2.如何创建3D图 在…

pix2pix GAN

import os os.environ[TF_CPP_MIN_LOG_LEVEL] = 2#设置tensorflow的日志级别 from tensorflow.python.platform import build_info import tensorflow as tf import os # 用于处理文件系统路径的面向对象的库。pathlib 提供了 Path 类, #该类表示文件系统路径,并提供了很多方…

Vue2 —— 学习(一)

&#xff08;二&#xff09;简单案例 1.实现过程 容器设置 Vue 实例设置 2.实现结果 3.注意事项 &#xff08;三&#xff09;Vue 插件 ​编辑三、Vue 模板语法 &#xff08;一&#xff09;插值语法 {{ }}&#xff1a; &#xff08;二&#xff09;指令语法 v- 四、…

如何更新Code::blocks的MinGW

前言 LVGL V9版本更新了很多新特性&#xff0c;其中windows平台部分也进行了优化&#xff0c;如果你是用的是Code::blocks体验LVGL那么在编译时会不通过&#xff1b;因为如果你使用的是 Code::blocks 20.03并且使用内置的MinGW&#xff0c;那么就会因为MinGW版本过低遇到下面所…

babyAGI(8)-babyCoder5主程序逻辑

前期代码都以阅读完毕&#xff0c;接下来我们来看主程序逻辑&#xff0c;建议大家好好看看流程图&#xff0c;有个流程的影响 1. 创建任务 下面一段代码主要用来创建任务以及打印相关信息&#xff0c;调用了四个agents code_tasks_initializer_agent 初始化任务code_tasks_…

信息系统项目管理师——第18章项目绩效域管理(二)

项目工作绩效域 预期目标 高效且有数的项目绩效 2.适合项目和环境的项目过程 3.干系人适当的沟通和参与 4.对实物资源进行了有效管理 5.对采购进行了有效管理 6.有效处理了变更 7.通过持续学习和过程改进提高了团队能力 绩效要点 1.项目过程 2.项目制约因素 3.专注于工作过…

React - 连连看小游戏

简介 小时候经常玩连连看小游戏。在游戏中&#xff0c;当找到2个相同的元素就可以消除元素。 本文会借助react实现连连看小游戏。 实现效果 实现难点 1.item 生成 1. 每一个图片都是一个item&#xff0c;items数组的大小为size*size。 item对象包括grid布局的位置&#xff0c;…

【爬虫开发】爬虫从0到1全知识md笔记第4篇:Selenium课程概要,selenium的介绍【附代码文档】

爬虫开发从0到1全知识教程完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;爬虫课程概要&#xff0c;爬虫基础爬虫概述,,http协议复习。requests模块&#xff0c;requests模块1. requests模块介绍,2. response响应对象,3. requests模块发送请求,4. request…

入门用Hive构建数据仓库

在当今数据爆炸的时代&#xff0c;构建高效的数据仓库是企业实现数据驱动决策的关键。Apache Hive 是一个基于 Hadoop 的数据仓库工具&#xff0c;可以轻松地进行数据存储、查询和分析。本文将介绍什么是 Hive、为什么选择 Hive 构建数据仓库、如何搭建 Hive 环境以及如何在 Hi…

分解因数

描述 给出一个正整数 a&#xff0c;要求分解成若干个正整数的乘积&#xff0c;即 aa1a2a3…an&#xff0c;并且 1<a1≤a2≤a3≤…≤an&#xff0c;问这样的分解的方案种数有多少。注意到aa 也是一种分解。 输入描述 第 1 行是测试数据的组数 n(1≤n≤10)&#xff0c;后面…

AcWing 4199. 公约数(数学-约数)

给定两个正整数 a a a 和 b b b。 你需要回答 q q q 个询问。 每个询问给定两个整数 l , r l,r l,r&#xff0c;你需要找到最大的整数 x x x&#xff0c;满足&#xff1a; x x x 是 a a a 和 b b b 的公约数。 l ≤ x ≤ r l≤x≤r l≤x≤r。 输入格式 第一行包含两个…

【PaletX】ui组件使用

表单 当表单不是以component和template形式时&#xff0c;不需要patchValue重新赋值 srcObj用于赋值表单初始值 表单校验 优先级&#xff1a;输入过程中的校验 > 焦点离开后的校验 > 点击确定按钮后的校验 适用场景&#xff1a; 输入过程中的校验&#xff1a;焦点进入…

类与对象(一)

目录 一、类的引入和定义 二、类的访问限定符及封装 1&#xff09;访问限定符 2&#xff09;封装 三、类的作用域和实例化 1&#xff09;类的作用域 2&#xff09;实例化 四、类的大小 1&#xff09;类的大小计算方式 2&#xff09;特殊的类的大小 五、this指针 1&…

C++设计模式:观察者模式(三)

1、定义与动机 观察者模式定义&#xff1a;定义对象间的一种1对多&#xff08;变化&#xff09;的依赖关系&#xff0c;以便当一个对象&#xff08;Subject&#xff09;的状态发生比改变时&#xff0c;所有依赖于它的对象都得到通知并且自动更新 再软件构建过程中&#xff0c…

回溯算法|332.重新安排行程 51. N皇后 37. 解数独

332.重新安排行程 力扣题目链接 class Solution { private: // unordered_map<出发机场, map<到达机场, 航班次数>> targets unordered_map<string, map<string, int>> targets; bool backtracking(int ticketNum, vector<string>& result…

蓝桥杯刷题-06-砍树-图遍历DFS⭐⭐⭐⭐

给定一棵由 n 个结点组成的树以及 m 个不重复的无序数对 (a1, b1), (a2, b2), . . . , (am, bm)&#xff0c;其中 ai 互不相同&#xff0c;bi 互不相同&#xff0c;ai ≠ bj(1 ≤ i, j ≤ m)。 小明想知道是否能够选择一条树上的边砍断&#xff0c;使得对于每个 (ai , bi) 满足…

小程序如何设置余额充值和消费功能

小程序中设置余额充值和消费功能非常重要的&#xff0c;通过让客户在小程序中进行余额充值&#xff0c;不仅可以提高用户粘性&#xff0c;还可以促进消费&#xff0c;增加用户忠诚度。以下是如何在小程序中设置余额充值和消费功能的步骤&#xff1a; 1. **设计充值入口**&…

代码随想录-14day:二叉树3

一、二叉树最大深度 最大深度&#xff1a;根节点到最远叶子节点的最长路径上的节点数。 可以使用迭代法和递归法&#xff0c;以递归法为例&#xff1a;还是以递归三要素为基准&#xff0c;进行解决。 int maxDepth(struct TreeNode* root) {// struct TreeNode** NodeList …

【力扣】242. 有效的字母异位词

242. 有效的字母异位词 题目描述 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t 互为字母异位词。 示例 1: 输入: s “anagram”, t “nagaram” 输出…