网络通信中的套接字技术 ~~~~~
时隔多日未更新,蓝桥杯赛事告一段落,现在开始持续输出技术干货;
一、网络编程概述
网络编程存在的意义何在?其核心价值在于实现跨设备的资源共享。设想这样的场景:当我们安坐家中,却能通过互联网访问全球各地的数字资源,欣赏到可能终生都无法亲眼所见的风景。这些资源本质上都是由二进制数据或文本信息构成,通过网络编程技术,我们可以实现海量用户对这些资源的并发访问。这与早期的单向信息传递相比,简直是天壤之别。
关键定义: 网络编程指的是不同主机上的进程,通过编程手段实现数据交换的过程;
值得注意的是,即使是同一台主机内不同进程间的通信,也属于网络编程范畴;
基础术语解析:
接收方:数据的目标接收进程及其所在主机;
发送方:数据的原始发送进程及其所在主机;
通信双方:同时具备收发功能的终端;
请求:向服务方发起的数据获取指令;
响应:服务方对请求的反馈数据;
类比餐饮场景:顾客点餐相当于请求,厨师上菜则是响应;
服务提供者:拥有资源并对外提供服务的一方;
服务请求者:向服务方获取资源的一方;
二、Socket技术详解
Socket即套接字,这个名称虽然抽象,但它实际上是操作系统提供的网络通信基础技术。作为TCP/IP协议栈的实现基础,Socket是网络程序开发的基本构建模块。
根据传输层协议差异,Socket主要分为两种类型:
我们重点探讨以下两种核心协议:
1. TCP协议:面向连接、可靠传输、基于字节流、全双工通信;
2. UDP协议:无连接、不可靠传输、基于数据报、全双工通信;
这里的"连接"是逻辑概念,并非物理连接;
TCP协议会维护通信对端信息,建立虚拟通道;而UDP则不会记录对方状态;
可靠与不可靠传输的区别:
网络传输过程中可能出现数据丢失,可靠传输通过确认机制和重传策略提高成功率,并能检测丢包情况;不可靠传输则发送后即不负责后续处理。虽然可靠传输更安全,但UDP在实时性要求高的场景更具优势;
字节流与数据报的区别:
字节流:以字节为最小传输单位;
数据报:以完整的数据包为传输单元;
全双工特性:
全双工:支持双向同时通信;
半双工:仅支持单向交替通信;
在Java中,网络设备通过Socket API被抽象为特殊文件进行统一管理;
三、UDP数据报编程实践
首先了解UDP编程的核心API:
DatagramSocket
用于UDP数据报的收发操作;
构造函数:
方法签名| 功能说明
---|---
DatagramSocket()| 创建客户端UDP套接字,自动绑定可用端口
DatagramSocket(int port)| 创建服务端UDP套接字,绑定指定端口
核心方法:
方法签名| 功能说明
---|---
void receive(DatagramPacket p)| 阻塞式接收数据报
void send(DatagramPacket p)| 非阻塞式发送数据报
void close()| 释放套接字资源
DatagramPacket
封装UDP通信的数据单元;
构造方法:
方法签名| 功能说明
---|---
DatagramPacket(byte[] buf, int length)| 创建接收用数据包,指定缓冲区和长度
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)| 创建发送用数据包,包含目标地址信息
实用方法:
方法签名| 功能说明
---|---
InetAddress getAddress()| 获取通信对端IP地址
int getPort()| 获取通信端口信息
byte[] getData()| 提取数据内容
SocketAddress getSocketAddress()| 获取完整地址信息
下面实现回声服务示例(服务端):
// UDP服务端实现代码
import java.net.*;
import java.io.*;
public class UdpEchoService {
private DatagramSocket serviceSocket;
public UdpEchoService(int port) throws SocketException {
serviceSocket = new DatagramSocket(port);
}
public void runService() throws IOException {
while(true) {
// 接收请求
byte[] buffer = new byte[4096];
DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);
serviceSocket.receive(receivePacket);
// 处理请求
String request = new String(receivePacket.getData(), 0, receivePacket.getLength());
String response = handleRequest(request);
// 发送响应
DatagramPacket responsePacket = new DatagramPacket(
response.getBytes(),
response.getBytes().length,
receivePacket.getSocketAddress()
);
serviceSocket.send(responsePacket);
// 记录日志
System.out.printf("客户端[%s:%d] 请求:%s 响应:%s\n",
receivePacket.getAddress().getHostAddress(),
receivePacket.getPort(),
request,
response);
}
}
private String handleRequest(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoService service = new UdpEchoService(9090);
service.runService();
}
}
客户端实现:
// UDP客户端实现代码
import java.net.*;
import java.util.Scanner;
public class UdpEchoClient {
private DatagramSocket clientSocket;
private InetAddress serverAddress;
private int serverPort;
public UdpEchoClient(String ip, int port) throws Exception {
this.serverAddress = InetAddress.getByName(ip);
this.serverPort = port;
this.clientSocket = new DatagramSocket();
}
public void startClient() throws IOException {
Scanner input = new Scanner(System.in);
while(true) {
System.out.print("请输入消息: ");
String message = input.nextLine();
// 发送请求
DatagramPacket requestPacket = new DatagramPacket(
message.getBytes(),
message.getBytes().length,
serverAddress,
serverPort
);
clientSocket.send(requestPacket);
// 接收响应
byte[] buffer = new byte[4096];
DatagramPacket responsePacket = new DatagramPacket(buffer, buffer.length);
clientSocket.receive(responsePacket);
String response = new String(
responsePacket.getData(),
0,
responsePacket.getLength()
);
System.out.println("服务器响应: " + response);
}
}
public static void main(String[] args) throws Exception {
UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
client.startClient();
}
}
运行效果展示:
四、TCP流式套接字编程
ServerSocket
服务端TCP套接字创建API;
构造方法:
方法签名| 功能说明
---|---
ServerSocket(int port)| 创建服务端套接字并绑定端口
核心方法:
方法签名| 功能说明
---|---
Socket accept()| 监听端口,阻塞等待客户端连接
void close()| 关闭套接字
Socket
客户端/服务端通信套接字;
构造方法:
方法签名| 功能说明
---|---
Socket(String host, int port)| 创建客户端套接字并建立连接
实用方法:
方法签名| 功能说明
---|---
InetAddress getInetAddress()| 获取连接地址
InputStream getInputStream()| 获取输入流
OutputStream getOutputStream()| 获取输出流
TCP服务端实现:
// TCP服务端实现
import java.net.*;
import java.io.*;
import java.util.concurrent.*;
public class TcpEchoService {
private ServerSocket serverSocket;
private ExecutorService threadPool;
public TcpEchoService(int port) throws IOException {
serverSocket = new ServerSocket(port);
threadPool = Executors.newCachedThreadPool();
}
public void startService() throws IOException {
System.out.println("TCP服务已启动");
while(true) {
Socket clientSocket = serverSocket.accept();
threadPool.execute(() -> {
try {
handleClient(clientSocket);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
private void handleClient(Socket clientSocket) throws IOException {
try (
InputStream in = clientSocket.getInputStream();
OutputStream out = clientSocket.getOutputStream();
Scanner scanner = new Scanner(in);
PrintWriter writer = new PrintWriter(out)
) {
while(scanner.hasNextLine()) {
String request = scanner.nextLine();
String response = processRequest(request);
writer.println(response);
writer.flush();
System.out.printf("客户端[%s:%d] 请求:%s 响应:%s\n",
clientSocket.getInetAddress().getHostAddress(),
clientSocket.getPort(),
request,
response);
}
}
}
private String processRequest(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoService service = new TcpEchoService(9090);
service.startService();
}
}
TCP客户端实现:
// TCP客户端实现
import java.net.*;
import java.io.*;
import java.util.*;
public class TcpEchoClient {
private Socket clientSocket;
public TcpEchoClient(String ip, int port) throws IOException {
clientSocket = new Socket(ip, port);
}
public void startClient() throws IOException {
try (
InputStream in = clientSocket.getInputStream();
OutputStream out = clientSocket.getOutputStream();
Scanner console = new Scanner(System.in);
Scanner netIn = new Scanner(in);
PrintWriter writer = new PrintWriter(out)
) {
while(true) {
System.out.print("请输入消息: ");
String message = console.nextLine();
writer.println(message);
writer.flush();
String response = netIn.nextLine();
System.out.println("服务器响应: " + response);
}
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
client.startClient();
}
}