网络编程
TCP 和 UDP 协议
TCP 协议
TCP 是面向连接、可靠传输的协议。通信前需要建立连接,并通过确认、重传、排序等机制保证数据尽可能完整且有序地到达。
UDP 协议
UDP 是面向数据报的无连接协议。它不会保证数据一定到达,也不会保证到达顺序,但协议开销较小,适合能够容忍少量丢包或由应用层自行保证可靠性的场景。
选择 TCP 还是 UDP,应根据可靠性、时延、吞吐量和业务特征决定,不能只根据网络状况判断。
TCP 编程
InetAddress 类
InetAddress 用于表示 IP 地址及其对应的主机信息。
localhost是指向本机的主机名。127.0.0.1是 IPv4 环回地址。
客户端与服务器模型
TCP 通常采用客户端与服务器模型。服务器通过 ServerSocket 监听端口,客户端使用 Socket 主动发起连接。


TCP 服务器端
public class TestServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(7000);
Socket socket = serverSocket.accept();
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(
socket.getOutputStream(), true)) {
System.out.println("有客户端连接了服务器");
System.out.println(reader.readLine());
writer.println("1 + 1 的结果是 2");
} catch (IOException e) {
e.printStackTrace();
}
}
}accept() 会阻塞当前线程,直到有客户端建立连接。连接成功后,服务器通过套接字的输入流接收数据,通过输出流发送数据。
TCP 客户端
public class TestClient {
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 7000);
PrintWriter writer = new PrintWriter(
socket.getOutputStream(), true);
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()))) {
writer.println("我是客户端,1 + 1 = ?");
System.out.println(reader.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}客户端和服务器都可以同时读取、写入数据。实现双向聊天时,通常需要分别处理发送和接收操作,避免某一方持续阻塞导致无法继续通信。
UDP 编程
DatagramSocket
DatagramSocket 用于发送和接收 UDP 数据报,可以理解为程序收发数据报的通信端点。
DatagramPacket
DatagramPacket 表示一个数据报。发送时需要封装数据、目标地址和目标端口;接收时需要预先准备字节数组作为接收缓冲区。
A 端程序
public class ATestUDP {
public static void main(String[] args) {
try (DatagramSocket socket = new DatagramSocket(7004)) {
byte[] sendData = "hello".getBytes(StandardCharsets.UTF_8);
DatagramPacket sendPacket = new DatagramPacket(
sendData,
sendData.length,
InetAddress.getByName("localhost"),
7003
);
socket.send(sendPacket);
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(
receiveData,
receiveData.length
);
socket.receive(receivePacket);
String message = new String(
receivePacket.getData(),
0,
receivePacket.getLength(),
StandardCharsets.UTF_8
);
System.out.println(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}B 端程序
public class BTestUDP {
public static void main(String[] args) {
try (DatagramSocket socket = new DatagramSocket(7003)) {
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(
receiveData,
receiveData.length
);
socket.receive(receivePacket);
String message = new String(
receivePacket.getData(),
0,
receivePacket.getLength(),
StandardCharsets.UTF_8
);
System.out.println(message);
byte[] sendData = "我收到了".getBytes(StandardCharsets.UTF_8);
DatagramPacket sendPacket = new DatagramPacket(
sendData,
sendData.length,
receivePacket.getAddress(),
receivePacket.getPort()
);
socket.send(sendPacket);
} catch (IOException e) {
e.printStackTrace();
}
}
}receive() 会阻塞,直到接收到数据报。UDP 每次发送和接收的基本单位都是一个完整的数据报。
URL
URL 是统一资源定位符,用于定位网络上的资源。常见结构可表示为:
协议://主机:端口/路径?参数名=参数值&参数名=参数值
端口、路径和查询参数不一定全部存在。
URL 编程
读取网页内容
public class TestURL {
public static void main(String[] args) {
try {
URL url = new URL("https://www.baidu.com/");
URLConnection connection = url.openConnection();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
connection.getInputStream(),
StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}下载网络文件
public class URLDownload {
public static void main(String[] args) {
String address = "https://copyright.bdstatic.com/vcg/creative/"
+ "65e11b3d96ac8f9b293b2d3486b7c422.jpg@wm_1,"
+ "k_cGljX2JqaHdh dGVyLmpwZw==".replace(" ", "");
try {
URL url = new URL(address);
URLConnection connection = url.openConnection();
try (BufferedInputStream input = new BufferedInputStream(
connection.getInputStream());
BufferedOutputStream output = new BufferedOutputStream(
new FileOutputStream("d:/lession/java2601/test/a.jpg"))) {
byte[] buffer = new byte[8192];
int length;
while ((length = input.read(buffer)) != -1) {
output.write(buffer, 0, length);
}
output.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}下载二进制文件时,应使用字节流,并通过缓冲区批量读写,避免逐字节处理造成不必要的性能损耗。
喜欢的话,留下你的评论吧~