摘要 本文阐述了 TCP 作为一种面向连接、可靠且具备滑动窗口流量控制与复杂拥塞控制机制的传输层协议,在 ESP32 平台上的实现与应用逻辑;而 UDP 在传输时不建立连接,具有高速、可广播等特性
参考资料 lwIP – ESP-IDF 编程指南 lwip/socket.h – esp-idf – Github Michael_ee视频教程 – Bilibili
文章目录
-
- TCP & UDP
-
- TCP
-
- TCP Client Example
- TCP Server Example
- UDP
- Difference between TCP and UDP
TCP & UDP
TCP
TCP 是一种面向连接的、可靠的、基于字节流的传输层通信协议,在数据传输过程中,TCP 将应用层数据分割成报文段,并利用序列号(Sequence Number)和确认应答(ACK)机制确保数据的有序到达与完整性;若发生丢包或超时,协议栈会自动触发重传机制;TCP 集成了滑动窗口(Sliding Window)机制以实现流量控制,防止发送方溢出接收方的缓冲区,并结合慢启动、拥塞避免、快重传和快恢复等拥塞控制算法,根据网络带宽状况动态调节发送速率,从而在不可靠的互联网络中提供高可靠性的端到端数据传输服务
ESP32 使用的 TCP 协议基于 LwIP 协议栈,支持标准的 BSD Sockets API(一个跨平台的 Socket API)
Socket(套接字) 是计算机网络通信的核心接口与端点,它由IP地址(用于定位计算机)和端口号(用于定位具体的应用程序)共同组成,充当了数据传输的门户;当两个程序需要跨网络交流时,它们必须各自创建一个 Socket 并建立连接,依靠它作为标准化的 API 来完成所有数据的发送与接收 BSD Socket API 是一种常见的跨平台 TCP/IP 套接字 API,最初源于 UNIX 操作系统的伯克利标准发行版,现已标准化为 POSIX 规范的一部分;BSD 套接字有时也称 POSIX 套接字,或伯克利套接字 在 ESP-IDF 中,lwIP 支持 BSD 套接字 API 的所有常见用法,然而,并非所有操作都完全线程安全,因此多个线程同时进行读写可能需要额外的同步机制
Workflow 标准 Socket 程序:Initialize->Connect->Communicate->Disconnect
#mermaid-svg-9O9F10wWRUGLoDHT{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9O9F10wWRUGLoDHT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9O9F10wWRUGLoDHT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9O9F10wWRUGLoDHT .error-icon{fill:#552222;}#mermaid-svg-9O9F10wWRUGLoDHT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9O9F10wWRUGLoDHT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9O9F10wWRUGLoDHT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9O9F10wWRUGLoDHT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9O9F10wWRUGLoDHT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9O9F10wWRUGLoDHT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9O9F10wWRUGLoDHT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9O9F10wWRUGLoDHT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9O9F10wWRUGLoDHT .marker.cross{stroke:#333333;}#mermaid-svg-9O9F10wWRUGLoDHT svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9O9F10wWRUGLoDHT p{margin:0;}#mermaid-svg-9O9F10wWRUGLoDHT .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-9O9F10wWRUGLoDHT .cluster-label text{fill:#333;}#mermaid-svg-9O9F10wWRUGLoDHT .cluster-label span{color:#333;}#mermaid-svg-9O9F10wWRUGLoDHT .cluster-label span p{background-color:transparent;}#mermaid-svg-9O9F10wWRUGLoDHT .label text,#mermaid-svg-9O9F10wWRUGLoDHT span{fill:#333;color:#333;}#mermaid-svg-9O9F10wWRUGLoDHT .node rect,#mermaid-svg-9O9F10wWRUGLoDHT .node circle,#mermaid-svg-9O9F10wWRUGLoDHT .node ellipse,#mermaid-svg-9O9F10wWRUGLoDHT .node polygon,#mermaid-svg-9O9F10wWRUGLoDHT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9O9F10wWRUGLoDHT .rough-node .label text,#mermaid-svg-9O9F10wWRUGLoDHT .node .label text,#mermaid-svg-9O9F10wWRUGLoDHT .image-shape .label,#mermaid-svg-9O9F10wWRUGLoDHT .icon-shape .label{text-anchor:middle;}#mermaid-svg-9O9F10wWRUGLoDHT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9O9F10wWRUGLoDHT .rough-node .label,#mermaid-svg-9O9F10wWRUGLoDHT .node .label,#mermaid-svg-9O9F10wWRUGLoDHT .image-shape .label,#mermaid-svg-9O9F10wWRUGLoDHT .icon-shape .label{text-align:center;}#mermaid-svg-9O9F10wWRUGLoDHT .node.clickable{cursor:pointer;}#mermaid-svg-9O9F10wWRUGLoDHT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9O9F10wWRUGLoDHT .arrowheadPath{fill:#333333;}#mermaid-svg-9O9F10wWRUGLoDHT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9O9F10wWRUGLoDHT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9O9F10wWRUGLoDHT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9O9F10wWRUGLoDHT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9O9F10wWRUGLoDHT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9O9F10wWRUGLoDHT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9O9F10wWRUGLoDHT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9O9F10wWRUGLoDHT .cluster text{fill:#333;}#mermaid-svg-9O9F10wWRUGLoDHT .cluster span{color:#333;}#mermaid-svg-9O9F10wWRUGLoDHT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9O9F10wWRUGLoDHT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9O9F10wWRUGLoDHT rect.text{fill:none;stroke-width:0;}#mermaid-svg-9O9F10wWRUGLoDHT .icon-shape,#mermaid-svg-9O9F10wWRUGLoDHT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9O9F10wWRUGLoDHT .icon-shape p,#mermaid-svg-9O9F10wWRUGLoDHT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9O9F10wWRUGLoDHT .icon-shape rect,#mermaid-svg-9O9F10wWRUGLoDHT .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9O9F10wWRUGLoDHT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9O9F10wWRUGLoDHT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9O9F10wWRUGLoDHT :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Client
Server
socket()
bind()
listen()
accept()
recv()
send()
close()
socket()
connect()
send()
recv()
close()
使用实例:protocol -> tcp_client、protocol -> tcp_server
这里要注意 esp32s3 只支持 2.4G 频段 Wi-Fi disconnected 201 表示未找到 AP,需要检查热点是否在 2.4G 频段
- socket() 创建一个 socket 用于侦听/连接
- bind() 绑定 socket 到一个端口
- listen() 侦听是否有客户端发起连接
- accept() 接受当前连接
- send() 数据发送
- recv() 数据接收
- close() 关闭连接
- connect() 连接到服务端
在 Server 示例中,一个 Server 只能同时与一个 Client 连接与数据传输,实际上,ESP-IDF/lwIP 支持多个 TCP 连接并发,若需要连接多个 Client,可以在一个 task 中使用非阻塞 socket + select()/poll() 多路复用多个已 accept 的 sock,或在每次 accept() 后为该客户端创建一个专用 task 做 recv/send,而主 task 只负责 listen/accept
在 TCP Server 和 Client 示例中,会使用 example_connect() 函数来快速建立一个 WiFi 连接,这个函数并不适用于真实项目中,仅用于 demo
TCP Client Example
headers
#include "sdkconfig.h"
#include <string.h>
#include <unistd.h> // POSIX universal headers
#include <sys/socket.h> // POSIX socket headers(POSIX socket is based on BSD socket)
#include <errno.h> // Error codes definitions
#include <netdb.h> // struct addrinfo, network database operations
#include <arpa/inet.h> // IP address conversion functions
#include "esp_netif.h" // ESP32 network interface headers
#include "esp_log.h"
#if defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
#include "addr_from_stdin.h" // Mainly used by socket client examples which read IP address from stdin (if configured)
#endif
POSIX(可移植操作系统接口)是一层软件兼容接口层,在ESP32中通常封装在FreeRTOS之上;它允许开发者使用通用的、标准化的C语言函数(如用于文件操作的 open/read/write,用于多线程的 pthread)来替代特定硬件或RTOS专有的指令;这层接口的核心价值在于可移植性,意味着为Linux或UNIX编写的代码只需很少的修改甚至无需修改,就可以直接在ESP32上运行,极大地降低了跨平台开发的门槛 BSD Socket 与 POSIX 的关系可以概括为“起源”与“标准”的关系:BSD Socket是网络编程接口的鼻祖和事实标准,而 POSIX 则是后来将这套接口正式纳入规范的官方标准;在ESP32上编写网络代码时,使用的是符合POSIX标准的接口,但其核心语法和逻辑完全源自BSD Socket
sockaddr_in definition 定义目标服务器 IP 地址、端口、使用的协议
struct sockaddr_in dest_addr; // Target server address structure (IPv4)
inet_pton(AF_INET, host_ip, &dest_addr.sin_addr); // Convert the dotted decimal string `host_ip` to a binary IPv4 address.
dest_addr.sin_family = AF_INET; // Address family = IPv4
dest_addr.sin_port = htons(PORT); // Convert port number from host byte order to network byte order
addr_family = AF_INET;
ip_protocol = IPPROTO_IP; // 0, it means choose the protocol automatically based on the given socket type
// "sin_" means "socket internet"
Socket create and transmit
int sock = socket(addr_family, SOCK_STREAM, ip_protocol); // Create a socket, SOCK_STREAM means TCP
int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); // Connect the server
err = send(sock, payload, strlen(payload), 0); // Send msg
int len = recv(sock, rx_buffer, sizeof(rx_buffer) – 1, 0); // Receive msg
shutdown(sock, 0); // Shutdown socket
// 0 – No more receptions
// 1 – No more transmissions
// 2 – No more receptions or transmissions
close(sock); // Close socket
TCP Server Example
variable declarations
char addr_str[128];
int addr_family = (int)pvParameters; // Use the passed parameter to decide IPv4 or IPv6, here is IPv4
int ip_protocol = 0;
int keepAlive = 1; // Enable keepalive option
int keepIdle = KEEPALIVE_IDLE; // Start keepalive after this period idle(seconds)
int keepInterval = KEEPALIVE_INTERVAL; // Interval between keepalive probes(seconds)
int keepCount = KEEPALIVE_COUNT; // When to drop connection after how many failed probes
struct sockaddr_storage dest_addr; // Definition of the server address structure
#ifdef CONFIG_EXAMPLE_IPV4
if (addr_family == AF_INET)
{
struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY); // Set local IP address to any(0.0.0.0 means all local interfaces), it means listen on all local interfaces
dest_addr_ip4->sin_family = AF_INET;
dest_addr_ip4->sin_port = htons(PORT);
ip_protocol = IPPROTO_IP;
}
#endif
Socket create and transmit
int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol); // Create a socket
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // Enable address reuse, so we can bind a local ip and port when a previous connection is in TIME_WAIT state
// So that we can restart server quickly after shutdown() or close()
int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); // Bind scoket to the port
err = listen(listen_sock, 1); // Listen for incoming connections
// When this API is called, the tcp stack will set this socket as server socket
/* — */
int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len); // Accept a connection and store the client address info
// This is a blocking call, so if no connections are incoming, will wait here
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int)); // Enable keepalive
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int)); // Start keepalive after this period idle
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int)); // Interval between keepalive probes
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int)); // When to drop connection after how many failed probes
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) – 1); // Convert the client ip address from binary to dotted decimal string
do_retransmit(sock); // Process data from client
shutdown(sock, 0); // Shutdown socket
close(sock); // Close socket
/* — */
close(listen_sock); // Close the listening socket
- listen_sock 由 socket() 系统调用创建,并通过 bind() 绑定至本地特定的 IP 地址与端口,确立服务端的寻址入口;随后 listen() 函数将其从主动状态转换为被动监听模式,使其内核控制块开始维护未完成连接队列(SYN 队列)和已完成连接队列(Accept 队列);该 scoket的生命周期贯穿服务端的主进程,其核心职能仅限于连接管理(Connection Management),即处理 TCP 三次握手请求,并通过 accept() 派发新的文件描述符,本身不参与应用层数据的传输
- sock 是由 accept() 系统调用在三次握手成功后返回的全新描述符,代表一个完全建立的 TCP 连接会话(Session),它是内核中五元组(源 IP、源端口、目的 IP、目的端口、协议)的具体句柄,专门用于当前特定客户端与服务端之间的全双工数据传输,所有的 recv() 和 send() 操作均作用于该套接字;其生命周期是瞬态的,仅存在于会话期间,一旦会话结束或发生链路错误,程序需调用 close() 将其销毁以释放系统资源,且不同客户端的 sock 相互隔离,互不干涉
UDP
UDP 是一种无连接的、面向报文的传输层协议,它不维护连接状态,发送端在数据发出前无需与接收端建立握手,直接将应用层数据封装成数据报发送 UDP 提供“尽力而为”(Best-Effort)的交付服务,不保证数据包的到达、顺序或无重复,且不具备内置的重传、流量控制或拥塞控制机制,由于其头部开销极小(仅 8 字节)且无连接建立时延,UDP 具有极高的传输效率和确定性的时序特征,适用于对实时性要求极高但对少量丢包不敏感的应用场景
多人在线游戏通常优先采用 UDP 协议,利用其无连接、低头部开销和无拥塞控制的特性来最小化端到端延迟;然而由于 UDP 遵循“尽力而为”(Best-Effort)交付原则且缺乏 TCP 的自动重传请求和序列纠正机制,物理链路中的拥塞或信号衰减导致的丢包会被直接暴露给应用层,导致状态同步数据缺失 这就是为什么在打 CS 时,丢包率一高就会出现跑步漂移和自己能动,但是对面和队友站住不动的现象
Workflow
#mermaid-svg-GehEGe1ptJPp0Lg0{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GehEGe1ptJPp0Lg0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GehEGe1ptJPp0Lg0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GehEGe1ptJPp0Lg0 .error-icon{fill:#552222;}#mermaid-svg-GehEGe1ptJPp0Lg0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GehEGe1ptJPp0Lg0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GehEGe1ptJPp0Lg0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GehEGe1ptJPp0Lg0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GehEGe1ptJPp0Lg0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GehEGe1ptJPp0Lg0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GehEGe1ptJPp0Lg0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GehEGe1ptJPp0Lg0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GehEGe1ptJPp0Lg0 .marker.cross{stroke:#333333;}#mermaid-svg-GehEGe1ptJPp0Lg0 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GehEGe1ptJPp0Lg0 p{margin:0;}#mermaid-svg-GehEGe1ptJPp0Lg0 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-GehEGe1ptJPp0Lg0 .cluster-label text{fill:#333;}#mermaid-svg-GehEGe1ptJPp0Lg0 .cluster-label span{color:#333;}#mermaid-svg-GehEGe1ptJPp0Lg0 .cluster-label span p{background-color:transparent;}#mermaid-svg-GehEGe1ptJPp0Lg0 .label text,#mermaid-svg-GehEGe1ptJPp0Lg0 span{fill:#333;color:#333;}#mermaid-svg-GehEGe1ptJPp0Lg0 .node rect,#mermaid-svg-GehEGe1ptJPp0Lg0 .node circle,#mermaid-svg-GehEGe1ptJPp0Lg0 .node ellipse,#mermaid-svg-GehEGe1ptJPp0Lg0 .node polygon,#mermaid-svg-GehEGe1ptJPp0Lg0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GehEGe1ptJPp0Lg0 .rough-node .label text,#mermaid-svg-GehEGe1ptJPp0Lg0 .node .label text,#mermaid-svg-GehEGe1ptJPp0Lg0 .image-shape .label,#mermaid-svg-GehEGe1ptJPp0Lg0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-GehEGe1ptJPp0Lg0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GehEGe1ptJPp0Lg0 .rough-node .label,#mermaid-svg-GehEGe1ptJPp0Lg0 .node .label,#mermaid-svg-GehEGe1ptJPp0Lg0 .image-shape .label,#mermaid-svg-GehEGe1ptJPp0Lg0 .icon-shape .label{text-align:center;}#mermaid-svg-GehEGe1ptJPp0Lg0 .node.clickable{cursor:pointer;}#mermaid-svg-GehEGe1ptJPp0Lg0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GehEGe1ptJPp0Lg0 .arrowheadPath{fill:#333333;}#mermaid-svg-GehEGe1ptJPp0Lg0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GehEGe1ptJPp0Lg0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GehEGe1ptJPp0Lg0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GehEGe1ptJPp0Lg0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GehEGe1ptJPp0Lg0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GehEGe1ptJPp0Lg0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GehEGe1ptJPp0Lg0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GehEGe1ptJPp0Lg0 .cluster text{fill:#333;}#mermaid-svg-GehEGe1ptJPp0Lg0 .cluster span{color:#333;}#mermaid-svg-GehEGe1ptJPp0Lg0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GehEGe1ptJPp0Lg0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GehEGe1ptJPp0Lg0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-GehEGe1ptJPp0Lg0 .icon-shape,#mermaid-svg-GehEGe1ptJPp0Lg0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GehEGe1ptJPp0Lg0 .icon-shape p,#mermaid-svg-GehEGe1ptJPp0Lg0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GehEGe1ptJPp0Lg0 .icon-shape rect,#mermaid-svg-GehEGe1ptJPp0Lg0 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GehEGe1ptJPp0Lg0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GehEGe1ptJPp0Lg0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GehEGe1ptJPp0Lg0 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Client
Server
socket()
bind()
recvfrom()
sendto()
close()
socket()
sendto()
recvfrom()
close()
sendto() 和 recvfrom() 在使用时需要传入目标 ip 和端口
Difference between TCP and UDP
- TCP 是基于连接的数据传输协议,UDP 没有建立连接(TCP 在数据交换之前建立了连接,send 和 recv 无需包含 ip 地址和端口,而 UDP 没有建立连接,在数据交换时,recvfrom 和 sendto 需要包含 ip 地址和端口)
- TCP 的数据传输是可靠的,而 UDP 的发送方不能保证接收方一定能够接收到数据,是不可靠的
- 由于 TCP 存在建立连接和数据校验的过程,速度较慢,而 UDP 速度较快
- 由于 TCP 需要建立连接,只能用于点对点传输,而 UDP 可以进行广播和组播
- TCP 常用于 email、ftp、http/https 等,UDP 常用于实时音视频传输等




