
🌸你好呀!我是断弦承露
🌟感谢陪伴~ 小白博主在线求友
🌿 跟着小白学/Java/软件设计/鸿蒙开发/芯片开发
📖专栏汇总:
《软件设计师》专栏 | 《Java》专栏 | 《 RISC-V 处理器实战》专栏 | 《Flutter鸿蒙实战》专栏 | 《React Native开发》专栏 ------|CSDN|------

文章目录
- [🔥 零基础Java网络编程全解:从核心概念到Socket实战,一文打通Java网络通信](#🔥 零基础Java网络编程全解:从核心概念到Socket实战,一文打通Java网络通信)
-
- [📝 文章摘要 📝](#📝 文章摘要 📝)
- [📊 本文核心内容思维导图 📊](#📊 本文核心内容思维导图 📊)
- [🔄 TCP通信核心工作流程图 🔄](#🔄 TCP通信核心工作流程图 🔄)
- [📡 一、网络通信核心基础概念 📡](#📡 一、网络通信核心基础概念 📡)
-
- [1.1 什么是网络通信 💬](#1.1 什么是网络通信 💬)
- [1.2 网络的定义与分类 🌍](#1.2 网络的定义与分类 🌍)
- [🧭 二、网络通信核心标识 🧭](#🧭 二、网络通信核心标识 🧭)
-
- [2.1 IP地址详解 📍](#2.1 IP地址详解 📍)
-
- [2.1.1 IP地址核心基础 📋](#2.1.1 IP地址核心基础 📋)
- [2.1.2 IPv4地址的组成与分类 📊](#2.1.2 IPv4地址的组成与分类 📊)
- [2.1.3 IPv6协议 🚀](#2.1.3 IPv6协议 🚀)
- [2.2 域名与DNS解析 🔗](#2.2 域名与DNS解析 🔗)
-
- [2.2.1 域名的核心作用 🏷️](#2.2.1 域名的核心作用 🏷️)
- [2.2.2 DNS解析 📖](#2.2.2 DNS解析 📖)
- [2.3 端口号详解 🚪](#2.3 端口号详解 🚪)
-
- [2.3.1 端口号的核心作用 🎯](#2.3.1 端口号的核心作用 🎯)
- [2.3.2 端口号核心规则 📏](#2.3.2 端口号核心规则 📏)
- [2.3.3 常见网络程序默认端口号 📌](#2.3.3 常见网络程序默认端口号 📌)
- [📚 三、网络通信协议核心体系 📚](#📚 三、网络通信协议核心体系 📚)
-
- [3.1 网络协议的定义 📜](#3.1 网络协议的定义 📜)
- [3.2 OSI七层模型与TCP/IP模型 🏗️](#3.2 OSI七层模型与TCP/IP模型 🏗️)
- [3.3 TCP协议详解 🛡️](#3.3 TCP协议详解 🛡️)
-
- [3.3.1 TCP协议核心特性 ✨](#3.3.1 TCP协议核心特性 ✨)
- [3.3.2 TCP三次握手(连接建立)🤝](#3.3.2 TCP三次握手(连接建立)🤝)
- [3.3.3 TCP四次挥手(连接释放)👋](#3.3.3 TCP四次挥手(连接释放)👋)
- [3.4 UDP协议详解 ⚡](#3.4 UDP协议详解 ⚡)
-
- [3.4.1 UDP协议核心特性 ✨](#3.4.1 UDP协议核心特性 ✨)
- [3.4.2 TCP与UDP核心对比 ⚖️](#3.4.2 TCP与UDP核心对比 ⚖️)
- [💻 四、Java网络编程核心API 💻](#💻 四、Java网络编程核心API 💻)
-
- [4.1 java.net包核心类总览 📋](#4.1 java.net包核心类总览 📋)
- [4.2 InetAddress类详解 📍](#4.2 InetAddress类详解 📍)
-
- [核心静态方法与实例方法 📝](#核心静态方法与实例方法 📝)
- [4.3 TCP通信核心类 🛡️](#4.3 TCP通信核心类 🛡️)
-
- [核心方法说明 📝](#核心方法说明 📝)
- [4.4 UDP通信核心类 ⚡](#4.4 UDP通信核心类 ⚡)
-
- [核心方法说明 📝](#核心方法说明 📝)
- [✅ 五、Java网络编程实战代码 ✅](#✅ 五、Java网络编程实战代码 ✅)
-
- [5.1 实战一:TCP双向通信实战(支持多客户端)🤝](#5.1 实战一:TCP双向通信实战(支持多客户端)🤝)
-
- [5.1.1 TCP服务端代码](#5.1.1 TCP服务端代码)
- [5.1.2 TCP客户端代码](#5.1.2 TCP客户端代码)
- [运行步骤 📝](#运行步骤 📝)
- [5.2 实战二:UDP消息收发实战(支持广播)📢](#5.2 实战二:UDP消息收发实战(支持广播)📢)
-
- [5.2.1 UDP接收端代码](#5.2.1 UDP接收端代码)
- [5.2.2 UDP发送端代码](#5.2.2 UDP发送端代码)
- [运行步骤 📝](#运行步骤 📝)
- [5.3 实战三:本机IP信息查询工具类 🔍](#5.3 实战三:本机IP信息查询工具类 🔍)
- [🚨 新手高频报错与根治方案 🚨](#🚨 新手高频报错与根治方案 🚨)
- [❓ 新手必看FAQ问答体系 ❓](#❓ 新手必看FAQ问答体系 ❓)
- [📚 权威参考资料 📚](#📚 权威参考资料 📚)
- [🏷️ 文章标签 🏷️](#🏷️ 文章标签 🏷️)
🔥 零基础Java网络编程全解:从核心概念到Socket实战,一文打通Java网络通信

📝 文章摘要 📝
本文针对Java零基础开发者,系统拆解了网络编程的核心基础概念,从网络通信的底层逻辑出发,完整讲解了IP地址、域名、端口号三大核心标识,深度解析了TCP/IP协议体系、OSI七层模型与TCP/UDP协议的核心特性。文中修正了入门资料中常见的原理性错误,补充了Java网络编程核心API的使用方法,提供了3套可直接运行的企业级实战代码,同时配套新手高频报错解决方案、完整FAQ问答体系与面试考点梳理。全文兼顾入门友好性与企业开发实用性,帮助开发者从0到1掌握Java网络编程,🚀 打通Java后端开发的网络通信核心能力。
📊 本文核心内容思维导图 📊
Java网络编程全解
网络通信基础概念
网络通信定义
网络的分类与边界
网络通信核心标识
IP地址详解
域名与DNS解析
端口号核心规则
网络通信协议体系
OSI七层与TCPIP模型
TCP协议核心特性
UDP协议核心特性
TCP与UDP核心对比
Java网络编程核心API
java.net包总览
InetAddress类
TCP Socket核心类
UDP Datagram核心类
Java网络编程实战代码
TCP双向通信实战
UDP消息收发实战
本机IP信息工具类
新手问题专区
高频报错根治方案
FAQ问答体系
面试考点与进阶指南
🔄 TCP通信核心工作流程图 🔄
TCP三次握手
否
是
服务端启动
ServerSocket绑定端口
监听客户端连接
客户端启动
Socket连接服务端IP+端口
建立Socket连接
客户端发送数据
服务端接收数据
服务端返回响应数据
客户端接收响应
通信是否结束
TCP四次挥手释放连接
进入TIME_WAIT状态2MSL
关闭Socket资源
📡 一、网络通信核心基础概念 📡
1.1 什么是网络通信 💬
网络通信的本质,是两台或多台设备之间,通过网络实现数据的传输与交换 。
我们日常的刷网页🌐、发微信💬、传文件📁、调用后端接口🔗,底层都是网络通信的过程。简单来说,网络通信要完成的核心事情只有两件:
- 找到网络上的目标设备(通过IP地址定位📍)
- 把数据准确、可靠地传输给目标设备上的对应程序(通过端口号定位程序,通过协议保证传输📜)
Java语言天生具备极强的网络编程能力💪,JDK内置的java.net包提供了完整的类与接口,封装了网络通信的底层细节,让开发者无需关注网络传输的底层实现,即可快速完成网络程序开发。
1.2 网络的定义与分类 🌍
网络的核心定义:两台或多台设备,通过物理传输介质(网线、WiFi、光纤)与网络设备(路由器、交换机)连接起来,就构成了网络。
根据网络的覆盖范围,行业通用的分类标准如下📋:
| 网络类型 | 覆盖范围 | 典型场景 | 核心特点 |
|---|---|---|---|
| 局域网(LAN)🏠 | 最小,几米到几公里 | 一个教室、机房、公司办公室、家庭网络 | 传输速度快⚡、延迟低、网络环境稳定、封闭性强 |
| 城域网(MAN)🏙️ | 中等,覆盖一个城市 | 城市内的政务网络、运营商城市骨干网 | 覆盖范围介于局域网和广域网之间,适配城市级数据传输 |
| 广域网(WAN)🌐 | 最大,可覆盖全国甚至全球 | 互联网、跨城市的企业专线网络 | 覆盖范围广、传输节点多、延迟相对较高,万维网(WWW)是广域网最典型的代表 |
🧭 二、网络通信核心标识 🧭
想要完成网络通信,必须有三个核心标识🔑,分别解决「设备在哪」「设备叫什么」「数据发给哪个程序」三个核心问题。
2.1 IP地址详解 📍
IP地址(Internet Protocol Address),是网络中设备的唯一身份标识,作用相当于现实中的家庭地址🏠,网络中的数据包通过IP地址,找到对应的目标设备。
2.1.1 IP地址核心基础 📋
- 查看本机IP地址命令
- Windows系统💻:CMD终端执行
ipconfig - Linux/Mac系统🖥️:终端执行
ip addr(ifconfig为旧版命令,部分新发行版已废弃)
- Windows系统💻:CMD终端执行
- IP地址的表示形式 :主流的IPv4采用点分十进制 格式,写作
xx.xx.xx.xx,比如我们最常用的本机地址192.168.1.69 - 数值范围 :点分十进制的每一段,都是一个8位二进制数,取值范围是 0~255 ,因此IPv4地址的总长度是32位,理论上最多有2^32个地址。

2.1.2 IPv4地址的组成与分类 📊
IPv4地址由两部分组成:网络地址 + 主机地址
- 网络地址:标识设备所在的网段,同一个局域网内的设备,网络地址相同
- 主机地址:标识网段内的具体设备,同一个网段内主机地址唯一
行业标准的IPv4地址分为5大类📚,核心分类规则与范围如下:
| 地址类型 | 二进制前缀 | 网络号长度 | 主机号长度 | 地址范围 | 核心用途 |
|---|---|---|---|---|---|
| A类 🔴 | 0 | 7位 | 24位 | 0.0.0.0 ~ 127.255.255.255 | 大型企业、国家级网络,其中0网段和127网段为保留网段,127网段为回环地址段 |
| B类 🟡 | 10 | 14位 | 16位 | 128.0.0.0 ~ 191.255.255.255 | 中型企业、校园网络 |
| C类 🟢 | 110 | 21位 | 8位 | 192.0.0.0 ~ 223.255.255.255 | 小型企业、家庭局域网,我们日常的路由器网段大多属于C类 |
| D类 🔵 | 1110 | 28位多播组号 | 无主机号 | 224.0.0.0 ~ 239.255.255.255 | 多播/组播场景,比如直播、视频会议 |
| E类 ⚫ | 11110 | 27位保留位 | 无主机号 | 240.0.0.0 ~ 255.255.255.255 | 保留地址,用于科研实验,不对外开放使用,其中255.255.255.255为受限广播地址 |
💡 关键修正说明:
- 特殊回环地址:
127.0.0.1是本机回环地址,永远指向当前设备自身,用于本机程序的网络通信测试🧪,不会经过物理网卡。- 私有地址段🔒:A类
10.0.0.0~10.255.255.255、B类172.16.0.0~172.31.255.255、C类192.168.0.0~192.168.255.255为局域网私有地址,仅能在局域网内使用,无法直接访问互联网。
2.1.3 IPv6协议 🚀
IPv6是互联网工程任务组(IETF)设计的下一代IP协议,用于替代IPv4。
- 核心解决的问题:IPv4的32位地址最大仅能提供约43亿个地址,随着物联网📱设备的爆发,地址资源已经完全枯竭。
- 核心优势:IPv6采用128位地址长度,地址空间约为3.4×10^38个,不仅解决了地址资源不足的问题,还优化了多设备接入、网络安全🔐、传输效率等核心能力。
2.2 域名与DNS解析 🔗
2.2.1 域名的核心作用 🏷️
IP地址是一串数字,记忆难度极高😵,比如百度的服务器IP是39.156.66.10,我们很难记住这串数字,但www.baidu.com这个域名,我们可以轻松记住。
域名的核心价值,就是解决IP地址难以记忆的问题,给难记的IP地址,起一个好记的别名🏷️。
2.2.2 DNS解析 📖
我们在浏览器输入域名后,无法直接通过域名找到服务器,必须先把域名转换成对应的IP地址,这个转换的过程,就是DNS解析 。
DNS(Domain Name System,域名系统),就像互联网的电话簿📞,它维护了域名与IP地址的映射关系,我们输入域名后,系统会先向DNS服务器发送请求,获取域名对应的IP地址,再通过IP地址访问目标服务器。
2.3 端口号详解 🚪
2.3.1 端口号的核心作用 🎯
我们通过IP地址找到了目标设备,但一台设备上会同时运行很多网络程序(比如微信💬、浏览器🌐、QQ、后端服务🔧),设备收到数据包后,该把数据交给哪个程序?
端口号就是解决这个问题的,它的核心定义是:用于标识设备上运行的特定网络程序,每个网络程序绑定一个唯一的端口号,设备通过端口号,把数据包转发给对应的程序🎯。
2.3.2 端口号核心规则 📏
-
表示形式 :端口号是一个16位的无符号整数 ,因此取值范围是 0 ~ 65535(2^16 - 1)
-
端口号分类 :行业通用分为三大类,有明确的使用规范📋
端口类型 端口范围 核心用途与使用规则 知名端口(系统保留端口)🔒 0 ~ 1023 预留给HTTP、FTP等系统级应用使用,普通用户程序禁止占用,比如HTTP:80、FTP:21、SSH:22 注册端口 📝 1024 ~ 49151 分配给企业级应用、用户开发的程序使用,比如Tomcat:8080、MySQL:3306、Redis:6379 动态/私有端口 ⚙️ 49152 ~ 65535 客户端程序临时使用的端口,程序关闭后会释放,可随意使用
2.3.3 常见网络程序默认端口号 📌
| 应用程序/协议 | 默认端口号 | 核心用途 |
|---|---|---|
| HTTP协议 | 80 | 网页访问,超文本传输协议 |
| HTTPS协议 🔐 | 443 | 加密的网页访问协议 |
| FTP协议 | 21 | 文件传输协议 |
| SSH协议 | 22 | 远程服务器登录 |
| SMTP协议 | 25 | 邮件发送协议 |
| Tomcat Web服务器 | 8080 | Java Web应用服务器 |
| MySQL数据库 🗄️ | 3306 | 关系型数据库通信 |
| Oracle数据库 🗄️ | 1521 | 关系型数据库通信 |
| SQL Server数据库 🗄️ | 1433 | 关系型数据库通信 |
| Redis数据库 🚀 | 6379 | 缓存数据库通信 |
📚 三、网络通信协议核心体系 📚
3.1 网络协议的定义 📜
网络协议,是网络通信的双方必须共同遵守的规则集合 ,就像两个人交流必须使用同一种语言🗣️,才能互相理解。
网络协议定义了数据的传输格式、传输顺序、错误处理、流量控制等一系列规则,只有通信双方都遵守相同的协议,才能完成数据的正常传输。
我们常说的TCP/IP,不是一个单一的协议,而是一个协议簇📦,它包含了IP、TCP、UDP、HTTP、FTP等上百个协议,是互联网的核心基础协议。
3.2 OSI七层模型与TCP/IP模型 🏗️
为了让网络通信的各个环节标准化,国际标准化组织制定了OSI七层参考模型 ,而互联网实际落地使用的,是简化后的TCP/IP四层模型,也有业界常用的五层模型拆分方式。
三者的对应关系与每层核心协议如下📋:
| OSI七层模型 | TCP/IP四层模型 | TCP/IP五层模型 | 每层核心协议 | 核心作用 |
|---|---|---|---|---|
| 应用层 🎛️ | 应用层 | 应用层 | HTTP、HTTPS、FTP、SMTP、DNS | 直接面向用户的应用程序,提供网络服务接口 |
| 表示层 🎨 | 应用层 | 应用层 | 数据加密、解密、格式转换 | 处理数据的格式,确保通信双方能识别数据 |
| 会话层 💬 | 应用层 | 应用层 | 会话建立、维护、断开 | 管理应用程序之间的通信会话 |
| 传输层 🚚 | 传输层 | 传输层 | TCP、UDP | 负责端到端的通信,确保数据传输到目标程序 |
| 网络层 🗺️ | 网络层 | 网络层 | IP、ICMP、ARP | 负责路由寻址,把数据包从源设备传输到目标设备 |
| 数据链路层 🔗 | 网络接口层 | 数据链路层 | 以太网、PPP | 负责相邻设备之间的数据帧传输,差错检测 |
| 物理层 ⚡ | 网络接口层 | 物理层 | 网线、光纤、WiFi | 负责物理介质上的比特流传输,电信号/光信号转换 |
💡 核心说明:Java网络编程的代码,全部工作在应用层,我们只需要调用JDK封装好的API,无需关注传输层及以下的底层实现,这也是Java网络编程的优势所在✅。
3.3 TCP协议详解 🛡️
TCP(Transmission Control Protocol),中文全称传输控制协议,是互联网中应用最广泛的传输层协议。
3.3.1 TCP协议核心特性 ✨
- 面向连接 :使用TCP协议传输数据前,必须先通过三次握手 建立稳定的连接,传输完成后,必须通过四次挥手释放连接📞,就像打电话必须先拨号接通,才能说话,说完必须挂电话。
- 可靠传输 :TCP通过校验和、序列号、累计确认应答、超时重传+快速重传机制、滑动窗口流量控制、慢开始+拥塞避免+快重传+快恢复拥塞控制,保证数据无丢失、无重复、按顺序到达,是可靠的传输协议✅。
- 面向字节流:TCP把数据看作一串无边界的字节流,不限制传输的数据大小,适合传输大量文件📁。
- 全双工通信:连接建立后,通信双方可以同时发送和接收数据,双向通信互不影响🔄。
3.3.2 TCP三次握手(连接建立)🤝
TCP建立连接的过程,称为三次握手,核心是确保双方的发送和接收能力都正常,并同步初始序列号(ISN):
- 第一次握手(SYN):客户端向服务端发送SYN报文,同步自己的初始序列号x,进入SYN_SENT状态。
- 第二次握手(SYN+ACK):服务端收到SYN后,回复SYN+ACK报文,同步自己的初始序列号y,并确认客户端的序列号x+1,进入SYN_RCVD状态。
- 第三次握手(ACK):客户端收到SYN+ACK后,回复ACK报文,确认服务端的序列号y+1,双方进入ESTABLISHED状态,连接正式建立✅。
3.3.3 TCP四次挥手(连接释放)👋
TCP断开连接的过程,称为四次挥手,核心是确保双方的数据都已经传输完成,安全释放连接,并进入TIME_WAIT状态:
- 第一次挥手(FIN):客户端向服务端发送FIN报文,请求断开连接,进入FIN_WAIT_1状态。
- 第二次挥手(ACK):服务端收到FIN后,回复ACK报文,确认断开请求,进入CLOSE_WAIT状态,客户端收到ACK后进入FIN_WAIT_2状态,服务端继续传输未发完的数据。
- 第三次挥手(FIN):服务端数据传输完成后,向客户端发送FIN报文,进入LAST_ACK状态。
- 第四次挥手(ACK):客户端收到FIN后,回复ACK报文,进入TIME_WAIT状态,等待2MSL(最大报文段生存时间,通常为2分钟)后,双方正式关闭连接🔚。
💡 关键补充:TIME_WAIT状态的作用
- 保证最后一个ACK报文能到达服务端,避免服务端因收不到ACK而重复发送FIN。
- 避免旧连接的延迟数据包在新连接中出现,干扰新连接的数据传输🔇。
3.4 UDP协议详解 ⚡
UDP(User Datagram Protocol),中文全称用户数据报协议,是与TCP并列的传输层协议。
3.4.1 UDP协议核心特性 ✨
- 无连接:使用UDP协议传输数据,不需要提前建立连接,直接把数据封装成数据包发送📨,不需要维护连接状态,就像发短信,编辑好内容直接发送,不需要提前接通。
- 不可靠传输:UDP没有确认应答、重传机制,数据包发送后,不保证对方一定能收到,也不保证数据按顺序到达,是不可靠的传输协议❌。
- 面向数据报:UDP把数据封装成一个个独立的数据报,单个数据报的总长度(包括UDP首部8字节+IP首部20字节)最大为65535字节,因此实际数据部分最大为65507字节,不适合传输大量数据。
- 传输速度极快:UDP没有TCP的连接建立、确认重传、流量控制等机制,额外开销极小,传输延迟极低⚡,速度远快于TCP。
- 支持单播、多播、广播:UDP不仅支持一对一单播,还支持一对多多播和一对全广播📢,适配直播、消息推送等场景。
3.4.2 TCP与UDP核心对比 ⚖️
| 特性 | TCP协议 🛡️ | UDP协议 ⚡ |
|---|---|---|
| 连接性 | 面向连接,需三次握手建立连接 | 无连接,无需提前建立连接 |
| 可靠性 | 可靠传输,保证数据无丢失、无重复、按序到达 | 不可靠传输,不保证数据到达,不保证顺序 |
| 传输形式 | 面向字节流,无数据大小限制 | 面向数据报,单个数据报最大65535字节(含首部) |
| 传输效率 | 效率低,额外开销大 | 效率极高,延迟极低,额外开销极小 |
| 拥塞/流量控制 | 有完整的流量控制、拥塞控制机制 | 无,无论网络状态如何,都按固定速率发送数据 |
| 双工性 | 全双工通信 | 支持全双工,也支持单播、多播、广播 |
| 适用场景 | 文件传输、网页访问、接口调用、邮件收发等对数据可靠性要求高的场景 | 直播、视频通话、游戏、广播消息、实时监控等对实时性要求高、允许少量丢包的场景 |
💻 四、Java网络编程核心API 💻
Java网络编程的核心API全部在java.net包中📦,该包封装了网络通信的底层细节,提供了面向对象的编程接口,是Java网络编程的核心。
4.1 java.net包核心类总览 📋
| 类名 | 核心用途 | 对应协议 |
|---|---|---|
InetAddress 📍 |
用于封装IP地址和域名,完成IP地址与域名的相关操作 | IP协议 |
Socket 📞 |
TCP客户端核心类,封装了TCP客户端的Socket连接与数据读写操作 | TCP协议 |
ServerSocket 🖥️ |
TCP服务端核心类,用于监听客户端的连接请求,创建与客户端的Socket连接 | TCP协议 |
DatagramSocket 📦 |
UDP通信核心类,用于发送和接收UDP数据报 | UDP协议 |
DatagramPacket 📨 |
UDP数据报封装类,封装了UDP数据包的内容、目标IP、端口等信息 | UDP协议 |
URL 🔗 |
统一资源定位符类,用于访问互联网上的资源,封装了HTTP协议的相关操作 | HTTP/HTTPS协议 |
MulticastSocket 📢 |
DatagramSocket的子类,用于UDP多播/组播场景 |
UDP协议 |
4.2 InetAddress类详解 📍
InetAddress类是Java对IP地址的封装,用于完成IP地址、域名的相关操作,无需通过构造方法创建对象,通过静态方法获取实例。
核心静态方法与实例方法 📝
| 方法名 | 方法作用 |
|---|---|
static InetAddress getLocalHost() |
获取本机的InetAddress实例,包含本机IP与主机名 |
static InetAddress getByName(String host) |
根据域名/主机名/IP地址字符串,获取对应的InetAddress实例 |
static InetAddress[] getAllByName(String host) |
根据域名获取所有对应的IP地址数组(支持域名解析到多个IP) |
String getHostName() |
获取实例对应的主机名/域名 |
String getHostAddress() |
获取实例对应的IP地址字符串 |
boolean isReachable(int timeout) |
测试该IP地址是否可以连通,timeout为超时时间,单位毫秒 |
4.3 TCP通信核心类 🛡️
Java TCP通信采用C/S(客户端-服务端)架构🏗️,核心是Socket和ServerSocket两个类:
- 服务端:通过
ServerSocket绑定端口,监听客户端的连接请求,接收到请求后,创建与客户端对应的Socket对象,完成数据读写。 - 客户端:通过
Socket连接服务端的IP+端口,连接建立后,通过Socket的输入输出流,完成与服务端的数据交互🔄。
核心方法说明 📝
-
ServerSocket类核心方法:方法名 方法作用 ServerSocket(int port)构造方法,绑定指定端口,创建服务端ServerSocket实例 ServerSocket(int port, int backlog)构造方法,绑定指定端口,并设置连接等待队列的长度 Socket accept()阻塞监听客户端的连接请求,接收到请求后,返回与客户端对应的Socket对象 void close()关闭ServerSocket,释放端口资源 void setSoTimeout(int timeout)设置accept()方法的阻塞超时时间,单位毫秒 -
Socket类核心方法:方法名 方法作用 Socket(String host, int port)构造方法,根据服务端的IP/域名和端口,创建客户端Socket实例,并尝试连接服务端 InputStream getInputStream()获取Socket的输入流,用于读取对方发送的数据 OutputStream getOutputStream()获取Socket的输出流,用于向对方发送数据 void close()关闭Socket连接,释放相关资源 boolean isConnected()判断Socket是否已经成功建立连接 void setSoTimeout(int timeout)设置读取数据的阻塞超时时间,单位毫秒 void setReuseAddress(boolean on)设置SO_REUSEADDR选项,允许端口复用,解决TIME_WAIT状态下的端口占用问题
4.4 UDP通信核心类 ⚡
Java UDP通信的核心是DatagramSocket和DatagramPacket两个类,UDP无需建立连接,通过数据报的形式完成数据收发📨:
- 发送端:将数据、目标IP、端口封装到
DatagramPacket数据包中,通过DatagramSocket发送数据包。 - 接收端:通过
DatagramSocket绑定端口,接收发送端的数据包,解析DatagramPacket中的数据内容。
核心方法说明 📝
-
DatagramSocket类核心方法:方法名 方法作用 DatagramSocket()构造方法,创建客户端DatagramSocket实例,系统自动分配端口 DatagramSocket(int port)构造方法,绑定指定端口,创建服务端/接收端DatagramSocket实例 void send(DatagramPacket p)发送UDP数据包 void receive(DatagramPacket p)阻塞接收UDP数据包,将接收到的数据封装到参数p中 void close()关闭DatagramSocket,释放端口资源 void setSoTimeout(int timeout)设置receive()方法的阻塞超时时间,单位毫秒 void setBroadcast(boolean on)设置是否允许发送广播数据包 -
DatagramPacket类核心方法:方法名 方法作用 DatagramPacket(byte[] buf, int length, InetAddress address, int port)构造方法,创建用于发送的数据包,包含数据缓冲区、数据长度、目标IP、目标端口 DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)构造方法,创建用于发送的数据包,支持从缓冲区的指定偏移量开始发送 DatagramPacket(byte[] buf, int length)构造方法,创建用于接收的数据包,包含数据缓冲区和最大接收长度 byte[] getData()获取数据包中的数据字节数组 int getLength()获取接收到/发送的数据实际长度 int getOffset()获取数据在缓冲区中的偏移量 InetAddress getAddress()获取数据包对应的目标/源IP地址 int getPort()获取数据包对应的目标/源端口号
✅ 五、Java网络编程实战代码 ✅
5.1 实战一:TCP双向通信实战(支持多客户端)🤝
实现功能:TCP服务端支持多客户端并发连接👥,每个客户端连接由独立线程处理,客户端向服务端发送消息,服务端收到后返回响应,实现双向通信。
5.1.1 TCP服务端代码
java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* TCP通信服务端(支持多客户端并发)
* 功能:监听客户端连接,为每个客户端开启独立线程处理通信
*/
public class TcpServer {
// 服务端监听端口,使用1024以上的注册端口,避免占用系统保留端口
// 命名逻辑:SERVER_PORT为常量,全大写下划线分隔,符合Java常量命名规范
private static final int SERVER_PORT = 8888;
// 连接等待队列长度
private static final int BACKLOG = 50;
public static void main(String[] args) {
// 1. 创建ServerSocket,绑定监听端口,设置SO_REUSEADDR允许端口复用
try (ServerSocket serverSocket = new ServerSocket(SERVER_PORT, BACKLOG)) {
// 设置SO_REUSEADDR,允许端口复用,解决服务端重启时的TIME_WAIT端口占用问题
serverSocket.setReuseAddress(true);
System.out.println("TCP服务端已启动,正在监听端口:" + SERVER_PORT);
System.out.println("等待客户端连接...");
// 2. 循环持续监听客户端连接
while (true) {
try {
// accept()方法会阻塞,直到有客户端连接,返回与客户端对应的Socket对象
Socket clientSocket = serverSocket.accept();
// 为每个客户端连接开启独立的线程处理通信,避免阻塞主线程
new Thread(new ClientHandler(clientSocket)).start();
} catch (IOException e) {
System.err.println("接受客户端连接异常:" + e.getMessage());
}
}
} catch (IOException e) {
// 捕获服务端启动、端口绑定异常
System.err.println("TCP服务端启动失败,端口" + SERVER_PORT + "可能被占用:" + e.getMessage());
}
}
/**
* 客户端通信处理线程类
* 功能:处理单个客户端的通信逻辑
*/
private static class ClientHandler implements Runnable {
// 与客户端的通信Socket
private final Socket clientSocket;
// 构造方法,传入客户端Socket
public ClientHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
// 获取客户端的IP地址
String clientIp = clientSocket.getInetAddress().getHostAddress();
System.out.println("客户端已连接,客户端地址:" + clientIp);
// try-with-resources自动关闭流资源
try (
// 获取Socket的输入流,包装为BufferedReader,用于按行读取客户端发送的消息
BufferedReader reader = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream(), StandardCharsets.UTF_8)
);
// 获取Socket的输出流,包装为PrintWriter,用于向客户端发送响应消息,自动刷新缓冲区
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(clientSocket.getOutputStream(), StandardCharsets.UTF_8),
true
)
) {
// 循环读取客户端消息
String clientMessage;
while ((clientMessage = reader.readLine()) != null) {
System.out.println("收到客户端" + clientIp + "的消息:" + clientMessage);
// 向客户端返回响应消息
String serverResponse = "服务端已收到你的消息:【" + clientMessage + "】,响应时间:" + System.currentTimeMillis();
writer.println(serverResponse);
System.out.println("已向客户端" + clientIp + "发送响应:" + serverResponse);
// 如果客户端发送exit,结束通信
if ("exit".equalsIgnoreCase(clientMessage.trim())) {
System.out.println("客户端" + clientIp + "请求断开连接");
break;
}
}
} catch (IOException e) {
// 捕获客户端通信过程中的异常
System.err.println("客户端" + clientIp + "通信异常:" + e.getMessage());
} finally {
// 关闭客户端Socket,释放资源
try {
clientSocket.close();
System.out.println("客户端" + clientIp + "连接已关闭");
} catch (IOException e) {
System.err.println("关闭客户端Socket异常:" + e.getMessage());
}
}
}
}
}
5.1.2 TCP客户端代码
java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
/**
* TCP通信客户端
* 功能:连接TCP服务端,发送消息并接收服务端响应
*/
public class TcpClient {
// 服务端的IP地址,这里使用本机回环地址,测试时服务端和客户端在同一台设备
// 命名逻辑:SERVER_HOST为服务端地址常量,全大写下划线分隔
private static final String SERVER_HOST = "127.0.0.1";
// 服务端的端口号,必须与服务端代码中的端口完全一致
private static final int SERVER_PORT = 8888;
// 连接超时时间,单位毫秒
private static final int CONNECT_TIMEOUT = 5000;
// 读取数据超时时间,单位毫秒
private static final int READ_TIMEOUT = 10000;
public static void main(String[] args) {
// try-with-resources语法自动关闭Socket、流资源
try (
// 创建Socket,设置连接超时时间
Socket socket = new Socket(SERVER_HOST, SERVER_PORT);
// 获取Socket输出流,包装为PrintWriter,向服务端发送消息
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8),
true
);
// 获取Socket输入流,包装为BufferedReader,读取服务端响应
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)
);
// 扫描器,读取控制台输入的内容
Scanner scanner = new Scanner(System.in)
) {
// 设置Socket的SO_REUSEADDR选项
socket.setReuseAddress(true);
// 设置读取数据的超时时间
socket.setSoTimeout(READ_TIMEOUT);
System.out.println("已成功连接到TCP服务端:" + SERVER_HOST + ":" + SERVER_PORT);
System.out.println("请输入要发送给服务端的消息(输入exit退出):");
// 循环发送消息
while (true) {
// 读取控制台输入的消息
String sendMessage = scanner.nextLine();
// 向服务端发送消息
writer.println(sendMessage);
// 如果输入exit,退出循环
if ("exit".equalsIgnoreCase(sendMessage.trim())) {
System.out.println("TCP客户端即将关闭");
break;
}
System.out.println("消息已发送,等待服务端响应...");
// 读取服务端返回的响应
String serverResponse = reader.readLine();
if (serverResponse != null) {
System.out.println("收到服务端响应:" + serverResponse);
} else {
System.out.println("服务端已断开连接");
break;
}
}
} catch (IOException e) {
// 捕获连接失败、通信异常
System.err.println("TCP客户端通信异常:" + e.getMessage());
}
}
}
运行步骤 📝
- 先运行
TcpServer类,启动服务端,控制台输出服务端启动成功✅ - 再运行
TcpClient类(可同时运行多个客户端👥),启动客户端,在控制台输入要发送的消息,按下回车 - 查看服务端和客户端的控制台输出,完成双向通信,输入exit可退出🔚

5.2 实战二:UDP消息收发实战(支持广播)📢
实现功能:UDP接收端绑定端口,持续监听消息;UDP发送端支持单播和广播,向接收端发送消息。
5.2.1 UDP接收端代码
java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
/**
* UDP通信接收端
* 功能:绑定端口,持续接收发送端的UDP数据包
*/
public class UdpReceiver {
// 接收端绑定的端口号
private static final int RECEIVER_PORT = 9999;
// 数据包接收缓冲区大小,单位字节,建议设置为65536(最大UDP数据报长度)
private static final int BUFFER_SIZE = 65536;
// 读取数据超时时间,单位毫秒
private static final int READ_TIMEOUT = 30000;
public static void main(String[] args) {
// try-with-resources自动关闭DatagramSocket资源
try (DatagramSocket datagramSocket = new DatagramSocket(RECEIVER_PORT)) {
// 设置SO_REUSEADDR,允许端口复用
datagramSocket.setReuseAddress(true);
// 设置读取数据的超时时间
datagramSocket.setSoTimeout(READ_TIMEOUT);
System.out.println("UDP接收端已启动,正在监听端口:" + RECEIVER_PORT);
System.out.println("等待接收消息...");
// 循环持续接收消息
while (true) {
// 1. 创建接收数据的缓冲区和数据包
// buffer:字节数组缓冲区,存储接收到的数据包内容
byte[] buffer = new byte[BUFFER_SIZE];
// datagramPacket:接收数据包,封装缓冲区和最大接收长度
DatagramPacket datagramPacket = new DatagramPacket(buffer, BUFFER_SIZE);
try {
// 2. 阻塞接收数据包,收到数据后会填充到datagramPacket中
datagramSocket.receive(datagramPacket);
// 3. 解析数据包中的内容
// 从缓冲区中提取实际接收到的数据,转换为UTF-8字符串
String receiveMessage = new String(
datagramPacket.getData(),
datagramPacket.getOffset(),
datagramPacket.getLength(),
StandardCharsets.UTF_8
);
// 获取发送端的IP地址和端口号
String senderIp = datagramPacket.getAddress().getHostAddress();
int senderPort = datagramPacket.getPort();
// 4. 打印接收到的消息
System.out.println("=====================================");
System.out.println("收到来自" + senderIp + ":" + senderPort + "的消息:");
System.out.println("消息内容:" + receiveMessage);
System.out.println("=====================================");
// 如果收到exit消息,退出循环,关闭接收端
if ("exit".equalsIgnoreCase(receiveMessage.trim())) {
System.out.println("收到退出指令,UDP接收端即将关闭");
break;
}
} catch (IOException e) {
System.err.println("接收数据包异常:" + e.getMessage());
}
}
} catch (SocketException e) {
System.err.println("UDP接收端启动失败,端口" + RECEIVER_PORT + "可能被占用:" + e.getMessage());
}
}
}
5.2.2 UDP发送端代码
java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
/**
* UDP通信发送端
* 功能:支持单播和广播,向UDP接收端发送数据包
*/
public class UdpSender {
// 接收端的IP地址,单播时使用具体IP,广播时使用255.255.255.255或网段广播地址
private static final String RECEIVER_HOST = "127.0.0.1";
// 接收端的端口号,必须与接收端代码中的端口完全一致
private static final int RECEIVER_PORT = 9999;
// 是否启用广播模式
private static final boolean ENABLE_BROADCAST = false;
public static void main(String[] args) {
// try-with-resources自动关闭DatagramSocket资源
try (DatagramSocket datagramSocket = new DatagramSocket();
Scanner scanner = new Scanner(System.in)) {
// 设置SO_REUSEADDR,允许端口复用
datagramSocket.setReuseAddress(true);
// 如果启用广播模式,设置允许发送广播数据包
if (ENABLE_BROADCAST) {
datagramSocket.setBroadcast(true);
}
System.out.println("UDP发送端已启动,目标地址:" + RECEIVER_HOST + ":" + RECEIVER_PORT);
System.out.println("广播模式:" + (ENABLE_BROADCAST ? "已启用" : "已关闭"));
System.out.println("请输入要发送的消息(输入exit退出):");
// 获取接收端的IP地址实例
InetAddress receiverAddress = InetAddress.getByName(RECEIVER_HOST);
// 循环发送消息
while (true) {
// 读取控制台输入的消息
String sendMessage = scanner.nextLine();
// 将消息转换为UTF-8编码的字节数组
byte[] messageBytes = sendMessage.getBytes(StandardCharsets.UTF_8);
// 创建UDP数据包,包含数据、数据长度、目标IP、目标端口
DatagramPacket datagramPacket = new DatagramPacket(
messageBytes,
messageBytes.length,
receiverAddress,
RECEIVER_PORT
);
// 发送数据包
datagramSocket.send(datagramPacket);
System.out.println("消息已发送成功✅!");
// 如果输入exit,退出循环
if ("exit".equalsIgnoreCase(sendMessage.trim())) {
System.out.println("UDP发送端即将关闭🔚");
break;
}
}
} catch (IOException e) {
System.err.println("UDP发送端异常:" + e.getMessage());
}
}
}
运行步骤 📝
- 先运行
UdpReceiver类,启动接收端,控制台输出接收端启动成功✅ - 再运行
UdpSender类,启动发送端,在控制台输入要发送的消息,按下回车 - 查看接收端控制台,会打印出发送端的消息,输入exit可退出🔚
- 如需测试广播📢,将
ENABLE_BROADCAST设为true,RECEIVER_HOST设为255.255.255.255或网段广播地址(如192.168.1.255)
5.3 实战三:本机IP信息查询工具类 🔍
java
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
/**
* 本机IP信息查询工具类
* 功能:获取本机主机名、回环地址、局域网IP地址等信息
*/
public class IpInfoUtils {
/**
* 获取本机的主机名
* @return 本机主机名
* @throws UnknownHostException 未知主机异常
*/
public static String getLocalHostName() throws UnknownHostException {
return InetAddress.getLocalHost().getHostName();
}
/**
* 获取本机的局域网IP地址
* @return 本机局域网IP地址字符串
* @throws UnknownHostException 未知主机异常
*/
public static String getLocalHostIp() throws UnknownHostException {
return InetAddress.getLocalHost().getHostAddress();
}
/**
* 获取本机回环地址
* @return 本机回环地址字符串
*/
public static String getLoopbackAddress() {
return InetAddress.getLoopbackAddress().getHostAddress();
}
/**
* 打印本机所有网卡的IP地址信息
* @throws SocketException 网卡操作异常
*/
public static void printAllNetworkInterface() throws SocketException {
// 获取本机所有的网卡接口
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
System.out.println("本机所有网卡IP信息📋:");
System.out.println("-------------------------------------");
// 遍历所有网卡
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
// 跳过虚拟网卡、回环网卡、未启用的网卡
if (networkInterface.isLoopback() || networkInterface.isVirtual() || !networkInterface.isUp()) {
continue;
}
System.out.println("网卡名称:" + networkInterface.getDisplayName());
System.out.println("网卡硬件地址(MAC):" + formatMacAddress(networkInterface.getHardwareAddress()));
// 获取网卡下的所有IP地址
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
System.out.println(" IP地址:" + inetAddress.getHostAddress());
System.out.println(" IP类型:" + (inetAddress.getAddress().length == 4 ? "IPv4 🟢" : "IPv6 🔵"));
}
System.out.println("-------------------------------------");
}
}
/**
* 格式化MAC地址
* @param macBytes MAC地址字节数组
* @return 格式化后的MAC地址字符串
*/
private static String formatMacAddress(byte[] macBytes) {
if (macBytes == null || macBytes.length == 0) {
return "未知❓";
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < macBytes.length; i++) {
sb.append(String.format("%02X", macBytes[i]));
if (i < macBytes.length - 1) {
sb.append(":");
}
}
return sb.toString();
}
// 测试方法
public static void main(String[] args) {
try {
System.out.println("本机主机名🏷️:" + getLocalHostName());
System.out.println("本机回环地址🔁:" + getLoopbackAddress());
System.out.println("本机局域网IP📍:" + getLocalHostIp());
System.out.println("=====================================");
printAllNetworkInterface();
} catch (Exception e) {
System.err.println("获取IP信息异常❌:" + e.getMessage());
}
}
}
🚨 新手高频报错与根治方案 🚨
| 报错现象 | 核心根因 | 根治解决方案 |
|---|---|---|
java.net.BindException: Address already in use: JVM_Bind |
端口被其他程序占用🔒,ServerSocket/DatagramSocket无法绑定端口 | 1. 更换未被占用的端口,重新启动程序 2. 通过命令查看占用端口的进程,关闭对应程序 Windows:`netstat -ano |
java.net.ConnectException: Connection refused: connect |
TCP客户端无法连接到服务端❌ | 1. 检查服务端是否已经正常启动,端口是否正确 2. 检查客户端代码中的服务端IP和端口是否与服务端完全一致 3. 检查服务端所在设备的防火墙是否拦截了端口,关闭防火墙或开放对应端口 4. 检查客户端与服务端是否在同一个网段,能否ping通 |
java.net.SocketException: Socket closed |
Socket已经被关闭🔚,还在尝试读写数据 | 1. 检查代码中是否提前关闭了Socket,再执行读写操作 2. 检查try-with-resources的作用域,确保读写操作在资源关闭前完成 3. 检查服务端/客户端是否提前断开了连接 |
java.net.SocketTimeoutException: Read timed out |
读取数据超时⏱️,对方长时间没有发送数据 | 1. 检查对方程序是否正常运行,是否已经发送了数据 2. 给Socket设置合理的超时时间,通过socket.setSoTimeout(超时时间)设置 3. 检查网络是否通畅,是否存在丢包、延迟过高的问题 |
| TCP通信中文乱码 🀄 | 发送端和接收端使用的字符集不一致,或使用了系统默认字符集 | 1. 发送端和接收端统一使用UTF-8字符集,不要使用系统默认字符集 2. 所有字节流与字符流的转换,都明确指定StandardCharsets.UTF_8 3. 避免使用String.getBytes()无参方法,必须指定字符集 |
| UDP接收端收不到消息 📨 | 1. 发送端的IP/端口填写错误 2. 接收端防火墙拦截了端口 3. 数据包超过65535字节大小限制 4. 接收端缓冲区小于数据包长度 | 1. 检查发送端的目标IP和端口是否与接收端完全一致 2. 关闭接收端防火墙,或开放对应UDP端口 3. 确保单个UDP数据包总长度(含首部)不超过65535字节,大数据拆分成多个包发送 4. 增大接收端的缓冲区大小,建议设置为65536 |
| 服务端只能处理一个客户端连接,处理完就退出 👥 | 服务端代码没有循环监听,只执行了一次accept()方法 | 1. 在服务端代码中,给accept()方法添加while(true)死循环,持续监听客户端连接 2. 每个客户端连接,开启独立的线程处理通信,避免阻塞主线程的监听 |
| TCP小数据包延迟发送,实时性差 ⏱️ | TCP的Nagle算法默认开启,会将小数据包合并后发送,导致延迟 | 1. 调用socket.setTcpNoDelay(true)关闭Nagle算法,禁用小数据包合并 2. 适用于对实时性要求高的场景,如即时通讯、游戏🎮 |
❓ 新手必看FAQ问答体系 ❓
Q1:Java网络编程中,TCP和UDP该怎么选?🤔
A1:核心选择标准看业务场景的核心需求:
- 如果你的业务对数据可靠性要求极高✅,不允许丢包、乱序,比如文件传输、支付接口调用、用户登录、邮件收发,必须选择TCP协议。
- 如果你的业务对实时性要求极高⚡,允许少量丢包,比如直播、视频通话、游戏、实时监控、广播消息,优先选择UDP协议。
- 绝大多数企业级Java后端开发,都是基于TCP协议的HTTP/HTTPS接口开发,TCP是Java网络编程的核心重点📚。
Q2:Socket是什么?为什么叫套接字?🔌
A2:Socket的中文翻译是套接字,它是操作系统提供的网络通信操作的抽象接口 ,是应用程序与TCP/IP协议栈之间的桥梁🌉。
简单来说,Socket封装了网络通信的底层细节,给应用程序提供了统一的编程接口,我们只需要调用Socket的API,操作系统底层会帮我们完成TCP连接建立、数据传输、断开连接等一系列操作,无需关注底层协议的实现。
Q3:为什么TCP建立连接是三次握手,两次不行吗?🤝
A3:TCP三次握手的核心目的,是确认通信双方的发送能力和接收能力都正常,并同步初始序列号,两次握手无法完成这个确认:
- 如果只有两次握手,客户端发送的连接请求如果在网络中延迟了,客户端会重发请求,服务端收到后建立连接。但延迟的请求后续到达服务端,服务端会再次建立连接,而客户端已经关闭了,服务端会一直维护这个无效连接,浪费服务器资源💻。
- 两次握手只能确认客户端的发送能力、服务端的接收能力正常,无法确认服务端的发送能力、客户端的接收能力正常,三次握手才能完成双向能力的确认,保证连接的可靠性✅。
Q4:Java网络编程中,为什么要使用try-with-resources语法关闭资源?♻️
A4:网络编程中的Socket、ServerSocket、流对象,都实现了AutoCloseable接口,属于系统资源,使用完必须关闭,否则会导致资源泄漏、端口占用等问题🔒。
- 传统的
try-finally写法,需要手动在finally中关闭资源,代码繁琐,且容易出现关闭顺序错误、空指针异常。 - try-with-resources语法,会在代码块执行完成后,自动按创建的逆序关闭所有资源,无需手动编写关闭代码,避免了资源泄漏,代码更简洁、更安全✅,是Java 7+推荐的资源关闭方式。
Q5:一个端口号可以同时被TCP和UDP两个程序占用吗?🔢
A5:可以✅。端口号的作用是标识设备上的程序,TCP和UDP是两个独立的传输层协议,各自维护自己的端口号表。
比如,一个程序用TCP绑定了8888端口,另一个程序同时用UDP绑定8888端口,是完全允许的,操作系统会根据传输层协议,把数据包转发给对应的程序。但同一个协议,同一个端口号,同时只能被一个程序占用❌。
Q6:Java网络编程中,TCP粘包拆包是什么?怎么解决?📦
A6:TCP是面向字节流的协议,它把数据看作一串无边界的字节流,没有数据包的概念,因此会出现粘包拆包问题:
- 粘包:发送端多次发送的小数据包,被TCP合并成一个大数据包发送,接收端一次收到了多个数据包的内容,无法区分。
- 拆包:发送端发送的一个大数据包,被TCP拆分成多个小包发送,接收端多次收到才能拼成完整的数据包。
核心解决方案:
- 固定消息长度:每个消息的长度固定,接收端按固定长度读取数据。
- 特殊分隔符:在每个消息的末尾添加特殊的分隔符(比如换行符、自定义符号),接收端通过分隔符区分消息边界。
- 消息头+消息体:在消息头中指定消息体的长度,接收端先读取消息头,获取消息长度,再读取对应长度的消息体,这是企业开发中最常用、最可靠的方案🏢。
Q7:Java网络编程的学习路线是什么?学完这些能做什么?📖
A7:Java网络编程的完整学习路线:
- 基础阶段:掌握网络通信核心概念、TCP/IP协议、Socket基础编程,就是本文覆盖的内容📚。
- 进阶阶段:学习IO模型(BIO、NIO、AIO)、Netty框架、HTTP/HTTPS协议、WebSocket协议🚀。
- 实战阶段:开发RPC框架、IM即时通讯系统、网关组件等企业级项目🏗️。
学完Java网络编程,你可以:
- 彻底理解Java后端接口调用的底层原理,打通HTTP、Tomcat、SpringBoot的底层网络通信逻辑🔗。
- 开发自定义的网络通信程序,比如即时通讯、物联网设备通信、游戏服务端🎮。
- 深入学习Netty、Dubbo、SpringCloud等主流框架,这些框架的底层都是基于Java网络编程实现的。
- 应对Java后端开发面试,网络编程、TCP/IP、Socket是中高级Java开发面试的必考点📝。
📚 权威参考资料 📚
- Oracle官方文档📖:Java网络编程官方指南
- IETF官方标准📜:RFC 793 TCP协议标准
- IETF官方标准📜:RFC 768 UDP协议标准
- CSDN官方Java技术专区💻:Java网络编程从入门到精通
- 阿里巴巴Java开发手册(黄山版)📘:网络编程规范
- 计算机网络(谢希仁 第8版)📚:TCP/IP协议体系权威教材
🏷️ 文章标签 🏷️
Java网络编程 📡 Socket 🔌 TCP 🛡️ UDP ⚡ Java基础 📚 网络通信 🔗 TCP/IP协议 📊 后端开发 💻 InetAddress 📍 零基础Java教程 ✅
如果本文对你有帮助,欢迎点赞👍、收藏⭐、评论💬、关注➕!
个人领域:C++/java/Al/软件开发/芯片开发
个人主页:「一名热衷协作的开发者,在构建中学习,期待与你交流技术、共同成长。」座右铭:「与其完美地观望,不如踉跄地启程」

