【问题标题】:Best practice for reading / writing to a java server socket读取/写入 Java 服务器套接字的最佳实践
【发布时间】:2012-04-25 08:17:11
【问题描述】:

如何设计一个在单个套接字上运行的读写循环(支持并行读写操作)?我必须使用多个线程吗?我的(java)解决方案有什么好处吗?那 sleep 命令呢?你如何在这样的循环中使用它?

我正在尝试使用 2 个线程:

阅读

public void run() {
    InputStream           clientInput;
    ByteArrayOutputStream byteBuffer;
    BufferedInputStream   bufferedInputStream;
    byte[]                data;
    String                dataString;
    int                   lastByte;

    try {
        clientInput         = clientSocket.getInputStream();
        byteBuffer          = new ByteArrayOutputStream();
        bufferedInputStream = new BufferedInputStream(clientInput);

        while(isRunning) {  

            while ((lastByte = bufferedInputStream.read()) > 0) {
                byteBuffer.write(lastByte);
            }
                data       = byteBuffer.toByteArray();  
                dataString = new String(data);
                byteBuffer.reset();     
        }   

    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void run() {
    OutputStream clientOutput;
    byte[]       data;
    String       dataString;

    try {
        clientOutput = clientSocket.getOutputStream();

        while(isOpen) { 

            if(!commandQueue.isEmpty()) {
                dataString = commandQueue.poll();
                data       = dataString.getBytes();
                clientOutput.write(data);
            }                   
            Thread.sleep(1000);
        }       
        clientOutput.close();           
    } 
    catch (IOException e) {
        e.printStackTrace();
    } 
    catch (InterruptedException e) {
        e.printStackTrace();
    }
}

读取无法提供正确的结果,因为没有发送 -1。 我该如何解决这个问题?

这个睡眠/写入循环是一个好的解决方案吗?

【问题讨论】:

  • 这是 Java 吗?请标记语言
  • 这是java,但这更多是一个设计问题,与任何java特性本身无关
  • 是的,但它使问题更具体

标签: java sockets networking


【解决方案1】:

网络 I/O 基本上有三种方式:

  1. 阻止。在这种模式下,读取和写入将阻塞,直到它们可以完成,因此如果您想同时执行这两个操作,您需要为每个单独的线程。

  2. 非阻塞。在这种模式下,读写将返回零(Java)或在某些语言(C)中,当它们无法完成时,会返回状态指示(返回 == -1,errno=EAGAIN/EWOULDBLOCK),因此您不需要单独的线程,但是您确实需要第三个 API 来告诉您何时可以完成操作。这就是select() API 的用途。

  3. 异步 ​​I/O,您可以在其中安排传输并返回某种句柄,您可以通过该句柄询问传输状态,或者在更高级的 API 中,回调。

您应该当然永远不要使用您在此处使用的while (in.available() > 0)/sleep() 样式。 InputStream.available() 很少有正确的用途,这不是其中之一,睡眠实际上是在浪费时间。数据可以在休眠时间内到达,正常的read()会立即唤醒。

【讨论】:

  • 您是否介意比较您的第一个和第二个解决方案 - 您如何选择您的方法?我的示例看起来像您的第二种方法 - 您如何评价我的解决方案?
  • @Schifty 这取决于您一次需要处理多少个连接。 select() 函数是在线程之前开发的,此时另一个连接意味着另一个进程。线程是轻量级进程,所以有一个论点说你这些天永远不需要使用select(),还有一个论点说使用select()意味着你正在将操作系统的线程调度开销转换为in-过程循环。在历史上的这个时刻,我会使用至少 10,000 个连接的线程,也许 100,000 个,具体取决于平台。
【解决方案2】:

您应该在需要时使用布尔变量而不是 while(true) 来正确关闭线程。同样是的,您应该创建多个线程,每个连接的客户端一个,因为线程将自己阻塞,直到接收到新数据(例如 DataInputStream().read() )。不,这不是一个真正的设计问题,每个库/框架或语言都有自己的方式从套接字监听,例如从 Qt 中的套接字监听你应该使用所谓的“信号和插槽”,而不是无限循环。

【讨论】:

  • DataInputStream().read() 不会阻塞,因为 .available > 0
  • @Schifty 但是你没有调用 available(),你不应该。
猜你喜欢
  • 2015-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-01
  • 2020-08-15
  • 2015-07-26
  • 2011-04-13
  • 1970-01-01
相关资源
最近更新 更多