目录
- Java网络编程学习笔记(简化案例版):TCP与UDP通信实现及网络编程基础
-
- 一、网络编程基础:InetAddress与协议概述
-
- [1.1 InetAddress类:IP地址与主机名操作](#1.1 InetAddress类:IP地址与主机名操作)
- [1.2 TCP与UDP协议:特点与适用场景](#1.2 TCP与UDP协议:特点与适用场景)
- 二、UDP通信:无连接的数据报传输
-
- [2.1 UDP核心类:DatagramSocket与DatagramPacket](#2.1 UDP核心类:DatagramSocket与DatagramPacket)
- [2.2 UDP通信案例:客户端发送消息,服务端接收](#2.2 UDP通信案例:客户端发送消息,服务端接收)
- [2.3 UDP通信步骤与注意事项](#2.3 UDP通信步骤与注意事项)
- 三、TCP通信:面向连接的可靠传输
- 四、并发与并行:网络编程中的多任务处理
-
- [4.1 概念区分](#4.1 概念区分)
- [4.2 网络编程中的应用](#4.2 网络编程中的应用)
- 五、总结
Java网络编程学习笔记(简化案例版):TCP与UDP通信实现及网络编程基础
一、网络编程基础:InetAddress与协议概述
1.1 InetAddress类:IP地址与主机名操作
InetAddress
是Java中用于表示IP地址的类,提供了获取主机名、IP地址等网络信息的方法。
常用方法及案例
java
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-XXX
System.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.com
System.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通信案例:客户端发送消息,服务端接收
服务端实现(接收数据)
java
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(); // 获取客户端IP
int clientPort = packet.getPort(); // 获取客户端端口
System.out.println("收到来自 " + clientIP + ":" + clientPort + " 的消息:" + data);
}
}
客户端实现(发送数据)
java
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:多线程服务端(支持同时处理多个客户端)
服务端
java
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;
}
@Override
public 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() + "客户端已下线");
}
}
}
客户端实现
java
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服务端(支持浏览器访问)
java
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)、并发处理(如线程池)等问题。