Java 网络编程:从基础概念到 TCP/UDP 实践

VibeCoding·九月创作之星挑战赛 10w+人浏览 1.5k人参与

网络编程是实现不同设备间数据传输的核心技术,Java 通过java.net包提供了丰富的 API,支持多种网络通信场景。本文将从网络编程基础概念入手,详解核心要素、架构差异,再结合 TCP 与 UDP 协议的实际案例,完整梳理 Java 网络编程的实现流程。

一、网络编程基础认知

1. 什么是网络编程?

网络编程指在网络协议规范下,实现不同计算机(或设备)上运行的程序之间的数据传输,常见应用场景包括即时通信、在线游戏、金融交易等。Java 的java.net包封装了底层网络细节,开发者无需关注复杂的协议实现,即可快速构建网络应用。

2. 两种核心架构对比

实际开发中,网络应用主要基于B/SC/S架构,二者各有优劣:

架构类型全称核心特点优点缺点
B/S浏览器端 / 服务器端客户端为浏览器,无需单独开发无需开发客户端;用户免下载,打开浏览器即可使用应用过大时,浏览器渲染压力大,用户体验下降
C/S应用端 / 服务器端需单独开发客户端(如 APP、桌面软件)界面精美,交互流畅,用户体验好需同时开发客户端与服务器端;用户需下载安装,更新繁琐

二、网络编程三要素

要实现设备间通信,必须明确IP 地址端口号通信协议三个核心要素,缺一不可。

1. IP 地址:设备的唯一标识

IP 地址是网络中设备的唯一编号,用于定位设备,分为IPv4IPv6两个版本:

  • IPv4:32 位二进制数,分 4 组(每组 8 位),格式如192.168.1.1,地址资源有限,已接近枯竭。
  • IPv6:128 位二进制数,分 8 组(每组 16 位),格式如2001:0db8:85a3:0000:0000:8a2e:0370:7334,用于解决 IPv4 地址不足问题。
特殊 IP 地址:
  • 私有地址192.168.0.0 - 192.168.255.255,仅用于局域网(如家庭 WiFi、公司内网),不对外暴露。
  • 本地回环地址127.0.0.1(或localhost),用于测试本机程序通信,数据不会发送到外部网络。
常用 CMD 命令:
  • ipconfig:查看本机 IP 地址、子网掩码等网络配置。
  • ping [IP/域名]:测试与目标设备的网络连通性(如ping 127.0.0.1测试本机网络)。
Java 中操作 IP:InetAddress

InetAddress类封装了 IP 地址信息,可通过静态方法获取实例:

// 通过IP地址获取实例
InetAddress addr1 = InetAddress.getByName("127.0.0.1");
// 通过主机名获取实例(需DNS解析)
InetAddress addr2 = InetAddress.getByName("localhost");

// 获取主机名
System.out.println(addr1.getHostName()); // 输出:localhost
// 获取IP地址字符串
System.out.println(addr1.getHostAddress()); // 输出:127.0.0.1

2. 端口号:应用程序的唯一标识

端口号是设备内应用程序的唯一编号,用于区分同一设备上的不同服务(如浏览器用 80 端口,QQ 用特定端口)。

  • 取值范围:0-65535(2 个字节表示)。
  • 注意事项:
    • 0-1023 为 “知名端口”,用于系统服务或常用协议(如 HTTP 用 80,HTTPS 用 443),开发者应使用 1024 以上端口。
    • 一个端口号只能被一个应用程序占用,否则会抛出 “端口被占用” 异常。

3. 通信协议:数据传输的规则

协议是设备间数据传输的约定,定义了数据的格式、传输速率、错误处理等。Java 网络编程中最常用的是UDPTCP协议:

协议类型核心特点优点缺点适用场景
UDP无连接、不可靠、面向数据报传输速度快;无连接 overhead;支持广播 / 组播不保证数据送达;数据大小限制(单次最多 64KB);可能乱序即时通信(如 QQ 消息)、视频直播、DNS 查询
TCP面向连接、可靠、面向字节流保证数据可靠送达(重传机制);无数据大小限制;有序连接建立耗时;传输效率低于 UDP文件传输(如下载)、登录验证、HTTP/HTTPS 通信

三、UDP 协议实践:无连接通信

UDP 通信无需建立连接,发送方直接将 “数据报”(DatagramPacket)发送到目标 IP 和端口,接收方被动接收。核心类为DatagramSocket(用于发送 / 接收数据报)和DatagramPacket(封装数据、目标地址和端口)。

1. UDP 发送端(UDPSendMessage)

步骤:

  1. 创建DatagramSocket对象(绑定端口,可选)。
  2. 封装数据为DatagramPacket(指定目标 IP 和端口)。
  3. 发送数据报。
  4. 释放资源。

代码实现:

import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;

public class UDPSendMessage {
    public static void main(String[] args) throws Exception {
        // 1. 创建发送端Socket(无参:随机端口;带参:指定端口)
        DatagramSocket sendSocket = new DatagramSocket();
        
        // 2. 准备数据并打包
        String data = "Hello UDP!";
        byte[] dataBytes = data.getBytes(); // 字符串转字节数组
        InetAddress targetIp = InetAddress.getByName("127.0.0.1"); // 目标IP(本机)
        int targetPort = 10086; // 目标端口
        
        // 封装数据报:数据字节数组、长度、目标IP、目标端口
        DatagramPacket packet = new DatagramPacket(
            dataBytes, dataBytes.length, targetIp, targetPort
        );
        
        // 3. 发送数据
        sendSocket.send(packet);
        
        // 4. 释放资源
        sendSocket.close();
    }
}

2. UDP 接收端(UDPReceiveMessage)

步骤:

  1. 创建DatagramSocket对象(必须绑定与发送端一致的端口)。
  2. 创建空数据报(用于接收数据)。
  3. 阻塞接收数据(receive()方法会等待数据到来)。
  4. 解析数据报(获取数据、发送方 IP 和端口)。
  5. 释放资源。

代码实现:

import java.net.DatagramSocket;
import java.net.DatagramPacket;

public class UDPReceiveMessage {
    public static void main(String[] args) throws Exception {
        // 1. 创建接收端Socket(绑定端口,必须与发送端一致)
        DatagramSocket receiveSocket = new DatagramSocket(10086);
        
        // 2. 创建空数据报(缓冲区:1024字节,足够存储大部分UDP数据)
        byte[] buffer = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
        
        // 3. 阻塞接收数据(程序会停在这里,直到收到数据)
        System.out.println("UDP接收端已启动,等待数据...");
        receiveSocket.receive(packet);
        
        // 4. 解析数据
        // 获取有效数据(避免缓冲区空字符)
        byte[] receivedData = packet.getData();
        int dataLength = packet.getLength();
        String data = new String(receivedData, 0, dataLength);
        
        // 获取发送方信息
        String senderIp = packet.getAddress().getHostAddress();
        int senderPort = packet.getPort();
        
        // 打印结果
        System.out.println("接收到数据:" + data);
        System.out.println("发送方:" + senderIp + ":" + senderPort);
        
        // 5. 释放资源
        receiveSocket.close();
    }
}

四、TCP 协议实践:可靠连接通信

TCP 是 “面向连接” 的协议,通信前必须先建立三次握手连接,通信后通过四次挥手关闭连接,保证数据可靠传输。核心类为Socket(客户端)和ServerSocket(服务器端),通过 IO 流实现数据读写。

1. TCP 客户端(TCPSendMessage)

客户端主动发起连接,向服务器发送数据。步骤:

  1. 创建Socket对象(指定服务器 IP 和端口,创建时即发起连接)。
  2. 获取OutputStream(从 Socket 中获取输出流,用于发送数据)。
  3. 发送数据(通过输出流写入字节)。
  4. 释放资源(先关流,再关 Socket)。

代码实现:

import java.net.Socket;
import java.io.OutputStream;

public class TCPSendMessage {
    public static void main(String[] args) throws Exception {
        // 1. 创建客户端Socket(指定服务器IP和端口,发起连接)
        // 细节:若服务器未启动,会抛出“连接拒绝”异常
        Socket clientSocket = new Socket("127.0.0.1", 10000);
        
        // 2. 获取输出流(发送数据)
        OutputStream os = clientSocket.getOutputStream();
        
        // 3. 发送数据(字符串转字节数组)
        String data = "Hello TCP! 你好,服务器";
        os.write(data.getBytes());
        
        // 4. 释放资源
        os.close();
        clientSocket.close();
    }
}

2. TCP 服务器端(TCPReceiveMessage)

服务器端被动监听连接,接收客户端数据。步骤:

  1. 创建ServerSocket对象(绑定端口,用于监听客户端连接)。
  2. 阻塞等待客户端连接(accept()方法,返回与客户端通信的Socket)。
  3. 获取InputStream(从 Socket 中获取输入流,用于接收数据)。
  4. 读取数据(通过输入流读取字节,转成字符串)。
  5. 释放资源(先关流,再关客户端 Socket,最后关 ServerSocket)。

代码实现:

import java.net.ServerSocket;
import java.net.Socket;
import java.io.InputStream;
import java.io.InputStreamReader;

public class TCPReceiveMessage {
    public static void main(String[] args) throws Exception {
        // 1. 创建服务器Socket(绑定端口,监听客户端连接)
        ServerSocket serverSocket = new ServerSocket(10000);
        System.out.println("TCP服务器已启动,等待客户端连接...");
        
        // 2. 阻塞等待客户端连接(返回与客户端通信的Socket)
        Socket clientSocket = serverSocket.accept();
        System.out.println("客户端已连接:" + clientSocket.getInetAddress().getHostAddress());
        
        // 3. 获取输入流(接收数据)
        InputStream is = clientSocket.getInputStream();
        // 包装为字符流(避免中文乱码,需与客户端编码一致)
        InputStreamReader isr = new InputStreamReader(is, "UTF-8");
        
        // 4. 读取数据(逐字符读取,直到流结束)
        int readChar;
        StringBuilder sb = new StringBuilder();
        while ((readChar = isr.read()) != -1) {
            sb.append((char) readChar);
        }
        System.out.println("接收到客户端数据:" + sb.toString());
        
        // 5. 释放资源
        isr.close();
        is.close();
        clientSocket.close();
        serverSocket.close();
    }
}

五、TCP 与 UDP 核心差异总结

对比维度UDPTCP
连接方式无连接面向连接(三次握手建立,四次挥手关闭)
可靠性不可靠(数据可能丢失、乱序)可靠(重传机制,保证数据有序送达)
数据限制单次最多 64KB无限制(基于字节流,可分段传输)
通信效率高(无连接 overhead)低(连接建立 / 关闭耗时,确认机制开销)
核心类DatagramSocketDatagramPacketSocketServerSocket
适用场景即时通信、直播、DNS文件传输、登录、HTTP/HTTPS

六、总结

Java 网络编程的核心是基于java.net包封装的 TCP/UDP 协议,开发者需先理解 “IP - 端口 - 协议” 三要素,再根据业务场景选择合适的协议:

  • 追求效率、可接受少量数据丢失:选 UDP(如直播、消息推送)。
  • 要求数据可靠、无丢失:选 TCP(如文件下载、登录验证)。

通过本文的基础概念梳理和代码实践,可掌握 Java 网络编程的核心流程,后续可进一步学习 Socket 长连接、NIO 非阻塞通信等高级内容,应对高并发网络场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值