【问题标题】:What is the best way to manage text-based client-server connections?管理基于文本的客户端-服务器连接的最佳方式是什么?
【发布时间】:2011-04-18 19:45:45
【问题描述】:

我希望编写一个基于客户端-服务器的小型文本游戏,它可以处理多个客户端连接并持续影响游戏状态。我想知道处理多个连接的最佳方法是什么,以便命令按照到达服务器的顺序进行处理。

理想情况下,我不希望利用多线程,至少在命令处理级别。我可以让每个客户端都有一个单独的线程(以便在每个线程上都有阻塞 IO),只要我之后可以在单个线程中统一处理。

由于客户端和服务器之间的唯一通信将是文本,我不确定如何最好地设置通信。如果我选择阻塞 IO,我将如何让处理在单个线程中发生?

或者,如果我选择非阻塞 IO 并使用选择器来查询客户端何时写入服务器,如何在不使用设置大小的 ByteBuffer 的情况下读取未知/无限长度的字符串?非阻塞也有利于将处理保持在单个线程中,因为它可以在客户端连接发送新数据时读取它们。但是,当我尝试使用 read/writeUTF 实现它时,我遇到了 IllegalBlockingModeException 呵呵。

任何关于如何以我未提及的方式执行此操作的问题或建议的答案将不胜感激!我对客户端和服务器还很陌生,所以我不知道 java.io 还是 java.nio 最合适。

很抱歉这个令人费解的问题。我想我自己逃跑了。

【问题讨论】:

    标签: java client mud


    【解决方案1】:

    意见不同,但我肯定会为每个客户端使用一个线程。然后,与单个处理线程的通信可以通过 LinkedBlockingQueue,或者只是一个同步的 LinkedList。

    在每个客户端线程上是这样的:

    public class Client implements Runnable, ResponseOutput {
    
        private final BufferedReader br;
        private final PrintWriter pw;
    
        public Client(Socket s) {
            br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            pw = new PrintWriter(s.getOutputStream());
        }
    
        // defined by the ResponseOutput interface
        public void sendReply(String reply) {
            pw.println(reply);
        }
    
        public void run() {
            try {
                while (true) {
                    String s = br.readLine();
                    if (s==null)
                        break;
                    Processor.queue(new Processor.InputItem(this, s));
                }
            } catch (IOException ioe) {
                ... error handling ...
            }
        }
    }
    

    然后这个进行处理:

    public class Processor implements Runnable {
        static public class InputItem {
            final ResponseOutput client;
            final String command;
    
            public InputItem(ResponseOutput client, String command) {
                this.client = client;
                this.command = command;
            }
        }
    
        static private Processor instance;
        static public void queue(InputItem item) {
            instance.commandQueue.add(item);
        }
    
        private BlockingQueue<InputItem> commandQueue;
    
        public void run() {
            try {
                while (true) {
                    InputItem item = commandQueue.take();
                    String reply = doStuff(item.command);
                    item.client.sendReply(reply);
                }
            } catch (InterruptedException ie) {
                ... error handling ....
            }
        }
    }
    

    InputItem 类中,您还可以包含对任何需要更新的游戏状态的引用。由于只有处理线程在更改它,因此您无需任何同步即可执行此操作。

    【讨论】:

    • 看起来棒极了!我会在一段时间内尝试实现它,看看它是否能解决我所有的问题,但目前它看起来很像。太棒了!
    • Sam,如果您遇到任何问题,请告诉我,我会尝试更新我的答案以解决这些问题。
    • 嗨,乔恩,我从“Input item = commandQueue.take()”收到 NullPointerException,这似乎来自 commandQueue。我有以下代码...while (true) { Input item = commandQueue.take(); String reply = parseInput(item.command); item.reply(reply); } 另外,有没有办法让服务器以非阻塞方式接受客户端?这样,在服务器端,我可以启动关闭,而无需等待客户端被接受。 public void run() { while (!shutdown) { try { acceptClient(server.accept()); } catch (...) {} } }
    • 啊哈,我的错!我需要像你提到的那样添加private BlockingQueue&lt;Input&gt; commandQueue = new LinkedBlockingQueue&lt;Input&gt;();。这是第一部分的排序。
    • Sam,你只需要在调用 server.close() 的接受线程中添加一个方法; - 这将导致您的 accept() 调用引发 IOException。
    【解决方案2】:

    我不是服务器客户端系统方面的专家,但我将分享一些技巧

    根据您的需要,您可以简单地设置一个 Tomcat 服务器并执行 http 请求,它相当简单,当然也全部使用 Java。

    缺点是请求可能有点慢。

    您可以检查的第二个选项是 RMI。

    这个概念很简单,您连接到另一台计算机,完成后您从代码中的本地对象调用另一台计算机上的方法。

    http://java.sun.com/developer/onlineTraining/rmi/RMI.html

    它可能看起来有点复杂(有时通过多台计算机调试堆栈有点棘手)但我建议这样做,因为它可以让您的代码保持清晰。

    最后你可以尝试使用套接字,但你自己:P

    【讨论】:

    • 在过去一周左右的时间里,我对 RMI 进行了一些了解(顺便说一下,这是我必须做的课程的一部分),但我认为这并不是我想要的这里有一个解决方案。套接字绝对是我想去这里的方式,但感谢您的回复。 :)
    猜你喜欢
    • 1970-01-01
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 2010-10-13
    • 2015-05-04
    • 2012-06-28
    • 2019-11-16
    • 2010-09-09
    相关资源
    最近更新 更多