java nio 传统标准io socket 和nio socket比较与学习
在计算机系统中,最不可靠的就是网络请求,我们通过服务器端给客户端echo信息(客户端请求什么信息服务端就返回给客户端什么信息)。比较两种socket io的优劣。
标准io socket:
服务器端代码:
主线程负责不断地请求echoServer.accept(),如果没有客户端请求主线程会阻塞,当有客户端请求服务器端时,主线程会用线程池新创建一个线程执行。也就是说一个线程负责一个客户端socket,当一个客户端socket因为网络延迟时,服务器端负责这个客户端的线程就会等待,浪费资源。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.Buffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 常规的socket服务端,服务器端采用一个线程接受一个客户端来处理。* Created by chenyang on 2017/3/26.*/
public class MultiThreadEchoServer {private static ExecutorService tp= Executors.newCachedThreadPool();static class HandleMsg implements Runnable{Socket clientSocket;public HandleMsg(Socket clientSocket) {this.clientSocket = clientSocket;}@Overridepublic void run() {BufferedReader is=null;PrintWriter os=null;try {is=new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));os=new PrintWriter(clientSocket.getOutputStream(),true);//从InputStream当中读取客户端所发送的数据String inputLine=null;long b=System.currentTimeMillis();while ((inputLine=is.readLine())!=null){os.println(inputLine);}long e=System.currentTimeMillis();System.out.println("spend:"+(e-b)+"ms");}catch (IOException e){e.printStackTrace();}finally {try {if(is!=null) is.close();if(os!=null) os.close();clientSocket.close();}catch (IOException ex){ex.printStackTrace();}}}}public static void main(String[] args) {ServerSocket echoServer=null;Socket clientSocket=null;try {echoServer=new ServerSocket(8000);}catch (IOException e){System.out.println(e);}while (true){try {clientSocket =echoServer.accept();//阻塞System.out.println(clientSocket.getRemoteSocketAddress()+" connect!"+System.currentTimeMillis());
//子线程负责执行与client socket 交互的操作。tp.execute(new HandleMsg(clientSocket));}catch (IOException e){System.out.println(e);}}}
}
客户端代码:
主线程创建10个子线程去请求server:这是个模拟网络拥堵时的客户端socket,每打一个字符就会停1秒。这样服务端的线程也要等待,这样服务器端的资源浪费的就很多。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.LockSupport;/*** 传统IO下,模拟10个网络不好的客户端同时访问server.* Created by chenyang on 2017/4/8.*/
public class HeavyThreadEchoClient {static ExecutorService es= Executors.newCachedThreadPool();static Long sleep_time=1000*1000*1000L;public static class EchoClient implements Runnable{@Overridepublic void run() {Socket client=null;PrintWriter writer=null;BufferedReader reader=null;try {client=new Socket();client.connect(new InetSocketAddress("localhost",8000));writer=new PrintWriter(client.getOutputStream(),true);writer.print("h");LockSupport.parkNanos(sleep_time);writer.print("e");LockSupport.parkNanos(sleep_time);writer.print("l");LockSupport.parkNanos(sleep_time);writer.print("l");LockSupport.parkNanos(sleep_time);writer.print("o");LockSupport.parkNanos(sleep_time);writer.print("!");LockSupport.parkNanos(sleep_time);writer.println();writer.flush();reader=new BufferedReader(new InputStreamReader(client.getInputStream()));System.out.println("from server:"+reader.readLine());}catch (UnknownHostException ex){ex.printStackTrace();}catch (IOException e){e.printStackTrace();} finally {if(writer!=null){writer.close();}if(reader!=null){try {reader.close();}catch (IOException ex){ex.printStackTrace();}}if(client!=null){try {client.close();}catch (IOException ex){ex.printStackTrace();}}}}}public static void main(String[] args) {EchoClient ec=new EchoClient();for(int i=0;i<10;i++){es.execute(ec);}}
}
spend:6023ms
spend:6024ms
spend:6024ms
spend:6025ms
spend:6025ms
spend:6026ms
spend:6027ms
spend:6028ms
都有6秒的延迟,这都是网络io等待时间造成的。
nio socket:
通过事件通知的机制,当数据准备好了才会通知服务器端线程进行读写,避免了网络io等待。
服务端多线程的结构示意图:
一个线程控制一个selector,一个selector可以轮询多个客户端的channel,这样服务器端线程不用等待网络io,只会处理准备好的数据。
服务器端代码:
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** Created by chenyang on 2017/4/8.*/
public class MultiThreadNIOEchoServer {public static Map<Socket,Long> geym_time_stat=new HashMap<Socket,Long>(10240);class EchoClient{private LinkedList<ByteBuffer> outq;EchoClient(){outq=new LinkedList<ByteBuffer>();}//return the output queuepublic LinkedList<ByteBuffer> getOutputQueue(){return outq;}//enqueue a ByteBuffer on the output queue.public void enqueue(ByteBuffer bb){outq.addFirst(bb);}}class HandleMsg implements Runnable{SelectionKey sk;ByteBuffer bb;public HandleMsg(SelectionKey sk, ByteBuffer bb) {this.sk = sk;this.bb = bb;}@Overridepublic void run() {EchoClient echoClient=(EchoClient)sk.attachment();echoClient.enqueue(bb);//we've enqueued data to be written to the client,we must//not set interest in OP_WRITEsk.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);selector.wakeup();}}private Selector selector;private ExecutorService tp= Executors.newCachedThreadPool();/*accept a new client and set it up for reading*/private void doAccept(SelectionKey sk){ServerSocketChannel server=(ServerSocketChannel)sk.channel();SocketChannel clientChannel;try {//获取客户端的channelclientChannel = server.accept();clientChannel.configureBlocking(false);//register the channel for readingSelectionKey clientKey=clientChannel.register(selector,SelectionKey.OP_READ);//Allocate an EchoClient instance and attach it to this selection key.EchoClient echoClient=new EchoClient();clientKey.attach(echoClient);InetAddress clientAddress=clientChannel.socket().getInetAddress();System.out.println("Accepted connetion from "+clientAddress.getHostAddress()+".");}catch (Exception e){System.out.println("Failed to accept new client");e.printStackTrace();}}private void doRead(SelectionKey sk){SocketChannel channel=(SocketChannel)sk.channel();ByteBuffer bb=ByteBuffer.allocate(8192);int len;try {len=channel.read(bb);if(len<0){disconnect(sk);return;}}catch (Exception e){System.out.println("Fail to read from client");e.printStackTrace();disconnect(sk);return;}bb.flip();tp.execute(new HandleMsg(sk,bb));}private void doWrite(SelectionKey sk){SocketChannel channel=(SocketChannel)sk.channel();EchoClient echoClient=(EchoClient)sk.attachment();LinkedList<ByteBuffer> outq=echoClient.getOutputQueue();ByteBuffer bb=outq.getLast();try {int len=channel.write(bb);if(len==-1){disconnect(sk);return;}if(bb.remaining()==0){outq.removeLast();}}catch (Exception e){e.printStackTrace();System.out.println("fail to write to client");disconnect(sk);}if(outq.size()==0){sk.interestOps(SelectionKey.OP_READ);}}private void disconnect(SelectionKey sk){SocketChannel sc=(SocketChannel)sk.channel();try {sc.finishConnect();}catch (IOException e){}}private void startServer() throws Exception{//声明一个selectorselector= SelectorProvider.provider().openSelector();//声明一个server socket channel,而且是非阻塞的。ServerSocketChannel ssc=ServerSocketChannel.open();ssc.configureBlocking(false);// InetSocketAddress isa=new InetSocketAddress(InetAddress.getLocalHost(),8000);//声明服务器端的端口InetSocketAddress isa=new InetSocketAddress(8000);//服务器端的socket channel绑定在这个端口。ssc.socket().bind(isa);//把一个socketchannel注册到一个selector上,同时选择监听的事件,SelectionKey.OP_ACCEPT表示对selector如果//监听到注册在它上面的server socket channel准备去接受一个连接,或 有个错误挂起,selector将把OP_ACCEPT加到//key ready set 并把key加到selected-key set.SelectionKey acceptKey=ssc.register(selector,SelectionKey.OP_ACCEPT);for(;;){selector.select();Set readyKeys=selector.selectedKeys();Iterator i=readyKeys.iterator();long e=0;while (i.hasNext()){SelectionKey sk=(SelectionKey)i.next();i.remove();if(sk.isAcceptable()){doAccept(sk);}else if(sk.isValid()&&sk.isReadable()){if(!geym_time_stat.containsKey(((SocketChannel)sk.channel()).socket())){geym_time_stat.put(((SocketChannel)sk.channel()).socket(),System.currentTimeMillis());doRead(sk);}}else if(sk.isValid()&&sk.isWritable()){doWrite(sk);e=System.currentTimeMillis();long b=geym_time_stat.remove(((SocketChannel)sk.channel()).socket());System.out.println("spend"+(e-b)+"ms");}}}}public static void main(String[] args) {MultiThreadNIOEchoServer echoServer=new MultiThreadNIOEchoServer();try {echoServer.startServer();}catch (Exception e){e.printStackTrace();}}
}
同样的客户端代码测试nio的服务器端结果:
spend8ms
spend10ms
spend11ms
spend15ms
spend7ms
spend7ms
spend6ms
spend6ms
spend6ms
spend8ms
几乎没有多少延迟。
总结:
nio在数据准备好后,再交由应用进行处理,数据的读写过程仍在应用线程中。也就是说应用线程不用再等待网络io了,准备好了读写还是要处理的。
节省的数据准备时间(因为selector可以多个channel复用)
java nio 传统标准io socket 和nio socket比较与学习相关推荐
- 以Java的视角来聊聊BIO、NIO与AIO的区别
说一说I/O 首先来说一下什么是I/O? 在计算机系统中I/O就是输入(Input)和输出(Output)的意思,针对不同的操作对象,可以划分为磁盘I/O模型,网络I/O模型,内存映射I/O, Dir ...
- 重拾Java基础知识:IO流
I0流 前言 字节流 InputStream OutputStream 字符流 Reader Writer 缓存流 转换流 序列化流 数据流 字节数组流 打印流 校验流 数据压缩 ZIP压缩和解压 G ...
- java nio 阻塞_Java NIO和传统阻塞IO
传统的阻塞IO我就不多说,因为之前我发过几篇使用传统阻塞IO做QQ聊天写的几篇文章,服务器端每得到一个新的连接就开一个线程,在这个线程中接收数据方向是阻塞的, 这种方式对性能的开销是非常大的,总结 ...
- Java传统的io和nio区别_Java中IO和NIO的本质和区别
简介 终于要写到java中最最让人激动的部分了IO和NIO.IO的全称是input output,是java程序跟外部世界交流的桥梁,IO指的是java.io包中的所有类,他们是从java1.0开始就 ...
- Java的IO:BIO | NIO | AIO
原文: http://my.oschina.net/bluesky0leon/blog/132361 BIO | NIO | AIO,本身的描述都是在Java语言的基础上的.而描述IO,我们需要从两个 ...
- 尚硅谷java——NIO(New IO)
Java NIO(New IO或 Non Blocking IO)是从java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API.NIO支持面向缓冲区的.基于通道的IO操 ...
- java 非阻塞_Java之NIO(非阻塞IO)
[1]NIO的与IO的区别: 总的来说java 中的IO 和NIO的区别主要有3点: 1)IO是面向流的,NIO是面向缓冲的: 2)IO是阻塞的,NIO是非阻塞的: 3)IO是单线程的,NIO 是通过 ...
- 【学习笔记】JAVA IO与NIO(new IO)的对比与不同IO模型的理解
JAVA IO 分类: 几种IO 模型 1. 阻塞 IO 模型 2. 非阻塞 IO 模型 JAVA NIO 多路复用 IO 模型(即Java中的NIO) JAVA IO 思维导图: 分类: 按照流的方 ...
- io 错误: socket closed_Tomcat NIO(9)IO线程Overall流程和关键类
在上一篇文章里我们主要介绍了 tomcat NIO 中 poller 线程的阻塞与唤醒,根据以前文章当 poller 线程监测到连接有数据可读事件的时候,会把原始 socket 的包装对象委托到 to ...
最新文章
- 序列比对-BLAST
- beeline执行sql语句_由“Beeline连接HiveServer2后如何使用指定的队列(Yarn)运行Hive SQL语句”引发的一系列思考...
- js替换数组中字符串实例
- 2020中国男士美妆市场洞察报告
- 居然之家完成35亿定增 小米、阿里等认购
- 项目管理 计算机仿真,刘宝林老师【项目管理】《挑战埃及》沙盘课程内容分享...
- 智慧社区电商后台管理系统
- 人工智能的发展历史概览
- 安装docker多系统操作示列(window为例)
- 三维重建 建立客观世界的虚拟现实||时空克隆 三维视频融合 投影融合 点卯 魔镜系列
- Xbrowser远程登录Ubuntu闪退问题的解决方案
- 从白嫖百度AI Studi Telsa V100 32G训练资源到使用tensorflow训练全流程(个人用)
- 计算机分区的优点,NTFS分区格式的优点及其转换
- 阿里巴巴矢量图标库icon图标在线引用
- 5e服务器显示fps被锁定,csgo强制被锁60帧 被锁60fps解决方法
- yolov5 6.0版本->onnx->ncnn +安卓部署 附加ncnn环境配置 保姆级详细教程
- Maven-Archetype Catalog
- JAVA计算机毕业设计二手手机回收平台系统Mybatis+源码+数据库+lw文档+系统+调试部署
- 2014 -- ISO 面试题
- 基于JAVA设计师品牌服装租赁网站计算机毕业设计源码+数据库+lw文档+系统+部署