NIO网络编程--实战多人聊天室

BIO网络模型

高并发场景下的BIO模型:每个客户端请求会建立一个阻塞线程

BIO模型的缺陷:

①阻塞式IO模型

②弹性伸缩能力差(每次都会起单独的线程)

③多线程消耗资源

 

NIO网络模型:

NIO模型优势:

①非阻塞式IO模型

②弹性伸缩能力强

③单线程节省资源

 

NIO网络编程详解:

Channel通道:双向传输,非阻塞,唯一通过buffer操作channel

 

使用方法:

 

 

Buffer缓冲器 从channel中读取数据,内存中一块属性

 

Capacity:容量 最大可储存的字节数,超过最大容量清空后才能继续写入数据

Position:容量位置,最大为容量-1

Limit:上限

Mark:标记,恢复读写位置

 

 

 

Selector选择器:检测多个IO通道是否准备就绪,NIO的基础核心,一个线程管理多个线程

 

Selectionkey:四种就绪状态常量,连接就绪,接受就绪,读就绪,写就绪。通过selectionkey集合获得当前channel

 

实战:利用NIO编程知识,实现多人聊天室功能:

package com..NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * NIO服务器端
 */
public class Nioserver {
    /**
     * 启动方法
     */
    public void start() throws IOException {
        //1.创建selector
        Selector selector = Selector.open();

        //2.通过serversocketchannel创建channerl通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        //3.为channel通道绑定监听端口
        serverSocketChannel.bind(new InetSocketAddress(9000));

        //4.设置channel为非阻塞模式
        serverSocketChannel.configureBlocking(false);

        //5.将channel注册到selector上,监听连接事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器启动成功");

        //6.循环等待新连接的事件
        for (; ; ) {
            int readyChannels = selector.select();
            if (readyChannels == 0) continue;

            Set<SelectionKey> selectionKeys = selector.selectedKeys();

            Iterator iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = (SelectionKey) iterator.next();

                iterator.remove(); // 去除实例后后移除

                //7.根据就绪状态,调用处理逻辑

                /**
                 * 接入事件
                 */
                if (selectionKey.isAcceptable()) {
                    acceptHandler(serverSocketChannel, selector);
                }

                /**
                 * 可读事件
                 */
                if (selectionKey.isReadable()) {
                    readHandler(selectionKey, selector);
                }
            }
        }
    }

    /**
     * 接入事件处理
     */
    public void acceptHandler(ServerSocketChannel serverSocketChannel,
                              Selector selector) throws IOException {
        // 创建socketchannel
        SocketChannel socketChannel = serverSocketChannel.accept();

        //将socketchannel设置为非阻塞模式
        socketChannel.configureBlocking(false);

        //将channel注册到selector上,监听事件
        socketChannel.register(selector, SelectionKey.OP_READ);

        //回复客户端提示消息
        socketChannel.write(Charset.forName("UTF-8").encode("你与聊天室其他人都不是朋友关系,请注意隐私安全"));

    }

    /**
     * 可读事件处理
     */
    public void readHandler(SelectionKey selectionKey, Selector selector) throws IOException {
        // 要从selectionkey中获取已经就绪的channel
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

        //创建buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //循环读取客户端请求
        String request = "";
        while (socketChannel.read(byteBuffer) > 1) {
            //切换成读模式
            byteBuffer.flip();

            //读取
            request += Charset.forName("utf-8").decode(byteBuffer);

        }

        //将channel再次注册到selectionkey上,再次监听他的可读事件
        socketChannel.register(selector, SelectionKey.OP_READ);

        //将客户端发送的信息广播给其他客户端 : 聊天室功能
        if (request.length() > 0) {
            System.out.println(request);
            broadCast(selector,socketChannel,request);
        }
    }

    private void broadCast(Selector selector, SocketChannel socketChannel, String request){
        // 获得所有已接入的客户端的channel
        Set<SelectionKey> selectionKeys = selector.keys();

        //向所有channel广播信息
        selectionKeys.forEach(selectionKey -> {
            Channel targetChannel = selectionKey.channel();

            if(targetChannel instanceof SocketChannel && targetChannel!=socketChannel){
                try {
                    ((SocketChannel) targetChannel).write(Charset.forName("utf-8").encode(request));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 主方法
     *
     * @param args
     */
    public static void main(String[] args) throws IOException {
        Nioserver nioserver = new Nioserver();
        nioserver.start();
    }
}

package com.NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;

/**
 * 客户端
 */
public class NioClient {
    public void start(String name) throws IOException {
        /**
         * 连接服务器端
         */
        SocketChannel socketChannel = SocketChannel.open(
                new InetSocketAddress("127.0.0.1", 9000));

        /**
         * 接受服务器端响应
         */
        Selector selector = Selector.open();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
        new Thread(new NioClientHandler(selector)).start();

        /**
         * 向服务器端发送请求
         */

        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String request = scanner.nextLine();
            if (request != null && request.length() > 0) {
                socketChannel.write(Charset.forName("utf-8").encode(name+": "+request));
            }

        }

    }

    public static void main(String[] args) throws IOException {
//        new NioClient().start();

    }
}

package com.NIO;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class NioClientHandler implements Runnable {

    public NioClientHandler(Selector selector) {
        this.selector = selector;
    }

    private Selector selector;

    @Override
    public void run() {
        try {
            for (; ; ) {
                int readyChannels = selector.select();
                if (readyChannels == 0) continue;

                Set<SelectionKey> selectionKeys = selector.selectedKeys();

                Iterator iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey selectionKey = (SelectionKey) iterator.next();

                    iterator.remove(); // 去除实例后后移除


                    //根据就绪状态,调用处理逻辑

                    /**
                     * 可读事件
                     */
                    if (selectionKey.isReadable()) {
                        readHandler(selectionKey, selector);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void readHandler(SelectionKey selectionKey, Selector selector) throws IOException {
        // 要从selectionkey中获取已经就绪的channel
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

        //创建buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //循环读取服务器端响应
        String response = "";
        while (socketChannel.read(byteBuffer) > 1) {
            //切换成读模式
            byteBuffer.flip();

            //读取
            response += Charset.forName("utf-8").decode(byteBuffer);

        }

        //将channel再次注册到selectionkey上,再次监听他的可读事件
        socketChannel.register(selector, SelectionKey.OP_READ);

        //将服务器端响应打印出来
        if (response.length() > 0) {
            System.out.println(response);
        }

    }
}

package com.NIO;

import java.io.IOException;

public class AClient {
    public static void main(String[] args) throws IOException {
        new NioClient().start("Aclient");
    }
}

package com.NIO;

import java.io.IOException;

public class BClient {
    public static void main(String[] args) throws IOException {
        new NioClient().start("Bclient");
    }
}

package com.NIO;

import java.io.IOException;

public class CClient {
    public static void main(String[] args) throws IOException {
        new NioClient().start("Cclient");
    }
}

多人聊天室功能实现:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值