栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

NIO做服务端

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

NIO做服务端

前言
   本文是NIO作为服务端示例。同学们需要先熟悉NIO的三大组件,在这里小编也将对三大基本组件进行通俗易懂的解释。

首先是Channel:他和文件输入输出流一样是读写数据的双向通道。可以从channel将数据写入buffer也可以将buffeer的数据写入channel。这里需要重点关注的是他是双向的。而我们熟悉的FileInputStream这种Stream是单向的要么只能读要么只能写。
常见的channel有FileChannel,DatagramChannel,SocketChannel以及ServerSocketChannel。我们做为服务端一般用到ServerSocketChannel。而SocketChannel是用做客户端链接服务端用的。buffer则用来缓冲读取数据。常见的buffer有ByteBuffer,FloatBuffer,ShortBuffer等等
提示:以下是本篇文章正文内容,下面案例可供参考

Buffer内部结构是怎么样的?


position
当前读取的位置。读/写操作的当前下标。当使用buffer的相对位置进行读/写操作时,读/写会从这个下标进行,并在操作完成后,buffer会更新下标的值。

mark
为某一读过的位置做标记,便于某些时候回退到该位置。一个临时存放的位置下标。调用mark()会将mark设为当前的position的值,以后调用reset()会将position属性设置为mark的值。mark的值总是小于等于position的值,如果将position的值设的比mark小,当前的mark值会被抛弃掉。

capacity
初始化时候的容量。这个Buffer最多能放多少数据。capacity一般在buffer被创建的时候指定。

limit
在Buffer上进行的读写操作都不能越过这个下标。当写数据到buffer中时,limit一般和capacity相等,当读数据时,limit代表buffer中有效数据的长度。读写的上限,limit<=capacity。

二、Selector介绍:

## 2.下面是nio作为服务端具体代码
代码如下(示例):
```c
package cn.itcast.neety.c4;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
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.util.Iterator;
import java.util.concurrent.ConcurrentlinkedQueue;


@Slf4j
public class Boss {
    public static void main(String[] args) throws IOException {

        Thread.currentThread().setName("Boss");
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        //专用于链接事件
        Selector boss = Selector.open();
        ssc.register(boss, SelectionKey.OP_ACCEPT);
        ssc.bind(new InetSocketAddress(9998));
        //创建固定数量的worker
        Worker worker = new Worker("worker=0");
        while (true) {
            boss.select();
            Iterator iterator = boss.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();
                if (key.isAcceptable()) {
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false);
                    log.debug("===connected...{}", sc.getRemoteAddress());
                    //2关联selector
                    log.debug("=====beforeRegister{}", sc.getRemoteAddress());
                    worker.register(sc); //boss调用 初始化worker的selector 启动线程
                    log.debug("====afterRegister{}", sc.getRemoteAddress());

                }
            }
        }
    }

    static class Worker implements Runnable {

        private Thread thread;

        private Selector selector;

        private String name;

        //线程是否启动
        private volatile boolean start = false;

        //线程安全队列放runnable对象
        ConcurrentlinkedQueue queue = new ConcurrentlinkedQueue<>();


        public Worker(String name) {
            this.name = name;
        }

        public void register(SocketChannel sc) throws IOException {
            if (!start) {
                selector = Selector.open();
                thread = new Thread(this, name);
                thread.start();
                start = true;
            }
            selector.wakeup();//唤醒selector
            sc.register(selector, SelectionKey.OP_READ);
        }

        public void devBugAll(ByteBuffer source) {
            source.flip();
            StringBuilder s = new StringBuilder("");
            for (int i = 0; i < source.limit(); i++) {
                byte b = source.get(i);
                s.append((char) b);
            }
            System.out.println("==========本次输出结果为:" + s);
            source.clear();
        }


        @Override
        public void run() {
            try {
                while (true) {
                    selector.select();
                    Iterator iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        iterator.remove();
                        if ((key.isReadable())) {
                            try {
                                ByteBuffer buffer = ByteBuffer.allocate(16);
                                SocketChannel channel = (SocketChannel) key.channel();
                                int read = channel.read(buffer); //如果客户端是正常断开的话,read方法的返回值是-1
                                if(read == -1){
                                    key.cancel();
                                }else {
                                    devBugAll(buffer);
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                                //如果客户端被强制关闭那么把key从selectedKey集合中移除
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();

            }
        }
    }

}

---

# 总结
NIO使用selector实现了单个线程就可以监听多个channel的方式,使用Selector的好处在于:使用更少的线程来就可以来处理通道了, 相比使用多个线程,避免了线程上下文切换带来的开销。

     
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/778203.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号