Initial commit
This commit is contained in:
29
Final/.gitignore
vendored
Normal file
29
Final/.gitignore
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
### IntelliJ IDEA ###
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
10
Final/.idea/.gitignore
generated
vendored
Normal file
10
Final/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# 依赖于环境的 Maven 主目录路径
|
||||
/mavenHomeManager.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
6
Final/.idea/misc.xml
generated
Normal file
6
Final/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
8
Final/.idea/modules.xml
generated
Normal file
8
Final/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/Final.iml" filepath="$PROJECT_DIR$/Final.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
124
Final/.idea/uiDesigner.xml
generated
Normal file
124
Final/.idea/uiDesigner.xml
generated
Normal file
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
||||
4
Final/.idea/vcs.xml
generated
Normal file
4
Final/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings" defaultProject="true" />
|
||||
</project>
|
||||
11
Final/Final.iml
Normal file
11
Final/Final.iml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
108
Final/src/aio/AIOChatClient.java
Normal file
108
Final/src/aio/AIOChatClient.java
Normal file
@@ -0,0 +1,108 @@
|
||||
package aio;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.AsynchronousSocketChannel;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class AIOChatClient {
|
||||
private static final String HOST = "127.0.0.1";
|
||||
private static final int PORT = 8888;
|
||||
private AsynchronousSocketChannel clientChannel;
|
||||
private CountDownLatch latch;
|
||||
public void start() throws Exception {
|
||||
clientChannel = AsynchronousSocketChannel.open();
|
||||
latch = new CountDownLatch(1);
|
||||
clientChannel.connect(new InetSocketAddress(HOST, PORT), null, new CompletionHandler<Void, Object>() {
|
||||
@Override
|
||||
public void completed(Void result, Object attachment) {
|
||||
System.out.println("成功连接到服务器。现在可以开始聊天了(输入 'exit' 退出)。");
|
||||
startRead();
|
||||
startWrite();
|
||||
}
|
||||
@Override
|
||||
public void failed(Throwable exc, Object attachment) {
|
||||
System.err.println("连接服务器失败: " + exc.getMessage());
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
latch.await();
|
||||
}
|
||||
private void startRead() {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
||||
clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
|
||||
@Override
|
||||
public void completed(Integer result, ByteBuffer attachment) {
|
||||
if (result > 0) {
|
||||
attachment.flip();
|
||||
String message = StandardCharsets.UTF_8.decode(attachment).toString();
|
||||
System.out.println(message);
|
||||
attachment.clear();
|
||||
} else if (result == -1) {
|
||||
handleDisconnect();
|
||||
return;
|
||||
}
|
||||
if (clientChannel.isOpen()) {
|
||||
clientChannel.read(attachment, attachment, this);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void failed(Throwable exc, ByteBuffer attachment) {
|
||||
System.err.println("读取数据失败: " + exc.getMessage());
|
||||
handleDisconnect();
|
||||
}
|
||||
});
|
||||
}
|
||||
private void startWrite() {
|
||||
new Thread(() -> {
|
||||
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in));
|
||||
try {
|
||||
String message;
|
||||
while ((message = consoleReader.readLine()) != null) {
|
||||
if ("exit".equalsIgnoreCase(message.trim())) {
|
||||
break;
|
||||
}
|
||||
ByteBuffer buffer = StandardCharsets.UTF_8.encode(message);
|
||||
clientChannel.write(buffer, null, new CompletionHandler<Integer, Object>() {
|
||||
@Override
|
||||
public void completed(Integer result, Object attachment) {}
|
||||
@Override
|
||||
public void failed(Throwable exc, Object attachment) {
|
||||
System.err.println("发送消息失败: " + exc.getMessage());
|
||||
handleDisconnect();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
handleDisconnect();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
private void handleDisconnect() {
|
||||
try {
|
||||
if (clientChannel.isOpen()) {
|
||||
clientChannel.close();
|
||||
System.out.println("与服务器的连接已断开。");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
new AIOChatClient().start();
|
||||
} catch (Exception e) {
|
||||
System.err.println("客户端启动时发生错误。");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
127
Final/src/aio/AIOChatServer.java
Normal file
127
Final/src/aio/AIOChatServer.java
Normal file
@@ -0,0 +1,127 @@
|
||||
package aio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.AsynchronousChannelGroup;
|
||||
import java.nio.channels.AsynchronousServerSocketChannel;
|
||||
import java.nio.channels.AsynchronousSocketChannel;
|
||||
import java.nio.channels.CompletionHandler;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class AIOChatServer {
|
||||
private static final int PORT = 8888;
|
||||
private AsynchronousServerSocketChannel serverSocketChannel;
|
||||
private final CopyOnWriteArrayList<AsynchronousSocketChannel> clients = new CopyOnWriteArrayList<>();
|
||||
public void start() {
|
||||
try {
|
||||
AsynchronousChannelGroup group = AsynchronousChannelGroup.withThreadPool(Executors.newFixedThreadPool(10));
|
||||
serverSocketChannel = AsynchronousServerSocketChannel.open(group);
|
||||
serverSocketChannel.bind(new InetSocketAddress(PORT));
|
||||
System.out.println("服务器已启动,监听端口:" + PORT);
|
||||
serverSocketChannel.accept(null, new AcceptHandler());
|
||||
System.in.read();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
private class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, Object> {
|
||||
@Override
|
||||
public void completed(AsynchronousSocketChannel clientChannel, Object attachment) {
|
||||
serverSocketChannel.accept(null, this);
|
||||
clients.add(clientChannel);
|
||||
try {
|
||||
System.out.println("客户端 [" + clientChannel.getRemoteAddress() + "] 已连接。当前在线人数:" + clients.size());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
||||
clientChannel.read(buffer, new ClientSession(clientChannel, buffer), new ReadWriteHandler());
|
||||
}
|
||||
@Override
|
||||
public void failed(Throwable exc, Object attachment) {
|
||||
System.out.println("接受连接失败:" + exc);
|
||||
}
|
||||
}
|
||||
private class ReadWriteHandler implements CompletionHandler<Integer, ClientSession> {
|
||||
@Override
|
||||
public void completed(Integer result, ClientSession session) {
|
||||
if (result > 0) {
|
||||
session.buffer.flip();
|
||||
String receivedMessage = StandardCharsets.UTF_8.decode(session.buffer).toString().trim();
|
||||
if (!receivedMessage.isEmpty()) {
|
||||
String messageToSend;
|
||||
try {
|
||||
messageToSend = "客户端 [" + session.clientChannel.getRemoteAddress() + "] 说: " + receivedMessage;
|
||||
System.out.println("正在广播: " + messageToSend);
|
||||
broadcast(messageToSend, session.clientChannel);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
session.buffer.clear();
|
||||
session.clientChannel.read(session.buffer, session, this);
|
||||
} else if (result == -1) {
|
||||
handleDisconnect(session.clientChannel);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void failed(Throwable exc, ClientSession session) {
|
||||
System.out.println("读写数据时发生错误:" + exc.getMessage());
|
||||
handleDisconnect(session.clientChannel);
|
||||
}
|
||||
}
|
||||
private void broadcast(String message, AsynchronousSocketChannel sender) {
|
||||
ByteBuffer initialBuffer = StandardCharsets.UTF_8.encode(message);
|
||||
for (AsynchronousSocketChannel client : clients) {
|
||||
if (client.isOpen() && !client.equals(sender)) {
|
||||
ByteBuffer bufferForWriting = initialBuffer.duplicate();
|
||||
class WriteContext {
|
||||
final AsynchronousSocketChannel clientChannel;
|
||||
final ByteBuffer buffer;
|
||||
WriteContext(AsynchronousSocketChannel client, ByteBuffer buf) {
|
||||
this.clientChannel = client;
|
||||
this.buffer = buf;
|
||||
}
|
||||
}
|
||||
WriteContext context = new WriteContext(client, bufferForWriting);
|
||||
client.write(bufferForWriting, context, new CompletionHandler<Integer, WriteContext>() {
|
||||
@Override
|
||||
public void completed(Integer result, WriteContext attachment) {
|
||||
// 如果缓冲区中还有剩余数据,说明是一次“部分写”,需要继续发送
|
||||
if (attachment.buffer.hasRemaining()) {
|
||||
attachment.clientChannel.write(attachment.buffer, attachment, this);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void failed(Throwable exc, WriteContext attachment) {
|
||||
System.err.println("向客户端 " + attachment.clientChannel + " 广播消息失败: " + exc);
|
||||
handleDisconnect(attachment.clientChannel);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
private void handleDisconnect(AsynchronousSocketChannel clientChannel) {
|
||||
clients.remove(clientChannel);
|
||||
try {
|
||||
System.out.println("客户端 [" + clientChannel.getRemoteAddress() + "] 已断开连接。当前在线人数:" + clients.size());
|
||||
clientChannel.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClientSession {
|
||||
AsynchronousSocketChannel clientChannel;
|
||||
ByteBuffer buffer;
|
||||
public ClientSession(AsynchronousSocketChannel clientChannel, ByteBuffer buffer) {
|
||||
this.clientChannel = clientChannel;
|
||||
this.buffer = buffer;
|
||||
}
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
new AIOChatServer().start();
|
||||
}
|
||||
}
|
||||
96
Final/src/aio/AsyncQueueDemo.java
Normal file
96
Final/src/aio/AsyncQueueDemo.java
Normal file
@@ -0,0 +1,96 @@
|
||||
package aio;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class AsyncQueueDemo {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
final int QUEUE_CAPACITY = 10;
|
||||
final int PRODUCER_COUNT = 3;
|
||||
final int CONSUMER_COUNT = 5;
|
||||
final int ITEMS_PER_PRODUCER = 20;
|
||||
AsyncQueue<String> queue = new AsyncQueue<>(QUEUE_CAPACITY);
|
||||
ExecutorService producerExecutor = Executors.newFixedThreadPool(PRODUCER_COUNT);
|
||||
ExecutorService consumerExecutor = Executors.newFixedThreadPool(CONSUMER_COUNT);
|
||||
AtomicInteger producedCount = new AtomicInteger(0);
|
||||
AtomicInteger consumedCount = new AtomicInteger(0);
|
||||
System.out.println("启动 " + PRODUCER_COUNT + " 个生产者和 " + CONSUMER_COUNT + " 个消费者...");
|
||||
for (int i = 0; i < PRODUCER_COUNT; i++) {
|
||||
final int producerId = i + 1;
|
||||
producerExecutor.submit(() -> {
|
||||
for (int j = 0; j < ITEMS_PER_PRODUCER; j++) {
|
||||
String item = "产品-" + producerId + "-" + (j + 1);
|
||||
queue.putAsync(item).thenRun(() -> {
|
||||
System.out.println("生产者 " + producerId + " 成功放入: " + item);
|
||||
producedCount.incrementAndGet();
|
||||
});
|
||||
try {
|
||||
Thread.sleep((long)(Math.random() * 100));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
for (int i = 0; i < CONSUMER_COUNT; i++) {
|
||||
final int consumerId = i + 1;
|
||||
consumerExecutor.submit(() -> {
|
||||
while (consumedCount.get() < PRODUCER_COUNT * ITEMS_PER_PRODUCER) {
|
||||
queue.takeAsync().thenAccept(item -> {
|
||||
System.out.println("消费者 " + consumerId + " 成功取出: " + item);
|
||||
consumedCount.incrementAndGet();
|
||||
});
|
||||
try {
|
||||
Thread.sleep((long)(Math.random() * 200));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
producerExecutor.shutdown();
|
||||
producerExecutor.awaitTermination(1, TimeUnit.MINUTES);
|
||||
while (consumedCount.get() < PRODUCER_COUNT * ITEMS_PER_PRODUCER) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
System.out.println("\n所有任务完成。");
|
||||
System.out.println("总共生产: " + producedCount.get());
|
||||
System.out.println("总共消费: " + consumedCount.get());
|
||||
consumerExecutor.shutdownNow();
|
||||
}
|
||||
}
|
||||
class AsyncQueue<T> {
|
||||
private final BlockingQueue<T> queue;
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool();
|
||||
public AsyncQueue(int capacity) {
|
||||
this.queue = new ArrayBlockingQueue<>(capacity);
|
||||
}
|
||||
public CompletableFuture<Void> putAsync(T item) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
queue.put(item);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException("放入队列时被中断", e);
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
public CompletableFuture<T> takeAsync() {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return queue.take();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException("从队列取出时被中断", e);
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
public void shutdown() {
|
||||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
47
Final/src/multi_thread/AlternatingPrint.java
Normal file
47
Final/src/multi_thread/AlternatingPrint.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package multi_thread;
|
||||
|
||||
public class AlternatingPrint {
|
||||
private final Object lock = new Object();
|
||||
private int number = 1;
|
||||
public static void main(String[] args) {
|
||||
AlternatingPrint printer = new AlternatingPrint();
|
||||
Thread oddThread = new Thread(printer::printOddNumbers, "奇数计数线程");
|
||||
Thread evenThread = new Thread(printer::printEvenNumbers, "偶数计数线程");
|
||||
oddThread.start();
|
||||
evenThread.start();
|
||||
}
|
||||
public void printOddNumbers() {
|
||||
synchronized (lock) {
|
||||
while (number < 100) {
|
||||
while (number % 2 == 0) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
System.err.println("奇数计数线程中断.");
|
||||
}
|
||||
}
|
||||
System.out.println(Thread.currentThread().getName() + ": " + number);
|
||||
number++;
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void printEvenNumbers() {
|
||||
synchronized (lock) {
|
||||
while (number <= 100) {
|
||||
while (number % 2 != 0) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
System.err.println("偶数计数线程中断.");
|
||||
}
|
||||
}
|
||||
System.out.println(Thread.currentThread().getName() + ": " + number);
|
||||
number++;
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Final/src/multi_thread/CountDownLatchDemo.java
Normal file
43
Final/src/multi_thread/CountDownLatchDemo.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package multi_thread;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CountDownLatchDemo {
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
int numberOfWorkers = 3;
|
||||
CountDownLatch latch = new CountDownLatch(numberOfWorkers);
|
||||
System.out.println("主线程启动工作线程...");
|
||||
for (int i = 1; i <= numberOfWorkers; i++) {
|
||||
Thread worker = new Thread(new Worker(i, latch));
|
||||
worker.start();
|
||||
}
|
||||
System.out.println("主线程正在等待工作线程结束...");
|
||||
latch.await();
|
||||
System.out.println("所有工作线程结束,主线程继续运行。");
|
||||
}
|
||||
}
|
||||
|
||||
class Worker implements Runnable {
|
||||
private final int id;
|
||||
private final CountDownLatch latch;
|
||||
public Worker(int id, CountDownLatch latch) {
|
||||
this.id = id;
|
||||
this.latch = latch;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
System.out.println("multi_thread.Worker " + id + " 开始工作。");
|
||||
int workTime = (int) (Math.random() * 3000) + 1000;
|
||||
TimeUnit.MILLISECONDS.sleep(workTime);
|
||||
System.out.println("multi_thread.Worker " + id + " 完成工作。");
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
System.err.println("multi_thread.Worker " + id + " 被中断。");
|
||||
} finally {
|
||||
latch.countDown();
|
||||
System.out.println("multi_thread.Worker " + id + " 执行计数器减一. 剩余: " + latch.getCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Final/src/multi_thread/ExecutorServiceDemo.java
Normal file
55
Final/src/multi_thread/ExecutorServiceDemo.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package multi_thread;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ExecutorServiceDemo {
|
||||
public static void main(String[] args) {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(3);
|
||||
List<Future<String>> futureList = new ArrayList<>();
|
||||
int numberOfTasks = 5;
|
||||
System.out.println("提交 " + numberOfTasks + " 个任务到线程池...");
|
||||
for (int i = 1; i <= numberOfTasks; i++) {
|
||||
Callable<String> task = new Task(i);
|
||||
Future<String> future = executor.submit(task);
|
||||
futureList.add(future);
|
||||
}
|
||||
System.out.println("所有任务已提交,获取结果...");
|
||||
for (Future<String> future : futureList) {
|
||||
try {
|
||||
String result = future.get();
|
||||
System.out.println(result);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
System.err.println("获取结果时出错 " + e.getMessage());
|
||||
}
|
||||
}
|
||||
System.out.println("终止 executor service。");
|
||||
executor.shutdown();
|
||||
try {
|
||||
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Task implements Callable<String> {
|
||||
private final int taskId;
|
||||
public Task(int taskId) {
|
||||
this.taskId = taskId;
|
||||
}
|
||||
@Override
|
||||
public String call() throws Exception {
|
||||
System.out.println("-> 任务 " + taskId + " 运行于线程: " + Thread.currentThread().getName());
|
||||
TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 2000) + 500);
|
||||
return "任务结果: " + taskId;
|
||||
}
|
||||
}
|
||||
74
Final/src/multi_thread/ProducerConsumer.java
Normal file
74
Final/src/multi_thread/ProducerConsumer.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package multi_thread;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
|
||||
public class ProducerConsumer {
|
||||
public static void main(String[] args) {
|
||||
Buffer buffer = new Buffer(5);
|
||||
Thread producerThread = new Thread(() -> {
|
||||
try {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
buffer.produce(i);
|
||||
Thread.sleep(new Random().nextInt(100));
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
System.err.println("生产者被中断。");
|
||||
}
|
||||
}, "生产者");
|
||||
Thread consumerThread = new Thread(() -> {
|
||||
try {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
buffer.consume();
|
||||
// 模拟消费耗时
|
||||
Thread.sleep(new Random().nextInt(250));
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
System.err.println("消费者被中断。");
|
||||
}
|
||||
}, "消费者");
|
||||
producerThread.start();
|
||||
consumerThread.start();
|
||||
try {
|
||||
producerThread.join();
|
||||
consumerThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
System.err.println("主线程在等待时被中断。");
|
||||
}
|
||||
System.out.println("所有生产和消费任务完成。程序结束。");
|
||||
}
|
||||
}
|
||||
|
||||
class Buffer {
|
||||
private final Queue<Integer> queue;
|
||||
private final int capacity;
|
||||
public Buffer(int capacity) {
|
||||
this.queue = new LinkedList<>();
|
||||
this.capacity = capacity;
|
||||
}
|
||||
public synchronized void produce(int item) throws InterruptedException {
|
||||
while (queue.size() == capacity) {
|
||||
System.out.println("缓冲区已满。" + Thread.currentThread().getName() + " 进入等待状态 (释放锁)...");
|
||||
wait();
|
||||
System.out.println(Thread.currentThread().getName() + " 已被唤醒!");
|
||||
}
|
||||
queue.add(item);
|
||||
System.out.println(Thread.currentThread().getName() + " 生产了: " + item + " (当前容量: " + queue.size() + ")");
|
||||
notifyAll();
|
||||
}
|
||||
public synchronized int consume() throws InterruptedException {
|
||||
while (queue.isEmpty()) {
|
||||
System.out.println("缓冲区为空。" + Thread.currentThread().getName() + " 进入等待状态 (释放锁)...");
|
||||
wait();
|
||||
System.out.println(Thread.currentThread().getName() + " 已被唤醒!");
|
||||
}
|
||||
int item = queue.poll();
|
||||
System.out.println(Thread.currentThread().getName() + " 消费了: " + item + " (当前容量: " + queue.size() + ")");
|
||||
notifyAll();
|
||||
return item;
|
||||
}
|
||||
}
|
||||
26
Final/src/multi_thread/StaticSyncCounter.java
Normal file
26
Final/src/multi_thread/StaticSyncCounter.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package multi_thread;
|
||||
|
||||
public class StaticSyncCounter {
|
||||
private static int count = 0;
|
||||
public static synchronized void increment() {
|
||||
count++;
|
||||
}
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
int numThreads = 10;
|
||||
int incrementsPerThread = 1000;
|
||||
Thread[] threads = new Thread[numThreads];
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
threads[i] = new Thread(() -> {
|
||||
for (int j = 0; j < incrementsPerThread; j++) {
|
||||
increment();
|
||||
}
|
||||
});
|
||||
threads[i].start();
|
||||
}
|
||||
for (Thread thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
System.out.println("期望计数: " + (numThreads * incrementsPerThread));
|
||||
System.out.println("实际计数: " + count);
|
||||
}
|
||||
}
|
||||
87
Final/src/nio/NioChatClient.java
Normal file
87
Final/src/nio/NioChatClient.java
Normal file
@@ -0,0 +1,87 @@
|
||||
package nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Iterator;
|
||||
import java.util.Scanner;
|
||||
|
||||
public class NioChatClient {
|
||||
private static final int PORT = 8080;
|
||||
private static final String HOST = "127.0.0.1";
|
||||
private static final Charset CHARSET = Charset.forName("UTF-8");
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
Selector selector = Selector.open();
|
||||
SocketChannel socketChannel = SocketChannel.open();
|
||||
socketChannel.configureBlocking(false);
|
||||
socketChannel.register(selector, SelectionKey.OP_CONNECT);
|
||||
socketChannel.connect(new InetSocketAddress(HOST, PORT));
|
||||
new Thread(() -> {
|
||||
try (Scanner scanner = new Scanner(System.in)) {
|
||||
System.out.println("请输入要发送的消息...");
|
||||
while (scanner.hasNextLine()) {
|
||||
String message = scanner.nextLine();
|
||||
if (message.length() > 0) {
|
||||
socketChannel.write(CHARSET.encode(message));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("发送消息时发生错误: " + e.getMessage());
|
||||
}
|
||||
}).start();
|
||||
|
||||
while (true) {
|
||||
if (selector.select() == 0) continue;
|
||||
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
SelectionKey key = iterator.next();
|
||||
iterator.remove();
|
||||
if (key.isConnectable()) {
|
||||
handleConnect(key, selector);
|
||||
} else if (key.isReadable()) {
|
||||
handleRead(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("客户端运行时发生错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
private static void handleConnect(SelectionKey key, Selector selector) throws IOException {
|
||||
SocketChannel channel = (SocketChannel) key.channel();
|
||||
if (channel.isConnectionPending()) {
|
||||
channel.finishConnect();
|
||||
}
|
||||
channel.configureBlocking(false);
|
||||
channel.register(selector, SelectionKey.OP_READ);
|
||||
System.out.println("成功连接到聊天服务器!");
|
||||
}
|
||||
private static void handleRead(SelectionKey key) throws IOException {
|
||||
SocketChannel channel = (SocketChannel) key.channel();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
||||
StringBuilder content = new StringBuilder();
|
||||
try {
|
||||
int readBytes = channel.read(buffer);
|
||||
if (readBytes > 0) {
|
||||
buffer.flip();
|
||||
content.append(CHARSET.decode(buffer));
|
||||
System.out.println(content.toString());
|
||||
} else if (readBytes == -1) {
|
||||
System.out.println("服务器已关闭连接。");
|
||||
key.cancel();
|
||||
channel.close();
|
||||
System.exit(0);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println("读取服务器消息时出错,连接已断开。");
|
||||
key.cancel();
|
||||
channel.close();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
109
Final/src/nio/NioChatServer.java
Normal file
109
Final/src/nio/NioChatServer.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
public class NioChatServer {
|
||||
private static final int PORT = 8080;
|
||||
private static final String HOST = "127.0.0.1";
|
||||
private static final Charset CHARSET = Charset.forName("UTF-8");
|
||||
private static final ConcurrentHashMap<String, SocketChannel> clients = new ConcurrentHashMap<>();
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
Selector selector = Selector.open();
|
||||
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
|
||||
serverSocketChannel.bind(new InetSocketAddress(HOST, PORT));
|
||||
serverSocketChannel.configureBlocking(false);
|
||||
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
|
||||
System.out.println("聊天服务器已启动,监听端口:" + PORT);
|
||||
while (true) {
|
||||
if (selector.select() == 0) continue;
|
||||
Set<SelectionKey> selectedKeys = selector.selectedKeys();
|
||||
Iterator<SelectionKey> iterator = selectedKeys.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
SelectionKey key = iterator.next();
|
||||
iterator.remove();
|
||||
if (key.isAcceptable()) {
|
||||
handleAccept(key, selector);
|
||||
} else if (key.isReadable()) {
|
||||
handleRead(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("服务器运行时发生错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
private static void handleAccept(SelectionKey key, Selector selector) throws IOException {
|
||||
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
|
||||
SocketChannel clientChannel = serverChannel.accept();
|
||||
clientChannel.configureBlocking(false);
|
||||
SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ);
|
||||
String clientId = UUID.randomUUID().toString();
|
||||
clients.put(clientId, clientChannel);
|
||||
clientKey.attach(clientId);
|
||||
System.out.println("客户端 [" + clientChannel.getRemoteAddress() + "] 连接成功。");
|
||||
broadcastMessage("系统消息:欢迎 " + clientChannel.getRemoteAddress() + " 加入聊天室!当前在线人数:" + clients.size());
|
||||
}
|
||||
private static void handleRead(SelectionKey key) {
|
||||
SocketChannel clientChannel = (SocketChannel) key.channel();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
||||
StringBuilder content = new StringBuilder();
|
||||
try {
|
||||
int readBytes = clientChannel.read(buffer);
|
||||
if (readBytes > 0) {
|
||||
buffer.flip();
|
||||
content.append(CHARSET.decode(buffer));
|
||||
String receivedMessage = content.toString();
|
||||
String address = clientChannel.getRemoteAddress().toString();
|
||||
String broadcastMsg = "来自 " + address + " 的消息: " + receivedMessage;
|
||||
System.out.println(broadcastMsg);
|
||||
broadcastMessage(broadcastMsg, clientChannel);
|
||||
} else if (readBytes == -1) {
|
||||
disconnectClient(key);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
disconnectClient(key);
|
||||
}
|
||||
}
|
||||
private static void broadcastMessage(String message) {
|
||||
broadcastMessage(message, null);
|
||||
}
|
||||
private static void broadcastMessage(String message, SocketChannel excludeChannel) {
|
||||
for (SocketChannel channel : clients.values()) {
|
||||
if (channel.isOpen() && (excludeChannel == null || !channel.equals(excludeChannel))) {
|
||||
try {
|
||||
channel.write(CHARSET.encode(message));
|
||||
} catch (IOException e) {
|
||||
System.err.println("向客户端发送消息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void disconnectClient(SelectionKey key) {
|
||||
String clientId = (String) key.attachment();
|
||||
SocketChannel clientChannel = (SocketChannel) key.channel();
|
||||
try {
|
||||
String address = clientChannel.getRemoteAddress().toString();
|
||||
key.cancel();
|
||||
clientChannel.close();
|
||||
if (clientId != null) {
|
||||
clients.remove(clientId);
|
||||
}
|
||||
System.out.println("客户端 [" + address + "] 已断开连接。");
|
||||
broadcastMessage("系统消息:" + address + " 离开了聊天室。当前在线人数:" + clients.size());
|
||||
} catch (IOException e) {
|
||||
System.err.println("关闭客户端连接时出错: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Final/src/nio/NioHttpServer.java
Normal file
84
Final/src/nio/NioHttpServer.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
public class NioHttpServer {
|
||||
private static final int PORT = 8081;
|
||||
private static final String HOST = "127.0.0.1";
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
Selector selector = Selector.open();
|
||||
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
|
||||
serverSocketChannel.bind(new InetSocketAddress(HOST, PORT));
|
||||
serverSocketChannel.configureBlocking(false);
|
||||
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
|
||||
System.out.println("HTTP服务器已启动,监听端口:" + PORT);
|
||||
while (true) {
|
||||
if (selector.select(3000) == 0) {
|
||||
continue;
|
||||
}
|
||||
Set<SelectionKey> selectedKeys = selector.selectedKeys();
|
||||
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
|
||||
while (keyIterator.hasNext()) {
|
||||
SelectionKey key = keyIterator.next();
|
||||
keyIterator.remove();
|
||||
|
||||
if (key.isAcceptable()) {
|
||||
handleAccept(key, selector);
|
||||
}
|
||||
if (key.isReadable()) {
|
||||
handleRead(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("HTTP服务器运行时发生错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
private static void handleAccept(SelectionKey key, Selector selector) throws IOException {
|
||||
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
|
||||
SocketChannel clientChannel = serverChannel.accept();
|
||||
clientChannel.configureBlocking(false);
|
||||
clientChannel.register(selector, SelectionKey.OP_READ);
|
||||
System.out.println("接收到新的HTTP连接:" + clientChannel.getRemoteAddress());
|
||||
}
|
||||
private static void handleRead(SelectionKey key) throws IOException {
|
||||
SocketChannel clientChannel = (SocketChannel) key.channel();
|
||||
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
||||
try {
|
||||
int bytesRead = clientChannel.read(buffer);
|
||||
if (bytesRead > 0) {
|
||||
buffer.flip();
|
||||
byte[] bytes = new byte[buffer.remaining()];
|
||||
buffer.get(bytes);
|
||||
String request = new String(bytes, StandardCharsets.UTF_8);
|
||||
System.out.println("接收到HTTP请求:\n" + request);
|
||||
|
||||
String responseBody = "<h1>Hello NIO!</h1><p>这是一个基于Java NIO的HTTP服务器响应。</p>";
|
||||
String httpResponse = "HTTP/1.1 200 OK\r\n" +
|
||||
"Content-Type: text/html; charset=utf-8\r\n" +
|
||||
"Content-Length: " + responseBody.getBytes(StandardCharsets.UTF_8).length + "\r\n" +
|
||||
"\r\n" +
|
||||
responseBody;
|
||||
ByteBuffer responseBuffer = ByteBuffer.wrap(httpResponse.getBytes(StandardCharsets.UTF_8));
|
||||
clientChannel.write(responseBuffer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("处理请求时发生错误: " + e.getMessage());
|
||||
} finally {
|
||||
if (clientChannel != null) {
|
||||
clientChannel.close();
|
||||
}
|
||||
key.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user