找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3858

积分

0

好友

567

主题
发表于 7 小时前 | 查看: 5| 回复: 0

今天同事突然问起:“TCP是怎么进行socket通信的?UDP能进行socket通信吗?” 这看似基础的问题,竟让我一时语塞。这提醒我,越是核心的概念,越有必要系统地梳理一遍。因此,我将自己的理解整理成文,若有不当之处,还请大家不吝指正。

Socket与C/S架构

要理解Socket,首先得了解互联网的经典架构——C/S(客户端/服务器)架构。这个模型无处不在,以大家熟悉的《王者荣耀》为例:腾讯的云服务器是服务端,而我们手机上的游戏App就是客户端。二者之间要通信,就需要一个通用的“接口”,这就是Socket

那么,Socket在网络协议栈中究竟处于什么位置呢?它就像是连接应用程序与底层网络协议的桥梁。

计算机网络协议分层及Socket位置示意图

简单来说:

  • 在网络通信中:Socket端点由IP地址和端口号唯一标识。
  • 在编程中:Socket是操作系统提供的一个接口,它封装了复杂的网络通信能力,让开发者可以像操作文件一样进行网络读写。

TCP协议:可靠的“连接”

TCP(传输控制协议) 是一种网络协议。协议可以理解为通信双方必须共同遵守的约定和规则集合。就像两个人交谈必须使用同一种语言,网络中的两台机器也必须遵循相同的协议才能正确交换信息。

说到TCP,最广为人知的特点就是“面向连接”和“可靠传输”,其核心机制便是三次握手四次挥手

TCP的三次握手(建立连接)

TCP三次握手过程详解

建立连接需要三步握手,目的是同步双方的初始序列号(ISN),并确认彼此的收发能力正常。

  1. SYN:客户端发送一个SYN包(seq=x),说:“服务端你好,我想和你建立一条链接通道。”
  2. SYN-ACK:服务端收到后,回复SYN-ACK包(seq=y, ACK=x+1),意思是:“好的,我同意你的请求。另外,我也想和你建立一条链接通道。”
  3. ACK:客户端最后回复一个ACK包(ACK=y+1),确认:“好的,我也同意。”

至此,双向通信通道建立成功,双方进入ESTABLISHED状态。

TCP的可靠传输机制

连接建立后,TCP通过确认应答(ACK)机制来保证可靠性。

TCP确认应答机制示例

如图所示,客户端发送“老铁你真帅”(携带序列号和确认号),服务端收到后必须回复一个确认包(ACK x+2),告知客户端“我已收到”。如果客户端在一定时间内没收到这个确认,就会认为数据包丢失并重发。这正是TCP被称为可靠传输协议的由来。

TCP的四次挥手(断开连接)

TCP四次挥手过程详解

断开连接为何需要四步?因为TCP连接是全双工的,即数据可以双向独立传输。因此,关闭连接需要每一方向单独关闭自己的数据通道。

  1. FIN:客户端主动发起关闭,发送FIN包(seq=x+2),表示:“服务端你好,我准备关闭我对你发送信息的通道。” 客户端进入FIN_WAIT_1状态。
  2. ACK:服务端收到后,回复ACK包,说:“我知道了。” 服务端进入CLOSE_WAIT状态,客户端进入FIN_WAIT_2状态。此时,客户端到服务端的通道关闭。
  3. FIN:当服务端也准备关闭时,发送自己的FIN包(seq=y+1),表示:“客户端你好,我准备关闭我对你发送信息的通道。” 服务端进入LAST_ACK状态。
  4. ACK:客户端收到后,回复最后一个ACK包确认。客户端进入TIME_WAIT状态,等待一段时间后彻底关闭。服务端收到ACK后连接关闭。
  • 状态简析
    • FIN_WAIT_1/2:主动关闭方等待对方确认或关闭。
    • TIME_WAIT:主动关闭方发送最后ACK后等待,防止包丢失导致异常。
    • CLOSE_WAIT:被动关闭方收到FIN后等待应用层处理关闭。
    • LAST_ACK:被动关闭方发送自己的FIN后等待最终确认。

UDP协议:高效的“报文”

与TCP的字节流模式不同,UDP(用户数据报协议) 是面向消息的。

UDP基于数据报(消息)的传输原理

UDP每次发送的都是一个完整的、有头有尾的数据包(称为数据报),接收方也是以整个报文为单位接收。更关键的是,UDP是无连接的。

UDP无连接通信示意图

如图所示,客户端无需事先“握手”建立连接,可以直接向服务器地址发送数据包。服务器收到后,可以选择是否回复。整个过程没有连接建立和拆除的开销。UDP发送数据后便不再过问,不保证对方一定能收到,也不保证顺序。

TCP vs UDP:核心差异一览

为了更直观地对比,我们用一个表格总结:

TCP与UDP协议特性对比表

实战:Socket通信代码示例

理论说完了,来看看它们如何通过 Socket接口 实现通信。简单类比:

  • TCP Socket:面向连接,可靠传输,像打电话。
  • UDP Socket:无连接,不可靠传输,像发短信。

TCP Socket通信示例

服务端代码 (C语言)

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    // 1. 创建 TCP Socket
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);

    // 2. 绑定 IP 和端口
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡
    addr.sin_port = htons(8080);       // 端口号
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));

    // 3. 开始监听
    listen(server_fd, 5); // 允许最多 5 个客户端排队

    // 4. 接受客户端连接
    int client_fd = accept(server_fd, NULL, NULL);

    // 5. 发送数据
    char msg[] = "Hello TCP Client!";
    send(client_fd, msg, sizeof(msg), 0);

    // 6. 关闭连接
    close(client_fd);
    close(server_fd);
    return 0;
}

客户端代码

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
    // 1. 创建 TCP Socket
    int sock = socket(AF_INET, SOCK_STREAM, 0);

    // 2. 连接服务器
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr); // 服务器 IP
    connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));

    // 3. 接收数据
    char buffer[1024] = {0};
    recv(sock, buffer, 1024, 0);
    printf("Server says: %s\n", buffer);

    // 4. 关闭连接
    close(sock);
    return 0;
}

TCP Socket 特点小结

  • 优点:可靠传输、保证顺序、有流量控制。
  • 缺点:建立连接延迟高、头部开销大、不适合实时性要求极高的场景(如音视频通话)。

UDP Socket通信示例

服务端代码 (C语言)

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main() {
    // 1. 创建 UDP Socket
    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    // 2. 绑定 IP 和端口
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(8080);
    bind(sock, (struct sockaddr*)&addr, sizeof(addr));

    // 3. 接收数据(无连接,直接收)
    char buffer[1024];
    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);
    recvfrom(sock, buffer, 1024, 0, (struct sockaddr*)&client_addr, &addr_len);

    // 4. 发送数据(无连接,直接发)
    char msg[] = "Hello UDP Client!";
    sendto(sock, msg, sizeof(msg), 0, (struct sockaddr*)&client_addr, addr_len);

    // 5. 关闭 Socket
    close(sock);
    return 0;
}

客户端代码

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
    // 1. 创建 UDP Socket
    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    // 2. 发送数据(无需 connect)
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
    char msg[] = "Hello Server!";
    sendto(sock, msg, sizeof(msg), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));

    // 3. 接收数据
    char buffer[1024] = {0};
    recvfrom(sock, buffer, 1024, 0, NULL, NULL);
    printf("Server says: %s\n", buffer);

    // 4. 关闭 Socket
    close(sock);
    return 0;
}

UDP Socket 特点小结

  • 优点:无连接延迟低、头部开销极小、支持广播/多播。
  • 缺点:不保证可靠到达、不保证顺序、无流量控制。

总结

TCP和UDP是传输层的两大支柱,它们与Socket编程接口的结合,构成了现代网络应用的基石。理解它们的核心区别——TCP的可靠连接与UDP的高效无连接——是进行网络/系统编程和后端服务设计的关键。在实际的C/C++网络编程中,选择SOCK_STREAM还是SOCK_DGRAM,就决定了你使用的是哪一套通信模型。

希望通过以上的原理图解和代码示例,能帮助你更清晰地掌握TCP与UDP的Socket通信机制。网络编程的世界深邃而有趣,欢迎在云栈社区继续交流探讨。

手势图标




上一篇:C语言指针与数组深度解析:地址本质、内存布局与实战应用
下一篇:深入理解动态链接:从地址无关代码到延迟绑定PLT机制
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-8 11:59 , Processed in 0.771984 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表