從0開始 3.8 非阻塞式IO的聊天室 - MUD Game

Elvira avatar
By Elvira
at 2020-06-13T09:57

Table of Contents


嗨~~ g大

想請問這個系列大作還會有繼續更新的機會嗎?

(敲碗~~~~~)

另外,我把您的相關文章都收精華區 (如下) 囉!

我在哪? z-7-3-3
mud
7. ◆ ω 版友討論與心得 ω
3. ◆ /■ ※版 友 專 區※
3. ◆ gasbomb 專區 [gasbomb]

歡迎您繼續跟大家分享您的寶貴經驗了!

感謝您!



※ 引述《gasbomb (虛空雷神獸)》之銘言:
: 之前實作的聊天室由於使用了阻塞式的 IO
: 在等待使用者輸入指令時整個執行緒都必須暫停
: 所以說線上有幾個使用者就等於我們要同時開啟幾條執行緒
: 這是非常浪費資源的
: 在後來的 java 版本有提供了非阻塞式的 IO
: 讓我們可以只用一條執行緒就可以應付許多連線
: 這次就使用 AsynchronousServerSocketChannel 來實作聊天室 (簡稱 AIO)
: 以下就是聊天室的程式碼
: 由於 AIO 有非常多的細節, 但是我們的目的是要開發 MUD
: 因此這邊我不打算解釋的太詳細
: // GeneralAioEchoServer.java
: // ✂--------------請沿虛線剪下--------------
: package test;
: import java.io.ByteArrayOutputStream;
: 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.HashSet;
: import java.util.LinkedList;
: import java.util.Queue;
: import java.util.Set;
: import java.util.concurrent.*;
: public class GeneralAioEchoServer {
: private AsynchronousServerSocketChannel assc;
: private Set<AsynchronousSocketChannel> users = new HashSet<>();
: public static void main(String[] args) throws Exception {
: GeneralAioEchoServer server = new GeneralAioEchoServer();
: server.start();
: // AIO 因為不會阻塞, 所以必須要有無限迴圈來維持 main thread
: while (true) {
: Thread.sleep(5000L);
: }
: }
: // 建立連線池, 設定 server port, 啟動
: private void start() throws IOException {
: ExecutorService pool = Executors.newSingleThreadExecutor();
: AsynchronousChannelGroup channelGroup =
: AsynchronousChannelGroup.withThreadPool(pool);
: assc = AsynchronousServerSocketChannel.open(channelGroup);
: assc.bind(new InetSocketAddress(4000));
: // 設定 callback method
: assc.accept(null, new AcceptHandler());
: }
: private class AcceptHandler implements
: CompletionHandler<AsynchronousSocketChannel, Object> {
: @Override
: public void completed(AsynchronousSocketChannel asc, Object o) {
: assc.accept(null, this);
: try {
: asc.write(StandardCharsets.UTF_8.encode(
: "歡迎來到 aio telnet chat server\r\n")).get();
: } catch (InterruptedException | ExecutionException e) {
: e.printStackTrace();
: }
: ByteBuffer bb = ByteBuffer.allocate(1024);
: asc.read(bb, null, new ReadHandler(asc, bb));
: users.add(asc);
: }
: @Override
: public void failed(Throwable throwable, Object o) {
: }
: }
: private class ReadHandler implements CompletionHandler<Integer, Object> {
: private AsynchronousSocketChannel asc;
: private ByteBuffer bb;
: private MyByteArrayOutputStream byteArrayOutputStream =
: new MyByteArrayOutputStream();
: private boolean firstChar = true;
: private Queue<String> inputs = new LinkedList<>();
: public ReadHandler(AsynchronousSocketChannel asc, ByteBuffer bb) {
: this.asc = asc;
: this.bb = bb;
: }
: @Override
: public void completed(Integer result, Object o) {
: if (result == -1) return;
: // 逐 byte 讀取玩家輸入的字元
: byte[] bytes = new byte[result];
: bb.flip().get(bytes).clear();
: for (byte b : bytes) {
: switch (b) {
: case '\n':
: if (firstChar) {
: firstChar = false;
: continue;
: }
: case '\r':
: inputs.offer(
: new String(byteArrayOutputStream.toByteArray(),
: StandardCharsets.UTF_8));
: byteArrayOutputStream.reset();
: firstChar = true;
: continue;
: case 127:
: byteArrayOutputStream.backspace();
: continue;
: default:
: byteArrayOutputStream.write(b);
: firstChar = false;
: }
: }
: try {
: while (!inputs.isEmpty()) {
: String message = inputs.poll() + "\r\n";
: for (AsynchronousSocketChannel user : users) {
: user.write(StandardCharsets.UTF_8.encode(message)).get();
: }
: }
: } catch (InterruptedException | ExecutionException e) {
: e.printStackTrace();
: }
: asc.read(bb, null, this);
: }
: @Override
: public void failed(Throwable throwable, Object o) {
: }
: }
: // 繼承ByteArrayOutputStream 實作 backspace 的功能
: private static class MyByteArrayOutputStream extends ByteArrayOutputStream {
: public void backspace() {
: if (count > 0) count--;
: }
: }
: }
: // ✂--------------請沿虛線剪下--------------
: 如此一來, 更輕量化的聊天室就完成了
: 下一次我們會開始實作登入系統

--
Tags: 線上

All Comments

亂世群雄 Heroes' Story 懷舊再開~

Leila avatar
By Leila
at 2020-06-13T09:05
阿華及大家早安呀! 一早上站就看到這個好消息,請老玩家們在這段期間 (試用期? XD)好好遊玩吧! 另外,msr大或clode大有剛好來本版看到的話,能否懇請您們也更新一下 在RW上面亂世群雄的連線IP呢? 這樣應能讓有在關注亂世的玩家發現now大已重新開啟了! 再次感謝囉~ :D 版工 T ...

亂世群雄 Heroes' Story 懷舊再開~

Catherine avatar
By Catherine
at 2020-06-13T07:33
嗚嗚 …真的非常謝謝通知這個消息 也感謝NOW大了 如果可以希望能開更久一點 無論如何 再次謝謝你們 ※ 引述《munak (阿華)》之銘言 : ※ 引述《jademan (大玉兒小玉人)》之銘言: : : 請問亂世群雄目前還有開嗎 ? : : 感謝~ : : ※ 引述《munak (阿華)》之銘言 : : ...

卡片等級從八等變七等?

Emily avatar
By Emily
at 2020-06-13T01:16
※ 引述《NAGI (阿魯馬其頓)》之銘言: : 今天早上將黑卡火哥從七等升到八等 : 結果剛剛登入後發現突然又變回七等 : 有人也遇過類似狀況嗎? 回自己的文 從6號回報到現在完全沒有修 永遠都是回覆處理中 想問問有人的卡片等級已經正常了嗎? - ...

亂世群雄 Heroes' Story 懷舊再開~

Kama avatar
By Kama
at 2020-06-13T01:15
※ 引述《jademan (大玉兒小玉人)》之銘言: : 請問亂世群雄目前還有開嗎 ? : 感謝~ : ※ 引述《munak (阿華)》之銘言 : : IP:220.135.206.112 : : PORT:7878 : : 感謝所有熱愛亂世的人們所做的一切努力,讓我們有了許多好好壞壞的種種回憶, : : N ...

關於MUD的歷史緣由 (Youtube)

Linda avatar
By Linda
at 2020-06-12T23:56
嗨! 各位版友本週過得如何呢? 大家一整週忙碌於家庭、工作或是課業上,週末請好好放鬆囉~~ 平常就有在上下班搭車時看Youtube殺時間的習慣, 接任版主後更是會注意與MUD有關的影片 (雖然目前不多了...QQ)。 比較多影片是邊玩某MUD邊介紹或是剪接比較無聊的~~ 近日剛好搜尋到一個介紹MUD ...