零基础Java网络编程全解:从核心概念到Socket实战,一文打通Java网络通信


🌸你好呀!我是断弦承露
🌟感谢陪伴~ 小白博主在线求友
🌿 跟着小白学/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 什么是网络通信 💬

网络通信的本质,是两台或多台设备之间,通过网络实现数据的传输与交换

我们日常的刷网页🌐、发微信💬、传文件📁、调用后端接口🔗,底层都是网络通信的过程。简单来说,网络通信要完成的核心事情只有两件:

  1. 找到网络上的目标设备(通过IP地址定位📍)
  2. 把数据准确、可靠地传输给目标设备上的对应程序(通过端口号定位程序,通过协议保证传输📜)

Java语言天生具备极强的网络编程能力💪,JDK内置的java.net包提供了完整的类与接口,封装了网络通信的底层细节,让开发者无需关注网络传输的底层实现,即可快速完成网络程序开发。

1.2 网络的定义与分类 🌍

网络的核心定义:两台或多台设备,通过物理传输介质(网线、WiFi、光纤)与网络设备(路由器、交换机)连接起来,就构成了网络

根据网络的覆盖范围,行业通用的分类标准如下📋:

网络类型 覆盖范围 典型场景 核心特点
局域网(LAN)🏠 最小,几米到几公里 一个教室、机房、公司办公室、家庭网络 传输速度快⚡、延迟低、网络环境稳定、封闭性强
城域网(MAN)🏙️ 中等,覆盖一个城市 城市内的政务网络、运营商城市骨干网 覆盖范围介于局域网和广域网之间,适配城市级数据传输
广域网(WAN)🌐 最大,可覆盖全国甚至全球 互联网、跨城市的企业专线网络 覆盖范围广、传输节点多、延迟相对较高,万维网(WWW)是广域网最典型的代表

🧭 二、网络通信核心标识 🧭

想要完成网络通信,必须有三个核心标识🔑,分别解决「设备在哪」「设备叫什么」「数据发给哪个程序」三个核心问题。

2.1 IP地址详解 📍

IP地址(Internet Protocol Address),是网络中设备的唯一身份标识,作用相当于现实中的家庭地址🏠,网络中的数据包通过IP地址,找到对应的目标设备。

2.1.1 IP地址核心基础 📋
  1. 查看本机IP地址命令
    • Windows系统💻:CMD终端执行 ipconfig
    • Linux/Mac系统🖥️:终端执行 ip addrifconfig为旧版命令,部分新发行版已废弃)
  2. IP地址的表示形式 :主流的IPv4采用点分十进制 格式,写作 xx.xx.xx.xx,比如我们最常用的本机地址 192.168.1.69
  3. 数值范围 :点分十进制的每一段,都是一个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为受限广播地址

💡 关键修正说明:

  1. 特殊回环地址:127.0.0.1 是本机回环地址,永远指向当前设备自身,用于本机程序的网络通信测试🧪,不会经过物理网卡。
  2. 私有地址段🔒: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 端口号核心规则 📏
  1. 表示形式 :端口号是一个16位的无符号整数 ,因此取值范围是 0 ~ 65535(2^16 - 1)

  2. 端口号分类 :行业通用分为三大类,有明确的使用规范📋

    端口类型 端口范围 核心用途与使用规则
    知名端口(系统保留端口)🔒 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协议核心特性 ✨
  1. 面向连接 :使用TCP协议传输数据前,必须先通过三次握手 建立稳定的连接,传输完成后,必须通过四次挥手释放连接📞,就像打电话必须先拨号接通,才能说话,说完必须挂电话。
  2. 可靠传输 :TCP通过校验和、序列号、累计确认应答、超时重传+快速重传机制、滑动窗口流量控制、慢开始+拥塞避免+快重传+快恢复拥塞控制,保证数据无丢失、无重复、按顺序到达,是可靠的传输协议✅。
  3. 面向字节流:TCP把数据看作一串无边界的字节流,不限制传输的数据大小,适合传输大量文件📁。
  4. 全双工通信:连接建立后,通信双方可以同时发送和接收数据,双向通信互不影响🔄。
3.3.2 TCP三次握手(连接建立)🤝

TCP建立连接的过程,称为三次握手,核心是确保双方的发送和接收能力都正常,并同步初始序列号(ISN):

  1. 第一次握手(SYN):客户端向服务端发送SYN报文,同步自己的初始序列号x,进入SYN_SENT状态。
  2. 第二次握手(SYN+ACK):服务端收到SYN后,回复SYN+ACK报文,同步自己的初始序列号y,并确认客户端的序列号x+1,进入SYN_RCVD状态。
  3. 第三次握手(ACK):客户端收到SYN+ACK后,回复ACK报文,确认服务端的序列号y+1,双方进入ESTABLISHED状态,连接正式建立✅。
3.3.3 TCP四次挥手(连接释放)👋

TCP断开连接的过程,称为四次挥手,核心是确保双方的数据都已经传输完成,安全释放连接,并进入TIME_WAIT状态:

  1. 第一次挥手(FIN):客户端向服务端发送FIN报文,请求断开连接,进入FIN_WAIT_1状态。
  2. 第二次挥手(ACK):服务端收到FIN后,回复ACK报文,确认断开请求,进入CLOSE_WAIT状态,客户端收到ACK后进入FIN_WAIT_2状态,服务端继续传输未发完的数据。
  3. 第三次挥手(FIN):服务端数据传输完成后,向客户端发送FIN报文,进入LAST_ACK状态。
  4. 第四次挥手(ACK):客户端收到FIN后,回复ACK报文,进入TIME_WAIT状态,等待2MSL(最大报文段生存时间,通常为2分钟)后,双方正式关闭连接🔚。

💡 关键补充:TIME_WAIT状态的作用

  1. 保证最后一个ACK报文能到达服务端,避免服务端因收不到ACK而重复发送FIN。
  2. 避免旧连接的延迟数据包在新连接中出现,干扰新连接的数据传输🔇。

3.4 UDP协议详解 ⚡

UDP(User Datagram Protocol),中文全称用户数据报协议,是与TCP并列的传输层协议。

3.4.1 UDP协议核心特性 ✨
  1. 无连接:使用UDP协议传输数据,不需要提前建立连接,直接把数据封装成数据包发送📨,不需要维护连接状态,就像发短信,编辑好内容直接发送,不需要提前接通。
  2. 不可靠传输:UDP没有确认应答、重传机制,数据包发送后,不保证对方一定能收到,也不保证数据按顺序到达,是不可靠的传输协议❌。
  3. 面向数据报:UDP把数据封装成一个个独立的数据报,单个数据报的总长度(包括UDP首部8字节+IP首部20字节)最大为65535字节,因此实际数据部分最大为65507字节,不适合传输大量数据。
  4. 传输速度极快:UDP没有TCP的连接建立、确认重传、流量控制等机制,额外开销极小,传输延迟极低⚡,速度远快于TCP。
  5. 支持单播、多播、广播: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(客户端-服务端)架构🏗️,核心是SocketServerSocket两个类:

  1. 服务端:通过ServerSocket绑定端口,监听客户端的连接请求,接收到请求后,创建与客户端对应的Socket对象,完成数据读写。
  2. 客户端:通过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通信的核心是DatagramSocketDatagramPacket两个类,UDP无需建立连接,通过数据报的形式完成数据收发📨:

  1. 发送端:将数据、目标IP、端口封装到DatagramPacket数据包中,通过DatagramSocket发送数据包。
  2. 接收端:通过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());
        }
    }
}
运行步骤 📝
  1. 先运行TcpServer类,启动服务端,控制台输出服务端启动成功✅
  2. 再运行TcpClient类(可同时运行多个客户端👥),启动客户端,在控制台输入要发送的消息,按下回车
  3. 查看服务端和客户端的控制台输出,完成双向通信,输入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());
        }
    }
}
运行步骤 📝
  1. 先运行UdpReceiver类,启动接收端,控制台输出接收端启动成功✅
  2. 再运行UdpSender类,启动发送端,在控制台输入要发送的消息,按下回车
  3. 查看接收端控制台,会打印出发送端的消息,输入exit可退出🔚
  4. 如需测试广播📢,将ENABLE_BROADCAST设为trueRECEIVER_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:核心选择标准看业务场景的核心需求:

  1. 如果你的业务对数据可靠性要求极高✅,不允许丢包、乱序,比如文件传输、支付接口调用、用户登录、邮件收发,必须选择TCP协议。
  2. 如果你的业务对实时性要求极高⚡,允许少量丢包,比如直播、视频通话、游戏、实时监控、广播消息,优先选择UDP协议。
  3. 绝大多数企业级Java后端开发,都是基于TCP协议的HTTP/HTTPS接口开发,TCP是Java网络编程的核心重点📚。

Q2:Socket是什么?为什么叫套接字?🔌

A2:Socket的中文翻译是套接字,它是操作系统提供的网络通信操作的抽象接口 ,是应用程序与TCP/IP协议栈之间的桥梁🌉。

简单来说,Socket封装了网络通信的底层细节,给应用程序提供了统一的编程接口,我们只需要调用Socket的API,操作系统底层会帮我们完成TCP连接建立、数据传输、断开连接等一系列操作,无需关注底层协议的实现。

Q3:为什么TCP建立连接是三次握手,两次不行吗?🤝

A3:TCP三次握手的核心目的,是确认通信双方的发送能力和接收能力都正常,并同步初始序列号,两次握手无法完成这个确认:

  1. 如果只有两次握手,客户端发送的连接请求如果在网络中延迟了,客户端会重发请求,服务端收到后建立连接。但延迟的请求后续到达服务端,服务端会再次建立连接,而客户端已经关闭了,服务端会一直维护这个无效连接,浪费服务器资源💻。
  2. 两次握手只能确认客户端的发送能力、服务端的接收能力正常,无法确认服务端的发送能力、客户端的接收能力正常,三次握手才能完成双向能力的确认,保证连接的可靠性✅。

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是面向字节流的协议,它把数据看作一串无边界的字节流,没有数据包的概念,因此会出现粘包拆包问题:

  1. 粘包:发送端多次发送的小数据包,被TCP合并成一个大数据包发送,接收端一次收到了多个数据包的内容,无法区分。
  2. 拆包:发送端发送的一个大数据包,被TCP拆分成多个小包发送,接收端多次收到才能拼成完整的数据包。

核心解决方案

  1. 固定消息长度:每个消息的长度固定,接收端按固定长度读取数据。
  2. 特殊分隔符:在每个消息的末尾添加特殊的分隔符(比如换行符、自定义符号),接收端通过分隔符区分消息边界。
  3. 消息头+消息体:在消息头中指定消息体的长度,接收端先读取消息头,获取消息长度,再读取对应长度的消息体,这是企业开发中最常用、最可靠的方案🏢。

Q7:Java网络编程的学习路线是什么?学完这些能做什么?📖

A7:Java网络编程的完整学习路线:

  1. 基础阶段:掌握网络通信核心概念、TCP/IP协议、Socket基础编程,就是本文覆盖的内容📚。
  2. 进阶阶段:学习IO模型(BIO、NIO、AIO)、Netty框架、HTTP/HTTPS协议、WebSocket协议🚀。
  3. 实战阶段:开发RPC框架、IM即时通讯系统、网关组件等企业级项目🏗️。

学完Java网络编程,你可以:

  1. 彻底理解Java后端接口调用的底层原理,打通HTTP、Tomcat、SpringBoot的底层网络通信逻辑🔗。
  2. 开发自定义的网络通信程序,比如即时通讯、物联网设备通信、游戏服务端🎮。
  3. 深入学习Netty、Dubbo、SpringCloud等主流框架,这些框架的底层都是基于Java网络编程实现的。
  4. 应对Java后端开发面试,网络编程、TCP/IP、Socket是中高级Java开发面试的必考点📝。

📚 权威参考资料 📚

  1. Oracle官方文档📖:Java网络编程官方指南
  2. IETF官方标准📜:RFC 793 TCP协议标准
  3. IETF官方标准📜:RFC 768 UDP协议标准
  4. CSDN官方Java技术专区💻:Java网络编程从入门到精通
  5. 阿里巴巴Java开发手册(黄山版)📘:网络编程规范
  6. 计算机网络(谢希仁 第8版)📚:TCP/IP协议体系权威教材

🏷️ 文章标签 🏷️

Java网络编程 📡 Socket 🔌 TCP 🛡️ UDP ⚡ Java基础 📚 网络通信 🔗 TCP/IP协议 📊 后端开发 💻 InetAddress 📍 零基础Java教程 ✅


如果本文对你有帮助,欢迎点赞👍、收藏⭐、评论💬、关注➕!

个人领域:C++/java/Al/软件开发/芯片开发
个人主页:「一名热衷协作的开发者,在构建中学习,期待与你交流技术、共同成长。」

座右铭:「与其完美地观望,不如踉跄地启程」

相关推荐
饭小猿人2 小时前
Flutter实现底部动画弹窗有两种方式
开发语言·前端·flutter
aq55356002 小时前
Workstation神技:一键克隆调试环境
java·开发语言
lly2024062 小时前
框架:构建高效系统的基石
开发语言
isyangli_blog2 小时前
6. 使用Mininet创建星型网络拓扑,手动设置流表项
网络
skywalk81633 小时前
发现Kotti项目的python包Beaker 存在安全漏洞
开发语言·网络·python·安全
今天你TLE了吗3 小时前
LLM到Agent&RAG——AI知识点概述 第六章:Function Call函数调用
java·人工智能·学习·语言模型·大模型
Rcnhtin3 小时前
RocketMQ
java·linux·rocketmq
天天进步20153 小时前
Python全栈项目:从零构建基于 Django 的知识管理系统(KMS)
开发语言·python·django
JH30733 小时前
RedLock-红锁
java·redis