一、什么是网络编程?

网络编程的核心是 “跨设备数据交互” —— 让运行在不同计算机(或同一计算机不同进程)上的程序,通过网络(局域网 / 互联网)实现数据的发送、接收、处理,本质是解决 “分布式系统中进程间通信(IPC)” 的问题。

⽹络编程:指⽹络上的主机,通过不同的进程,以编程的⽅式实现⽹络通信(或称为⽹络数据传输)

举个生活例子:你用微信发消息给朋友,微信客户端(你的手机)就是 “数据发送方”,朋友的微信客户端(对方手机)是 “数据接收方”,中间的移动网络 / WiFi 是 “传输通道”,而微信后台服务器则负责转发、验证等协调工作 —— 这整个过程的底层实现,就是网络编程的范畴。

网络编程的核心目标:

  1. 建立跨设备的通信连接;
  2. 可靠 / 高效地传输数据(文字、文件、视频等);
  3. 处理通信中的异常(网络中断、数据丢失、超时等)。

只要满⾜进程不同就⾏;所以即便是同⼀个主机,只要是不同进程,基于⽹络来传输数 据,也属于⽹络编程。 特殊的,对于开发来说,在条件有限的情况下,⼀般也都是在⼀个主机中运⾏多个进程来完成⽹络编程。

二、网络编程中的基本概念

要理解网络编程,必须先掌握以下核心概念,它们是所有通信模型的基础:

1. 网络协议(Protocol)

定义:计算机之间通信的 “规则约定”,规定了数据的格式、传输方式、校验机制等(类似人与人交流时的 “语言规则”)。核心作用:解决不同设备、不同操作系统之间的 “兼容性问题” —— 只要双方遵守同一协议,就能正确解析对方发送的数据。常见协议

  • 底层:TCP(传输控制协议)、UDP(用户数据报协议)、IP(网际协议);
  • 应用层:HTTP(网页通信)、FTP(文件传输)、SMTP(邮件发送)等。

2. IP 地址(Internet Protocol Address)

定义:网络中设备的 “唯一标识”,类似现实中的 “家庭住址” —— 确保数据能准确发送到目标设备。分类

  • IPv4:32 位二进制数,格式为x.x.x.x(如192.168.1.1),是目前主流;
  • IPv6:128 位二进制数,格式为8组十六进制数(如2001:0db8:85a3:0000:0000:8a2e:0370:7334),解决 IPv4 地址耗尽问题。特殊 IP
  • 127.0.0.1(或localhost):本地回环地址,用于程序在本机内自我通信(测试常用);
  • 局域网 IP:如192.168.x.x10.x.x.x,仅在局域网内可访问。

3. 端口号(Port)

定义设备内 进程的唯一标识”,类似家庭住址中的 “房间号” —— 一台设备可能同时运行多个网络程序(微信、浏览器、QQ),端口号用于区分数据该交给哪个程序处理范围:0~65535(16 位无符号整数),分为三类:

  • 知名端口(0~1023):系统占用,如 HTTP 的 80 端口、HTTPS 的 443 端口、FTP 的 21 端口;
  • 注册端口(1024~49151):应用程序注册使用,如 MySQL 的 3306 端口;
  • 动态端口(49152~65535):临时分配给客户端程序,通信结束后释放。核心原则:同一设备上,同一端口同一时间只能被一个进程占用(否则会报 “端口冲突” 错误)。

4. Socket(套接字)

定义:网络编程的 “通信端点”,是操作系统提供的 “网络通信接口”(API),本质是一个 “抽象文件描述符” —— 程序通过 Socket 发送 / 接收数据,无需关心底层网络协议的实现细节。组成:Socket = IP 地址 + 端口号 + 传输协议(TCP/UDP),例如 (192.168.1.100, 8080, TCP) 唯一标识一个通信端点。作用:屏蔽底层 TCP/IP 协议的复杂性,给程序员提供简单的 “读 / 写” 接口(类似操作文件的 “打开 - 读写 - 关闭” 流程)。

5. TCP 与 UDP 的核心区别(请看JavaEE专栏→计算机网络技术

TCP 和 UDP 是网络编程中最基础的两种传输协议,所有应用层协议(HTTP、FTP 等)都基于二者实现

三、常见的客户端 - 服务端(C/S)模型

网络通信的核心架构是 客户端 - 服务端(Client/Server,C/S)模型 —— 由 “服务端” 提供资源 / 服务,“客户端” 请求并使用资源,是分布式系统的基础架构。

1. 模型核心角色

  • 服务端(Server)
    • 被动等待连接(监听端口);
    • 可同时处理多个客户端请求;
    • 提供稳定的服务(如数据存储、业务逻辑处理);
    • 通常运行在固定 IP 和端口的服务器上(如阿里云服务器)。
  • 客户端(Client)
    • 主动发起连接(指定服务端 IP 和端口);
    • 向服务端发送请求(如 “登录”“查询数据”);
    • 接收服务端响应并展示结果;
    • 运行在用户设备上(手机、电脑)。

2. 模型工作流程(通用)

  1. 服务端启动:绑定 IP 和端口,开始监听客户端连接;
  2. 客户端启动:指定服务端 IP 和端口,发起连接请求;
  3. 建立通信:服务端接受连接,双方建立 Socket 通信通道;
  4. 数据交互:客户端发送请求数据,服务端处理后返回响应数据;
  5. 关闭连接:通信结束后,双方关闭 Socket,释放资源。

3. 扩展模型:B/S 与 P2P

  • B/S(Browser/Server):C/S 的特例,客户端是浏览器(Chrome、Edge),服务端是 Web 服务器(Tomcat、Nginx),通信协议为 HTTP/HTTPS(如访问网页、使用在线工具);
  • P2P(Peer-to-Peer):无中心服务端,每个节点既是客户端也是服务端(如迅雷下载、比特币网络),但本质仍基于 C/S 的 “点对点通信” 逻辑。

四、Socket 套接字详解

Socket 是网络编程的 “核心工具”,不管是 TCP 还是 UDP 通信,都离不开 Socket API。下面从 “本质、分类、工作流程” 三个维度详解:

1. Socket 的本质

Socket 是操作系统内核中的一个 “数据结构”(包含协议、IP、端口、缓冲区等信息),同时提供给应用程序的 “一组 API 函数”(如创建 Socket、绑定端口、发送数据等)。

你可以把 Socket 理解为 “网络版的文件句柄”:

  • 操作文件:打开文件(open)→ 读写数据(read/write)→ 关闭文件(close)
  • 操作 Socket:创建Socket(socket)→ 绑定/连接(bind/connect)→ 读写数据(send/recv)→ 关闭Socket(close)

2. Socket 的分类

根据传输协议,Socket 主要分为两类:

  • 流式 Socket(SOCK_STREAM):基于 TCP 协议,提供可靠、有序、面向连接的字节流传输(类似 “打电话”,必须先接通,说话有顺序,不会漏听);
  • 数据报 Socket(SOCK_DGRAM):基于 UDP 协议,提供不可靠、无连接、无序的数据报传输(类似 “发短信”,无需接通,可能丢失或乱序)。

3. Socket 的通用工作流程

(1)TCP Socket(流式)工作流程
  • 服务端:

    1. 创建 Socket(socket()):创建一个 TCP 类型的 Socket;
    2. 绑定端口(bind()):将 Socket 与 “IP + 端口” 绑定(告诉系统 “监听这个端口的请求”);
    3. 开始监听(listen()):进入 “等待连接” 状态,指定最大等待队列长度;
    4. 接受连接(accept()):阻塞等待客户端连接,连接成功后返回一个 “新的 Socket”(用于与该客户端通信,原 Socket 继续监听其他客户端);
    5. 读写数据(read()/write() 或 recv()/send()):通过新 Socket 与客户端交互;
    6. 关闭 Socket(close()):通信结束后关闭连接。
  • 客户端:

    1. 创建 Socket(socket()):创建 TCP 类型的 Socket;
    2. 连接服务端(connect()):指定服务端 IP 和端口,发起连接请求;
    3. 读写数据(read()/write()):与服务端交互;
    4. 关闭 Socket(close()):通信结束。
(2)UDP Socket(数据报)工作流程
  • 服务端:

    1. 创建 Socket(socket()):创建 UDP 类型的 Socket;
    2. 绑定端口(bind()):绑定 “IP + 端口”(必须绑定,否则客户端无法找到服务端);
    3. 接收数据(recvfrom()):阻塞等待客户端发送数据,同时获取客户端的 “IP + 端口”;
    4. 发送数据(sendto()):根据客户端的 “IP + 端口”,返回响应数据;
    5. 关闭 Socket(close())。
  • 客户端:

    1. 创建 Socket(socket()):创建 UDP 类型的 Socket(无需绑定端口,系统自动分配动态端口);
    2. 发送数据(sendto()):指定服务端 IP 和端口,发送数据报;
    3. 接收数据(recvfrom()):接收服务端响应;
    4. 关闭 Socket(close())。

关键区别:UDP 客户端无需绑定端口,服务端必须绑定;TCP 需要listen()accept()建立连接,UDP 直接发送数据。

服务端绑定端口是 “亮明身份”,客户端用 IP + 端口是 “精准找人”,连接建立后就是 “专属双向通道”,双方凭已知的 IP + 端口互发数据

1. 服务端 “绑定端口”= 店铺 “挂招牌 + 固定门牌号”

  • 服务端不用绑定具体客户端(因为不知道谁会来),只需要绑定 固定端口(比如 8888)—— 这就像店铺挂个招牌 “微信用户信息服务”,再固定门牌号 “8888”,告诉所有人:“要找这个服务,就来这个门牌号”。
  • 补充:服务端的 IP 其实是 “店铺地址”(比如公网 IP = 北京市朝阳区 XX 路),端口是 “门牌号”。服务端启动时,ServerSocket(port) 本质是 “把店铺地址 + 门牌号一起备案给系统”,系统收到发往这个 IP + 端口的数据包,就会转发给这个服务端程序。

2. 客户端 “指定 IP + 端口”= 顾客 “按地址 + 门牌号找店铺”

  • 客户端知道自己要找哪个服务(比如微信的用户查询功能),所以必须明确服务端的 公网 IP(地址)+ 固定端口(门牌号) —— 就像顾客知道要去 “北京市朝阳区 XX 路(IP)的 8888 号门(端口)” 找那家微信服务店铺。
  • 客户端不用自己绑定固定端口(系统会自动分配一个临时端口,比如 54321),因为它是 “主动找人” 的一方,服务端找到它只需要 “顾客的临时联系方式(客户端 IP + 临时端口)”,不用顾客自己 “固定联系方式”。

3. 建立连接 = 顾客进门,店铺开辟 “专属包间”

  • 客户端发起连接请求(拿着 IP + 端口找服务端),服务端通过 accept() 接收后,会 创建一个新的 Socket(专属通道) —— 这就像店铺大门(ServerSocket)接收顾客后,不给顾客在大厅待着,而是开辟一个 “专属包间”(新的clientSocket),专门对接这个顾客。
  • 此时:
    • 服务端的 “专属通道” 知道客户端的信息:客户端的 IP(比如顾客的手机尾号 / 住址)+ 临时端口(比如顾客的临时座位号);
    • 客户端的 Socket 也知道服务端的信息:公网 IP(店铺地址)+ 固定端口(8888 门牌号);
    • 原来的 ServerSocket 还在大门处继续接其他顾客(新客户端),互不干扰。

4. 通道用于返回数据 = 包间里 “双向对话”

  • 这个 “专属通道” 是双向的:
    • 客户端发请求(比如 “查询用户 1001”):相当于顾客在包间里喊需求;
    • 服务端返回响应(比如 “用户 1001 信息是 XXX”):相当于服务员在包间里回应;
    • 甚至服务端主动推送(比如 “你的账号有新消息”):相当于服务员主动进包间提醒。
  • 之所以能双向通信,核心就是 连接建立后,双方都掌握了对方的 “通信坐标”(IP + 端口) —— 服务端知道 “要把响应发给哪个 IP + 临时端口”,客户端知道 “要等哪个 IP + 固定端口的回复”,这条通道就是基于这个坐标建立的 “直达链路”。

最终验证:

  • 服务端绑定端口:new ServerSocket(8888) → 门牌号 8888,等待顾客;
  • 客户端找服务端:new Socket("公网IP", 8888) → 按地址 + 门牌号进门;
  • 建立通道:服务端accept()返回clientSocket → 专属包间创建;
  • 互发数据:双方通过clientSocket的流读写 → 包间里对话;
  • 关闭通道:双方close() → 顾客离店,包间释放。

端口是服务端的 “唯一标识”,IP + 端口是客户端找服务端的 “精准坐标”,连接建立后的双向通道,本质是双方互知对方坐标后的直达通信

问题:UDP 为啥不建立连接,还能发数据?—— 靠 “每次发数据都带目标地址”

UDP 的核心是「无连接」,但 “无连接” 不代表 “不知道发给谁”,而是 “不提前建立专属通道”。它的逻辑是:每次发送数据时,都手动指定目标服务端的 IP + 端口;服务端接收数据时,能拿到客户端的 IP + 临时端口,再针对性回复

用 “发短信” 比喻:

  • 你(UDP 客户端)给朋友(UDP 服务端)发短信,不用先 “打通电话(建立连接)”,只要在短信里写清楚 “收件人手机号(服务端 IP + 端口)”,直接发送就行;
  • 朋友(UDP 服务端)收到短信后,短信里会显示你的手机号(客户端 IP + 临时端口),他想回复的话,直接把短信发给这个手机号就行,也不用提前建立连接。
UDP 的关键流程(对应代码):
  1. 服务端必须绑定固定端口:因为客户端要 “精准发短信”,必须知道服务端的固定端口(比如 8888),所以 UDP 服务端必须new DatagramSocket(8888)绑定端口 —— 否则客户端不知道往哪个端口发。

  2. 客户端发送数据:每次都带目标 IP + 端口:客户端不用绑定端口(系统分配临时端口),但每次发数据时,都要在DatagramPacket里明确指定服务端的 IP 和端口:

    3.服务端接收数据:拿到客户端的 IP + 端口:服务端通过recvfrom()接收数据时,会从DatagramPacket里解析出客户端的 IP 和临时端口:

   4.服务端回复数据:指定客户端的 IP + 端口:服务端回复时,再创建一个新的DatagramPacket,把客户端的 IP + 端口作为目标地址:

TCP 和 UDP 的 “通信地址管理” 核心区别

特性 TCP(有连接) UDP(无连接)
地址指定时机 连接时指定一次(connect (IP + 端口)) 每次发数据都要指定(sendto (IP + 端口))
通信通道 连接建立后,专属双向通道(新 Socket) 无专属通道,每次数据都是 “独立短信”
地址存储 通道里缓存双方 IP + 端口,后续不用再指定 不缓存,每次都要手动传目标地址
可靠性 通道保证数据有序、不丢失 无通道保障,数据可能丢
Logo

葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务

更多推荐