最近看了很好的一篇博客: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 多线程服务器
JDK1.4提供的无阻塞I/O(NIO)有效解决了多线程服务器存在的线程开销问题,但在使用上略显得复杂一些...本文将通过一个基于事件回调的NIO多线程服务器的设计,试图提供一个简洁、直观、易于扩展的NIO多线程服务器模型。
基于事件的_NIO_多线程服务器
该包封装过的NIO比sun本身的更容易处理 server中只有区区几行就搞定了: //创建listener TimeHandler timer = new TimeHandler(); //获取Notifier Notifier notifier = Notifier.getNotifier(); //注册监听 notifier....
如果你是开发服务器端的程序,java nio 是一个很好的选择,流I/O的效率大家是知道的
实现功能:基于HTTP协议,解析请求和拼接响应,基于NIO的非阻塞,线程池,文件传输。代码有详细注释和清晰的框架。 程序入口是: /HttpServerReactor/src/com/StartServer.Java 访问1,浏览:...
在本篇文章里小编给大家整理的是关于Java实现基于NIO的多线程Web服务器实例内容,需要的朋友们可以学习下。
客户端,套接字通道>由多个客户端共享客户输入和侦听线程共享LinkedBlockingQueue ConcurrentHashMap 与上面相同Nio工具内部多线程安全异常处理一般的对于一般功能,抛出异常对于来电者,尝试抓住服务器第一要务:...
28 基于事件的NIO多线程服务器.mht 29 驯服 Tiger 并发集合.mht 30 Java5 多线程实践.mht 31 Java 理论与实践 并发集合类.mht 32 Java 理论与实践 构建一个更好的 HashMap.mht 33 Java 理论与实践 JDK 5_0 中更...
使用java NIO的方式实现服务器群聊功能,可以检测到客户端上下线,多个客户端之间的群聊功能。采用了Reactor单线程方式实现。
基于 NIO 的单线程聊天服务器的 Java 实现,可防御来自多线程聊天客户端的 DoS 攻击。 为了比较,还实现了一个没有 DoS 防御的聊天服务器和一个良性的聊天客户端。 裸聊服务器的架构 DoS 攻击时崩溃 视频演示 [!...
// 还需要一个循环事件,执行事件监听,此处可能需要使用到多线程编程 // 设置接收链接方式 serverSocket.setConnectionAcceptor(ConnectionAcceptor.ALLOW); while (true) { // 循环不断监听...
Netty是一个异步的事件驱动的网络编程模型框架,使用Java NIO构建了Reactor模型,该模型是一种具有优良扩展性和性能的非阻塞异步模式,它同步等待多个I/O事件的到达,对其进行多路分离,派发给工作线程。Netty包括一...
I / O选择器是基于“React器”的事件循环的核心,并监视多个I / O对象以进行各种类型的准备工作,例如,准备好进行读取或写入。 使用nio4r的项目 :Rails 5 WebSocket协议,将nio4r用于WebSocket服务器 oid:基于...
阻塞的原因在于:操作系统允许的线程数量是有限的,多个 socket 申请与服务端建立连接时,服务端不能提供相应数量的处理线程,没有分配到处理线程的连接就会阻塞等待或被拒绝。 BIO方式适用于连接数目比较小且固定的...
NIO 的特点:事件驱动模型、单线程处理多任务、非阻塞 I/O,I/O 读写不再阻塞,而是返 回 0、基于 block 的传输比基于流的传输更高效、更高级的 IO 函数 zero-copy、IO 多路复用 大大提高了 Java 网络应用的可伸缩性...
让应用服务器可以极大的优化线程模型,相比传统的阻塞式IO线程和链路一对一的模式,NIO只需少量的线程即可处理所有的链路,这对广大的java开发者来说是一大福音,基于NIO能开发出更高效的网络应用。 然而使用JDK...
(1),NioEventLoopGroup是一个处理I / O操作的多线程事件循环。 Netty为不同类型的传输提供各种EventLoopGroup实现。我们在此示例中实现了服务器端应用程序,因此将使用两个NioEventLoopGroup。第一个,通常称为...
如果客户端数量增加,则将在服务器上创建太多线程。 因此,将创建使用NIO的此应用程序的增强版本,其中创建的线程数将取决于负载,而不是连接数。 同样,当前该应用程序还基于推送机制,即,一个客户端发布的更改被...