NIO系列-03-NIO

来源:互联网 发布:软件项目立项过程 编辑:程序博客网 时间:2024/05/20 10:23

声明

该系列文章由书籍《Netty权威指南》第二版整理而来。只为记录学习笔记。
若认为内容侵权请及时通知本人删除相关内容。

  • 时间服务器NIO
    • 服务端代码
    • 客户端代码
    • 总结

时间服务器–NIO

服务端代码

服务端主程序

public class TimeServer {    public static void main(String[] args) {        int port = 1234;        try {            TimeServerDispatcher timeServer;            timeServer = new TimeServerDispatcher(port);            new Thread(timeServer).start();        } catch (IOException e) {            e.printStackTrace();        }    }}

服务端处理类

public class TimeServerDispatcher implements Runnable {    private Selector selector;    private ServerSocketChannel ssc;    private volatile boolean stop;    private DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    public TimeServerDispatcher(int port) throws IOException {        selector = Selector.open();        ssc = ServerSocketChannel.open();        ssc.configureBlocking(false);        ssc.socket().bind(new InetSocketAddress(port), 10000);        ssc.register(selector, SelectionKey.OP_ACCEPT);        System.out.println("The time server is listening on port : " + port);    }    public void stop() {        this.stop = true;    }    @Override    public void run() {        while (!this.stop) {            try {                selector.select(1000);                Set<SelectionKey> selectedKeys = selector.selectedKeys();                for (SelectionKey k : selectedKeys) {                    selectedKeys.remove(k);                    try {                        doProcessRequest(k);                    } catch (Exception e) {                        if (k != null) {                            k.cancel();                            if (k.channel() != null)                                k.channel().close();                        }                    }                }            } catch (Throwable t) {                t.printStackTrace();            }        }        if (selector != null) {            try {                selector.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    private void doProcessRequest(SelectionKey key) throws IOException {        if (!key.isValid())            return;        if (key.isAcceptable()) {            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();            SocketChannel sc = ssc.accept();            sc.configureBlocking(false);            sc.register(selector, SelectionKey.OP_READ);        }        if (key.isReadable()) {            SocketChannel sc = (SocketChannel) key.channel();            ByteBuffer readBuffer = ByteBuffer.allocate(1024);            int len = sc.read(readBuffer);            if (len > 0) {                readBuffer.flip();                byte[] bytes = new byte[readBuffer.remaining()];                readBuffer.get(bytes);                String reqMsg = new String(bytes, "UTF-8");                System.out.println("request msg is : " + reqMsg);                String respMsg = this.df.format(new Date());                doResponse(sc, respMsg);            } else if (len < 0) {                key.cancel();                sc.close();            } else {                // 0            }        }    }    private void doResponse(SocketChannel sc, String respMsg) throws IOException {        if (respMsg == null)            return;        byte[] bytes = respMsg.getBytes();        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);        writeBuffer.put(bytes);        writeBuffer.flip();        sc.write(writeBuffer);    }}

客户端代码

客户端主程序

public class TimeClient {    public static void main(String[] args) {        int port = 1234;        new Thread(new TimeClientHandler("127.0.0.1", port)).start();    }}

客户端处理程序

public class TimeClientHandler implements Runnable {    private String host;    private int port;    private Selector selector;    private SocketChannel socketChannel;    private volatile boolean stop;    public TimeClientHandler(String host, int port) {        this.host = host;        this.port = port;        try {            selector = Selector.open();            socketChannel = SocketChannel.open();            socketChannel.configureBlocking(false);        } catch (IOException e) {            e.printStackTrace();        }    }    @Override    public void run() {        try {            if (socketChannel.connect(new InetSocketAddress(host, port))) {                socketChannel.register(selector, SelectionKey.OP_READ);                doSendReqMsg(socketChannel, "Hi Server !");            } else                socketChannel.register(selector, SelectionKey.OP_CONNECT);        } catch (IOException e) {            e.printStackTrace();        }        while (!this.stop) {            try {                selector.select(1000);                Set<SelectionKey> selectedKeys = selector.selectedKeys();                for (SelectionKey k : selectedKeys) {                    selectedKeys.remove(k);                    try {                        doProcessResponse(k);                    } catch (Exception e) {                        if (k != null) {                            k.cancel();                            if (k.channel() != null)                                k.channel().close();                        }                    }                }            } catch (Exception e) {                e.printStackTrace();            }        }        if (selector != null) {            try {                selector.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    private void doProcessResponse(SelectionKey key) throws IOException {        if (!key.isValid())            return;        SocketChannel sc = (SocketChannel) key.channel();        if (key.isConnectable()) {            if (sc.finishConnect()) {                sc.register(selector, SelectionKey.OP_READ);                doSendReqMsg(sc, "Hi Server !");            } else {                throw new RuntimeException("连接失败");            }        }        if (key.isReadable()) {            ByteBuffer readBuffer = ByteBuffer.allocate(1024);            int readBytes = sc.read(readBuffer);            if (readBytes > 0) {                readBuffer.flip();                byte[] bytes = new byte[readBuffer.remaining()];                readBuffer.get(bytes);                String respMsg = new String(bytes, "UTF-8");                System.out.println("time : " + respMsg);                this.stop = true;            } else if (readBytes < 0) {                key.cancel();                sc.close();            } else {                // 0            }        }    }    private void doSendReqMsg(SocketChannel sc, String reqMsg) throws IOException {        byte[] req = reqMsg.getBytes();        ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);        writeBuffer.put(req);        writeBuffer.flip();        sc.write(writeBuffer);    }}

总结

这种模型有如下特点:

  • 客户端的连接操作时异步的
  • SocketChannel的读写操作是异步的
  • 一个Selector可以处理成千上万个客户端连接

但是:

  • 代码复杂
  • 这里的实现可能出现 “读半包”,”写半包”的情况

参考资料: 《Netty权威指南》第二版

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 荣耀v10锁屏密码忘了怎么办 荣耀9锁屏密码忘记了怎么办 华为荣耀v9锁屏密码忘记了怎么办 荣耀手机密码忘了怎么办数字锁 华为畅享5忘了开机密码怎么办 华为p9连接热点忘了密码怎么办 苹果手机用联通卡信号不好怎么办 魅族手机充电口松了怎么办 华为手机刷机失败开不了机怎么办 华为刷机失败开不了机怎么办 银行卡信息被盗密码被改该怎么办 全民k歌手机话筒有杂音怎么办 手机刷机清除数据需要密码怎么办 oppo手机屏锁密码忘了怎么办 华为手机摔了一下开不了机怎么办 华为v9手机删除隐私空间了怎么办 华为荣耀畅玩5x卡顿怎么办 淘宝买家收到货后恶意退款怎么办 手机淘宝申请退款后不想退了怎么办 买房交首付时的收据发票掉了怎么办 苹果商城消费提示问题忘记了怎么办 psd文件超过2g不能存储怎么办 手机拍的照片做微信头像太大怎么办 上传的照片在等待中传不上去怎么办 淘宝购物车里的图片模糊怎么办 天猫超市一箱饮料少两瓶怎么办 网上卖一件代发顾客要退货怎么办 京东买东西卖家拒绝发货怎么办 淘宝不小心退款给买家了怎么办 不小心智能清理了淘宝物流怎么办 移动卡绑定了太多东西换联通怎么办 淘宝上卖出的东西快递弄丢了怎么办 京东第三方不确认收货怎么办? 天猫评价被判定为广告怎么办 天猫一个订单用卷分单退货怎么办 天猫对已付款成功后自动退款怎么办 拼多多新人红包减价卖家怎么办 魔力宝贝手机版注册人数已满怎么办 买家投诉虚假签收淘宝卖家该怎么办 手机淘宝商家老打骚扰电话怎么办 手机上查询详单忘记服务密码怎么办