Java网络编程:TCP与UDP通信实现及网络编程基础
目录
- Java网络编程学习笔记(简化案例版):TCP与UDP通信实现及网络编程基础
- 一、网络编程基础:InetAddress与协议概述
- 1.1 InetAddress类:IP地址与主机名操作
- 常用方法及案例
- 1.2 TCP与UDP协议:特点与适用场景
- 二、UDP通信:无连接的数据报传输
- 2.1 UDP核心类:DatagramSocket与DatagramPacket
- 2.2 UDP通信案例:客户端发送消息,服务端接收
- 服务端实现(接收数据)
- 客户端实现(发送数据)
- 2.3 UDP通信步骤与注意事项
- 服务端步骤:
- 客户端步骤:
- 注意事项:
- 三、TCP通信:面向连接的可靠传输
- 3.1 TCP核心类:Socket与ServerSocket
- 3.2 TCP通信案例1:多线程服务端(支持同时处理多个客户端)
- 服务端
- 客户端实现
- 3.3 TCP通信案例2:B/S架构模拟(使用线程池管理)
- 简易HTTP服务端(支持浏览器访问)
- 3.4 TCP通信步骤与注意事项
- 服务端步骤:
- 客户端步骤:
- 注意事项:
- 四、并发与并行:网络编程中的多任务处理
- 4.1 概念区分
- 4.2 网络编程中的应用
- 五、总结
Java网络编程学习笔记(简化案例版):TCP与UDP通信实现及网络编程基础
一、网络编程基础:InetAddress与协议概述
1.1 InetAddress类:IP地址与主机名操作
InetAddress
是Java中用于表示IP地址的类,提供了获取主机名、IP地址等网络信息的方法。
常用方法及案例
import java.net.InetAddress;
import java.net.UnknownHostException;public class InetAddressDemo {public static void main(String[] args) throws UnknownHostException {// 1. 获取本地主机信息InetAddress localHost = InetAddress.getLocalHost();System.out.println("本地主机名:" + localHost.getHostName()); // 例如:DESKTOP-XXXSystem.out.println("本地IP地址:" + localHost.getHostAddress()); // 例如:192.168.1.100// 2. 根据域名获取IP地址(DNS解析)InetAddress baidu = InetAddress.getByName("www.baidu.com");System.out.println("百度主机名:" + baidu.getHostName()); // www.baidu.comSystem.out.println("百度IP地址:" + baidu.getHostAddress()); // 例如:180.101.50.242// 3. 判断是否可达(超时时间:3000毫秒)boolean reachable = baidu.isReachable(3000);System.out.println("百度服务器是否可达:" + reachable); // true(网络正常时)}
}
注意事项:
getByName(String host)
:host可以是域名(如www.baidu.com
)或IP字符串(如192.168.1.1
);isReachable(int timeout)
:判断主机是否可达,依赖ICMP协议(类似ping),部分系统可能需要管理员权限。
1.2 TCP与UDP协议:特点与适用场景
TCP(传输控制协议)和UDP(用户数据报协议)是TCP/IP协议族中两种核心传输层协议,核心区别在于是否提供可靠连接。
对比维度 | TCP | UDP |
---|---|---|
连接方式 | 面向连接(三次握手建立连接) | 无连接(直接发送,无需建立连接) |
可靠性 | 可靠(重传丢失数据包,保证顺序) | 不可靠(可能丢包、乱序,不重传) |
速度 | 较慢(三次握手、确认机制开销) | 较快(无连接开销,实时性高) |
数据边界 | 无(字节流,需应用层处理边界) | 有(数据报独立,一次发送一个完整包) |
适用场景 | 文件传输、登录认证、HTTP通信等 | 视频通话、直播、DNS查询、游戏数据等 |
类比生活场景 | 打电话(需接通,说话有顺序,对方确认听到) | 发短信(无需确认对方是否接收,直接发送) |
二、UDP通信:无连接的数据报传输
UDP是一种无连接、不可靠的传输协议,数据以“数据报”形式发送,适用于对实时性要求高但可容忍少量丢包的场景(如视频直播、游戏)。
2.1 UDP核心类:DatagramSocket与DatagramPacket
- DatagramSocket:用于发送/接收数据报的套接字(类似“快递收发站”);
- DatagramPacket:封装数据的数据包(类似“快递包裹”,包含数据、目标地址和端口)。
2.2 UDP通信案例:客户端发送消息,服务端接收
服务端实现(接收数据)
import java.net.DatagramPacket;
import java.net.DatagramSocket;public class UDPServer {public static void main(String[] args) throws Exception {// 1. 创建DatagramSocket,绑定端口(服务端必须指定端口,让客户端知道发送到哪里)DatagramSocket socket = new DatagramSocket(8888); // 端口8888(1024-65535之间,避免冲突)// 2. 创建数据包,用于接收数据(缓冲区大小:1024字节)byte[] buffer = new byte[1024];DatagramPacket packet = new DatagramPacket(buffer, buffer.length);System.out.println("UDP服务端启动,等待接收数据...");// 3. 接收数据(阻塞方法,直到收到数据包)socket.receive(packet); // 将接收的数据存入packet// 4. 解析数据包String data = new String(packet.getData(), 0, packet.getLength(), "UTF-8"); // 从缓冲区提取有效数据String clientIP = packet.getAddress().getHostAddress(); // 获取客户端IPint clientPort = packet.getPort(); // 获取客户端端口System.out.println("收到来自 " + clientIP + ":" + clientPort + " 的消息:" + data);}
}
客户端实现(发送数据)
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;public class UDPClient {public static void main(String[] args) throws Exception {// 1. 创建DatagramSocket(客户端可不指定端口,系统自动分配临时端口)DatagramSocket socket = new DatagramSocket();// 2. 准备发送的数据String message = "Hello UDP Server!";byte[] data = message.getBytes("UTF-8"); // 字符串转字节数组(网络传输必须用字节)// 3. 创建数据包,指定目标IP、端口和数据InetAddress serverIP = InetAddress.getByName("127.0.0.1"); // 服务端IP(本地回环地址,测试用)int serverPort = 8888; // 服务端端口(必须与服务端绑定的端口一致)DatagramPacket packet = new DatagramPacket(data, data.length, serverIP, serverPort);// 4. 发送数据包socket.send(packet);// 5. 关闭资源socket.close();}
}
2.3 UDP通信步骤与注意事项
服务端步骤:
- 创建
DatagramSocket
并绑定端口(new DatagramSocket(端口号)
); - 创建
DatagramPacket
作为接收缓冲区; - 调用
socket.receive(packet)
阻塞接收数据; - 解析
packet
获取数据、客户端IP和端口;
客户端步骤:
- 创建
DatagramSocket
(可不指定端口,系统自动分配); - 准备数据并转为字节数组;
- 创建
DatagramPacket
,指定服务端IP、端口和数据; - 调用
socket.send(packet)
发送数据; - 关闭
socket
。
注意事项:
- 无连接特性:UDP客户端发送数据前无需与服务端建立连接,可能出现“服务端未启动,客户端仍能发送数据”的情况(数据会丢失);
- 数据报边界:每个
DatagramPacket
是独立的,服务端receive()
一次接收一个完整数据包; - 不可靠性:UDP不保证数据一定到达,也不保证顺序,需应用层自行处理(如添加序号、重传机制);
- 端口冲突:服务端绑定的端口若被占用,会抛出
BindException
,需更换端口。
三、TCP通信:面向连接的可靠传输
TCP是一种面向连接、可靠的传输协议,通过三次握手建立连接,四次挥手断开连接,保证数据按序、不丢失地传输,适用于对可靠性要求高的场景(如文件传输、登录)。
3.1 TCP核心类:Socket与ServerSocket
- ServerSocket:服务端套接字,用于监听客户端连接请求(类似“总机接线员”);
- Socket:客户端与服务端建立连接后的“双向通道”,通过输入流接收数据,输出流发送数据(类似“电话接通后的通话线路”)。
3.2 TCP通信案例1:多线程服务端(支持同时处理多个客户端)
服务端
import java.net.ServerSocket;
import java.net.Socket;public class TCPServer {public static void main(String[] args) throws Exception {//目标:创建服务器,接收数据System.out.println("服务器启动...");//创建TCP服务器ServerSocket server = new ServerSocket(9999);while (true) {//等待客户端连接Socket ss = server.accept();//输出连接客户端的IP地址System.out.println(ss.getInetAddress().getHostAddress()+"客户端已上线");//将客户端连接的Socket对象交给ServerRead线程处理new ServerRead(ss).start();}}
}import java.io.DataInputStream;
import java.io.InputStream;
import java.net.Socket;public class ServerRead extends Thread{private Socket tcpserver;//创建有参数构造方法接收服务器套接字public ServerRead(Socket tcpserver){this.tcpserver = tcpserver;}@Overridepublic void run(){try {//创建输入流InputStream is = tcpserver.getInputStream();//使用特殊数据流包装输入流DataInputStream dis = new DataInputStream(is);while (true) {//获取数据String str = dis.readUTF();System.out.println("收到客户端:"+ tcpserver.getInetAddress().getHostAddress() +",端口:"+ tcpserver.getPort() + ",的数据:" + str);}} catch (Exception e) {//输入exit或客户端断开连接(非正常退出),则输出客户端已下线System.out.println(tcpserver.getInetAddress().getHostAddress() + "客户端已下线");}}
}
客户端实现
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class TCPClient {public static void main(String[] args) throws Exception {//目标:创建TCP客户端,发送数据System.out.println("客户端启动...");//创建TCP客户端,指定服务器的IP地址和端口号Socket socket = new Socket("127.0.0.1", 9999);//创建输出流OutputStream os = socket.getOutputStream();//使用特殊数据流包装输出流DataOutputStream dos = new DataOutputStream(os);Scanner sc = new Scanner(System.in);//发送数据while (true) {System.out.println("请输入数据:");String str = sc.nextLine();if ("exit".equals(str)){System.out.println("客户端退出...");socket.close();break;}dos.writeUTF(str);dos.flush();}}
}
3.3 TCP通信案例2:B/S架构模拟(使用线程池管理)
B/S架构(Browser/Server)是TCP通信的典型应用,浏览器(客户端)向Web服务器发送HTTP请求,服务器响应HTML页面。以下模拟这一过程:
简易HTTP服务端(支持浏览器访问)
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class HttpServer {public static void main(String[] args) throws Exception {ServerSocket serverSocket = new ServerSocket(8080); // HTTP默认端口是80,这里用8080测试ExecutorService threadPool = Executors.newFixedThreadPool(10); // 线程池管理请求System.out.println("HTTP服务端启动,监听端口8080,浏览器访问:http://127.0.0.1:8080");while (true) {Socket socket = serverSocket.accept(); // 接收浏览器连接(大堂经理接客)threadPool.submit(() -> handleHttp(socket)); //创建子线程 (转交给服务员)}}private static void handleHttp(Socket socket) {try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);) {// 读取HTTP请求(只读取请求行,简化处理)String requestLine = br.readLine();System.out.println("HTTP请求行:" + requestLine); // 例如:GET /index.html HTTP/1.1// 构建HTTP响应(响应行 + 响应头 + 空行 + 响应体)pw.println("HTTP/1.1 200 OK"); // 响应行:协议版本 状态码 状态描述pw.println("Content-Type: text/html;charset=UTF-8"); // 响应头:内容类型pw.println(); // 空行(分隔响应头和响应体)pw.println("<h1>Hello!</h1>"); // 响应体(HTML内容)} catch (Exception e) {e.printStackTrace();} finally {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}
}
测试方式:启动服务端后,打开浏览器访问http://127.0.0.1:8080
,页面会显示“Hello!”。
3.4 TCP通信步骤与注意事项
服务端步骤:
- 创建
ServerSocket
并绑定端口(new ServerSocket(端口号)
); - 循环调用
serverSocket.accept()
阻塞等待客户端连接,返回Socket
对象; - 通过
Socket
获取输入流(接收客户端数据)和输出流(发送响应); - 处理数据(如多线程/线程池并发处理多个客户端);
- 关闭
Socket
和ServerSocket
。
客户端步骤:
- 创建
Socket
,指定服务端IP和端口(new Socket(ip, port)
); - 通过
Socket
获取输入流(接收服务端响应)和输出流(发送数据); - 读写数据;
- 关闭
Socket
。
注意事项:
- 连接建立:TCP客户端
new Socket(ip, port)
时,若服务端未启动,会抛出ConnectException
(连接拒绝); - 流的关闭顺序:通常先关闭输出流,再关闭输入流,最后关闭
Socket
; - 粘包问题:TCP是字节流,多次发送的小数据可能被合并成一个包发送(需应用层处理边界,如固定长度、分隔符);
- 线程池优化:服务端处理多个客户端时,使用线程池避免“客户端过多导致线程创建耗尽资源”的问题(如案例3.2中的
ExecutorService
)。
四、并发与并行:网络编程中的多任务处理
4.1 概念区分
- 并发(Concurrency):同一时间段内,多个任务交替执行(单核CPU通过时间片切换实现,如“一个CPU同时处理多个客户端请求”);
- 并行(Parallelism):同一时刻,多个任务同时执行(多核CPU,多个任务在不同核心上真正同时运行)。
4.2 网络编程中的应用
- TCP服务端多线程/线程池:通过并发处理多个客户端连接(如案例3.2中,线程池同时处理5个客户端,单核CPU时交替执行,多核时并行执行);
- UDP服务端:可通过多线程同时接收和处理多个数据报,提高吞吐量。
五、总结
Java网络编程核心基于TCP和UDP协议,两者各有适用场景:
- UDP:无连接、速度快、不可靠,适用于实时通信(如视频、游戏);
- TCP:面向连接、可靠、速度较慢,适用于数据准确性要求高的场景(如文件传输、登录)。
开发步骤上,UDP通过DatagramSocket
和DatagramPacket
实现数据报传输,TCP通过ServerSocket
和Socket
实现字节流传输。实际开发中,需根据业务需求选择协议,并注意资源释放(如关闭Socket)、并发处理(如线程池)等问题。