14 Socket
1 UDP
DatagramSocket:用于发送和接收数据报
| // 创建DatagramSocket(绑定到随机端口)
DatagramSocket socket = new DatagramSocket();
// 创建DatagramSocket(绑定到指定端口)
DatagramSocket socket = new DatagramSocket(8888);
// 创建DatagramSocket(绑定到指定IP和端口)
InetAddress address = InetAddress.getByName("192.168.1.100");
DatagramSocket socket = new DatagramSocket(8888, address);
|
DatagramPacket:表示数据报(包含数据和目标地址)
| // 用于接收的数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 用于发送的数据包
byte[] data = "Hello".getBytes();
InetAddress address = InetAddress.getByName("localhost");
DatagramPacket packet = new DatagramPacket(data, data.length, address, 8888);
|
UDP Socket 通信:
| // UDP服务器端
public class UDPServer {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(8888);
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); // 接收数据
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println("收到UDP消息:" + message);
socket.close();
}
}
// UDP客户端
public class UDPClient {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
String message = "Hello UDP Server";
byte[] data = message.getBytes();
InetAddress address = InetAddress.getByName("localhost");
DatagramPacket packet = new DatagramPacket(
data, data.length, address, 8888
);
socket.send(packet); // 发送数据
socket.close();
}
}
|
2 TCP
Socket:客户端 socket
| // 创建Socket连接到服务器
Socket socket = new Socket("localhost", 8080);
|
ServerSocket:服务器 socket
| // 创建服务器Socket监听端口
ServerSocket serverSocket = new ServerSocket(8080);
|
TCP Socket 通信:
| public class Server {
public static void main(String[] args) throws IOException {
// 1. 创建ServerSocket,监听端口8888
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务器启动,等待客户端连接...");
// 2. 等待客户端连接
Socket socket = serverSocket.accept();
System.out.println("客户端已连接");
// 3. 获取输入流,读取客户端数据
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = br.readLine();
System.out.println("收到客户端消息:" + info);
// 4. 获取输出流,向客户端发送数据
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.println("Hello Client!");
pw.flush();
// 5. 关闭资源
pw.close();
br.close();
socket.close();
serverSocket.close();
}
}
public class Client {
public static void main(String[] args) throws IOException {
// 1. 创建Socket连接服务器
Socket socket = new Socket("localhost", 8888);
// 2. 获取输出流,向服务器发送数据
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.println("Hello Server!");
pw.flush();
// 3. 获取输入流,读取服务器响应
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String response = br.readLine();
System.out.println("收到服务器响应:" + response);
// 4. 关闭资源
br.close();
pw.close();
socket.close();
}
}
|
3 nio
3.1 Channel
Channel 是 NIO 中用于 I/O 操作的抽象,类似于流,但有一些重要区别:
| 特性 |
Channel |
Stream |
| 方向 |
双向(可读可写) |
单向 |
| 阻塞 |
支持非阻塞模式 |
总是阻塞 |
| 操作 |
读写 Buffer |
读写字节/字符 |
Channel 类型:
FileChannel:文件 IO
SocketChannel:TCP 客户端
ServerSocketChannel:TCP 服务器端
DatagramChannel:UDP
3.2 Buffer
Buffer 类型:
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
3.3 Selector
Selector 是多路复用器,可以监控多个 Channel 的 I/O 事件
| public class NioServerExample {
private Selector selector;
private ServerSocketChannel serverSocketChannel;
public NioServerExample(int port) throws IOException {
// 1. 创建Selector(多路复用器)
selector = Selector.open();
// 2. 创建ServerSocketChannel
serverSocketChannel = ServerSocketChannel.open();
// 3. 绑定端口并设置为非阻塞模式
serverSocketChannel.socket().bind(new InetSocketAddress(port));
serverSocketChannel.configureBlocking(false);
// 4. 将ServerSocketChannel注册到Selector,监听ACCEPT事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO服务器启动,监听端口: " + port);
}
public void start() throws IOException {
while (true) {
// 5. 阻塞等待就绪的Channel,超时时间1秒
int readyChannels = selector.select(1000);
if (readyChannels == 0) {
continue; // 没有就绪的Channel,继续等待
}
// 6. 获取就绪的SelectionKey集合
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
// 7. 处理不同的事件
if (key.isAcceptable()) {
handleAccept(key);
} else if (key.isReadable()) {
handleRead(key);
} else if (key.isWritable()) {
handleWrite(key);
}
// 8. 处理完成后移除当前key,防止重复处理
keyIterator.remove();
}
}
}
/**
* 处理客户端连接请求
*/
private void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
// 9. 接受客户端连接
SocketChannel clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);
// 10. 注册客户端Channel到Selector,监听READ事件
clientChannel.register(selector, SelectionKey.OP_READ);
System.out.println("客户端连接: " + clientChannel.getRemoteAddress());
// 发送欢迎消息
String welcomeMsg = "欢迎连接到NIO服务器!\r\n";
ByteBuffer buffer = ByteBuffer.wrap(welcomeMsg.getBytes());
clientChannel.write(buffer);
}
/**
* 处理读取客户端数据
*/
private void handleRead(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
try {
// 11. 读取客户端数据
int bytesRead = clientChannel.read(buffer);
if (bytesRead == -1) {
// 客户端关闭连接
System.out.println("客户端断开连接: " + clientChannel.getRemoteAddress());
clientChannel.close();
return;
}
if (bytesRead > 0) {
buffer.flip(); // 切换为读模式
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data).trim();
System.out.println("收到消息: " + message);
// 12. 准备回复消息
String response = "服务器收到: " + message + "\r\n";
ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
// 13. 注册WRITE事件,准备发送回复
clientChannel.register(selector, SelectionKey.OP_WRITE, responseBuffer);
}
} catch (IOException e) {
System.out.println("读取数据异常,关闭连接");
clientChannel.close();
}
}
/**
* 处理写入数据到客户端
*/
private void handleWrite(SelectionKey key) throws IOException {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
if (buffer.hasRemaining()) {
// 14. 写入数据到客户端
clientChannel.write(buffer);
}
if (!buffer.hasRemaining()) {
// 数据发送完成,重新注册READ事件
clientChannel.register(selector, SelectionKey.OP_READ);
}
}
public static void main(String[] args) {
try {
NioServerExample server = new NioServerExample(8888);
server.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class NioClientExample {
public static void main(String[] args) throws IOException {
// 1. 创建SocketChannel
SocketChannel socketChannel = SocketChannel.open();
// 2. 连接到服务器
socketChannel.connect(new InetSocketAddress("localhost", 8888));
socketChannel.configureBlocking(false);
System.out.println("连接到服务器...");
// 3. 读取服务器欢迎消息
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (socketChannel.read(buffer) == 0) {
// 非阻塞模式下可能立即返回0,稍作等待
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println("服务器说: " + new String(data));
// 4. 创建Scanner读取用户输入
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("请输入消息 (输入 'exit' 退出): ");
String message = scanner.nextLine();
if ("exit".equalsIgnoreCase(message)) {
break;
}
// 5. 发送消息到服务器
ByteBuffer writeBuffer = ByteBuffer.wrap((message + "\r\n").getBytes());
socketChannel.write(writeBuffer);
// 6. 读取服务器响应
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int bytesRead;
// 等待服务器响应
while ((bytesRead = socketChannel.read(readBuffer)) == 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (bytesRead > 0) {
readBuffer.flip();
byte[] responseData = new byte[readBuffer.remaining()];
readBuffer.get(responseData);
System.out.println("服务器回复: " + new String(responseData));
}
readBuffer.clear();
}
// 7. 关闭连接
socketChannel.close();
scanner.close();
System.out.println("客户端已断开连接");
}
}
|