最近看了很好的一篇博客:http://www.ibm.com/developerworks/cn/java/l-niosvr/
于是我按照作者给的源码做实验(源码以上传到附件中NIOServer.jar),我模拟发送1000次消息,服务器做出一千次响应。
public static void main(String args[]) { Socket client = null; DataOutputStream out = null; DataInputStream in = null; try { for(int i=0;i<CLIENT_NUM;i++){ client = new Socket("10.13.30.160", 5100); client.setSoTimeout(10000); out = new DataOutputStream( (client.getOutputStream())); String query = "GB"; byte[] request = query.getBytes(); out.write(request); out.flush(); client.shutdownOutput(); in = new DataInputStream(client.getInputStream()); byte[] reply = new byte[40]; in.read(reply); System.out.println("Time: " + new String(reply, "GBK")); in.close(); out.close(); client.close(); } } catch (Exception e) { e.printStackTrace(); System.out.println(e.getMessage()); } } }
但是在运行过程中抛出了异常:
java.io.IOException: 您的主机中的软件放弃了一个已建立的连接。
经过我调试代码,发现是在Response类的send(byte[] data)中SocketChannel调用write(byte[])的时候出错了。
我在上面的测试代码的System.out.println("Time: " + new String(reply, "GBK"));之后让其Thread.sleep(200);一会儿发现就不会出现上面的放弃建立连接的异常了。
我在想难道是服务器发送数据的时候,客户端已经关闭了?所以我们让客户端在关闭之前Thread.sleep(200);延迟一会儿等服务器发消息发完?
我查了一下Connect reset的原因,具体可以参考:http://wjy320.iteye.com/blog/2069518
应该可以断定是服务端发送数据的时候,客户端已经关闭了(调用了close());但是客户端中的 in.read(reply)应该会阻塞呀,会一直等到服务端发来的数据后才返回呀,难道是in.read(byte[])是非阻塞的?于是好奇心促使我看了一下DataInputStream和InputStream的read(byte[])的源码。
//DataInputStream的read(byte[])源码 public final int read(byte b[]) throws IOException { return in.read(b, 0, b.length); } //其中调用了InputStream的read(byte b[], int off, int len);InputStream的read(byte b[], int off, int len)的源代码如下: public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; }
我稍微对InputStream的read(...)进行一下说明:
从所包含的输入流中读取一定数量的字节,并将它们存储到缓冲区数组 b 中。以整数形式返回实际读取的字节数。在输入数据可用、检测到文件末尾 (end of file) 或抛出异常之前,此方法将一直阻塞。 如果 b 为 null,则抛出 NullPointerException。如果 b 的长度为零,则不读取字节并返回 0;否则试图读取至少一个字节。如果因为该流在文件末尾而无字节可用,则返回值 -1;否则至少读取一个字节并将其存储到 b 中。 将读取的第一个字节存储到元素 b[0] 中,将下一个字节存储到 b[1] 中,依此类推。读取的字节数至多等于 b 的长度。设 k 为实际读取的字节数;这些字节将存储在从 b[0] 到 b[k-1] 的元素中,b[k] 到 b[b.length-1] 的元素不受影响。 如果因为文件末尾以外的其他原因而无法读取第一个字节,则抛出 IOException。尤其在输入流已关闭的情况下,将抛出 IOException。
顺便说一句,read()每次读一个byte。
由此可见read(...)返回的原因有三个:
1:给定的参数不合法。
2:读到了文件末尾(-1) ps:一方关闭也会发送-1。
3:缓冲区(就是那个byte[])被写满。
所以应该不是read(byte[])非阻塞的原因。
看到第三条,我恍然大悟。难道是我的缓冲区开辟小了,我只开辟了40个byte;而服务器返回的信息是:“Time: 2014年5月22日 星期四 上午11时24分58秒 CST”。长度却是41个byte。所以每次read(...)返回的原因都是缓冲区被写满,而不是服务端发送完毕的文件结尾。那么第二次读的时候操作系统的缓冲区中本身就有一个byte的数据。所以在第二次读数据的时候可能服务端还没有发送完毕,但是客户端因为缓冲区写满就已经关闭了。所以服务器端会报出“放弃一个已经建立的连接”的异常。
所以客户端每条显示少了最后一个"T",但是肯定能正常显示,不会报异常。而服务器有时候会报出以上异常,跟时机有关。
所以解决办法就是将客户端的缓冲区开辟大一些就行了,开辟41个byte就行了,服务端就不会报错了。
相关推荐
基于事件的 NIO 多线程服务器
### 基于事件的NIO多线程服务器解析 #### 概述 在Java的网络编程中,NIO(Non-blocking I/O)作为一种高效的数据处理模式,自JDK 1.4版本引入以来,逐渐成为了开发高性能网络应用的重要工具之一。与传统的阻塞I/O...
【事件回调机制】在基于事件的NIO多线程服务器设计中,事件回调机制是核心组件之一。事件回调允许服务器在特定事件发生时调用预定义的处理函数,以执行相应的业务逻辑。这种方式使得代码更加模块化,易于扩展和维护...
### 基于时间的NIO多线程服务器——深入解析与关键技术点 #### 引言 在服务器端编程领域,随着互联网应用的不断发展,如何高效处理大量的并发连接成为了一个重要议题。Java NIO(非阻塞I/O)作为一种先进的I/O处理...
该包封装过的NIO比sun本身的更容易处理 server中只有区区几行就搞定了: //创建listener TimeHandler timer = new TimeHandler(); //获取Notifier Notifier notifier = Notifier.getNotifier(); //注册监听 notifier....
总结来说,"Nio多线程CS收发信息问题"涉及到在Java NIO环境下,如何构建一个多线程、高并发的客户端-服务器通信系统,并解决在这个过程中可能遇到的各种挑战。通过理解NIO的核心概念,合理设计线程管理和异常处理...
构建一个基于NIO的多线程服务器,主要步骤如下: 1. **创建ServerSocketChannel**: 首先,我们需要创建一个`ServerSocketChannel`,它是服务器端接收客户端连接的入口。通过`ServerSocketChannel.open()`方法初始...
Java NIO服务器的多线程设计有助于提高服务器的并发性能,特别是在高并发场景下,可以有效地利用系统资源,避免大量线程导致的内存消耗和上下文切换开销。同时,通过选择器的使用,减少了对主线程的占用,使得服务器...
Java实现基于NIO的多线程Web服务器实例 Java实现基于NIO的多线程Web服务器实例是使用Java语言基于NIO(New I/O)技术实现的多线程Web服务器。NIO技术可以提供高性能、低延迟的I/O操作,非常适合高并发的Web服务器。...
实现功能:基于HTTP协议,解析请求和拼接响应,基于NIO的非阻塞,线程池,文件传输。代码有详细注释和清晰的框架。 程序入口是: /HttpServerReactor/src/com/StartServer.Java 访问1,浏览:...
【标题】基于多线程的Web服务器Java源码解析 在Web开发领域,服务器是至关重要的组成部分,它们负责处理客户端的HTTP请求并返回相应的HTTP响应。本篇文章将深入探讨一个基于多线程的Web服务器Java源码,帮助你理解...
3. **Netty的事件驱动模型**:Netty采用 reactor 模式,通过EventLoopGroup来管理事件循环线程,每个线程负责处理多个连接的事件。当有新连接、读写事件发生时,会触发相应的ChannelHandler进行处理。 4. **Netty的...
6. **性能优化**:为了最大化效率,多线程服务器可能采用各种策略,如使用非阻塞I/O(NIO)以减少线程等待时间,或者利用异步事件驱动模型(如Reactor模式)来减少线程上下文切换的开销。 然而,多线程模型并非没有...
1. **Selector(选择器)**:选择器允许单个线程检查多个通道(Channels)上的事件,如连接建立、数据到达等。通过注册通道到选择器,并设置感兴趣的事件类型,我们可以避免创建大量线程来处理每个连接,从而提高...
在IT领域,尤其是在服务器开发中,多线程技术扮演着至关重要的角色,特别是在构建高性能的Web服务器时。本文将深入探讨多线程Web服务器的概念、Java中的Socket编程以及如何通过实现Runnable接口来创建多线程。 多...
本主题“基于nio实现的多文件上传源码”探讨的是如何利用Java NIO来实现高效的多文件上传功能,尤其对于小文件数量较大的情况。 首先,理解NIO的基本概念是必要的。NIO中的“非阻塞”意味着当数据不可用时,读写...
在这个“多线程精品资源--Java NIO+多线程实现聊天室”的压缩包中,我们可以推测它包含了一套关于如何使用Java NIO和多线程技术来创建一个实时聊天应用的教程或示例代码。 首先,多线程是Java中并行处理的基础。...
这个文件中包含了一个基于NIO的服务器端实现。服务器的核心组件是`Selector`,它允许一个单独的线程监听多个套接字通道的状态变化。当客户端发起连接请求时,服务器会注册`ServerSocketChannel`到`Selector`上,等待...