(九)socket·NIO socket 实现 服务端广播+客户端群聊 -不限制次序,不限制交互次数

文章目录

  • 1、 socket·NIO socket 实现 控制台 一问一答式聊天
  • 2、设计思路
  • 3、代码
    • 3.0、关键 selector.key() 和 selector.selectedKeys
    • 3.1、服务端
    • 3.2、客户端1和客户端2(代码都一样,模拟多个客户端而已)
      • 3.2.1、客户端1
      • 3.2.2、客户端2
    • 3、展示
      • 服务端
      • 客户端1
      • 客户端2

1、 socket·NIO socket 实现 控制台 一问一答式聊天

回顾 socket·NIO socket 实现 控制台 一问一答式聊天
本文将实现多客户端自由群聊,加服务端广播的功能

2、设计思路

服务端广播需要输入,而 NIO socket 依赖循环遍历不通的状态,会造成阻塞,一种思路是对不通状态的监听使用多线程,一种是将写这个动作使用多线程。
从实践看,NIO 是可以避免使用多线程的。所以我们采取第二种方法,使用 Queue 保存控制台写入的信息。

3、代码

为了降低 Cpu 负载,我们在代码中增加了 Thread.sleep 的逻辑

3.0、关键 selector.key() 和 selector.selectedKeys

selector.key() 是全集注册的 channel,但是你不能手动 remove,除非 “ A key is removed only after
it has been cancelled and its channel has been deregistered”。

selector.selectedKeysa() 允许你编辑加 remove,看业务场景

/**
     * Returns this selector's key set.
     *
     * 

The key set is not directly modifiable. A key is removed only after * it has been cancelled and its channel has been deregistered. Any * attempt to modify the key set will cause an {@link * UnsupportedOperationException} to be thrown. * *

The key set is not thread-safe.

* * @return This selector's key set * * @throws ClosedSelectorException * If this selector is closed */
public abstract Set<SelectionKey> keys(); /** * Returns this selector's selected-key set. * *

Keys may be removed from, but not directly added to, the * selected-key set. Any attempt to add an object to the key set will * cause an {@link UnsupportedOperationException} to be thrown. * *

The selected-key set is not thread-safe.

* * @return This selector's selected-key set * * @throws ClosedSelectorException * If this selector is closed */
public abstract Set<SelectionKey> selectedKeys();

3.1、服务端

import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.SelectorProvider;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @Title: Socket Server
 * @Description: NIO Socket
 * @Author: WuJie
 */
public class NioSocketServer {
    private final static String CHARSET = "UTF8";
    private static Selector selector = null;
    private final static String CLIENTINFO = "clientInfo";
    /**降低一下CPU的负载~*/
    private final static long SLEEP_MILLIS = 100;
    /**临时存储输入的消息*/
    volatile static Queue<String> writeInfoQueue = new LinkedBlockingQueue();

    public static void main(String[] args) {
        NioSocketServer nioSocketServer = new NioSocketServer();
        //启动服务器,等待客户端访问
        nioSocketServer.startServer();

        //处理控制台的输入信息
        ScannerThread myThread = new ScannerThread();
        myThread.start();
        if (selector != null) {
            //处理客户端的请求
            nioSocketServer.handle(selector);
        } else {
            System.out.println("error 2,服务端启动失败");
        }
    }

    private static class ScannerThread extends Thread{

        @Override
        public void run() {
            while(true){
                Scanner scanner=new Scanner(System.in);
                String response=scanner.nextLine();
                writeInfoQueue.add(response);
            }
        }
    }


    /**
     * 启动服务器
     */
    public void startServer() {

        try {
            //初始化 nio Selector
            selector = SelectorProvider.provider().openSelector();
            //阻塞 selector 1000 毫秒,非必须
            selector.select(100);

            //Channel 多线程安全、支持异步关闭
            //初始化服务端 SocketChannel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //设置服务端端口
            serverSocketChannel.bind(new InetSocketAddress(10000));
            //非阻塞模式,否则就不是NIO了
            serverSocketChannel.configureBlocking(false);
            //将服务端 Channel 注册到 Selector,感兴趣事件为 准备接受访问
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务端已启动,服务器端字符编码格式为" + CHARSET + ",等待客户端的访问:");
        } catch (IOException e) {
            System.out.println("error 1,获取 Selector 失败" + e);
        }
    }

    /**
     * 处理客户端的请求与交互
     *
     * @param selector
     */
    public void handle(Selector selector) {
        try {
            for (; ; ) {
                try {
                    Thread.sleep(SLEEP_MILLIS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //获取已注册 channel 数量
                int keyNumbers = selector.select();
                //如果已注册 channel 数量>0,则开始处理
                if (keyNumbers > 0) {
                    //获取 selector 中已注册 channel 的 SelectionKey 集合
                    Set<SelectionKey> selectionKeySet = selector.selectedKeys();

                    Iterator iterable = selectionKeySet.iterator();
                    while (iterable.hasNext()) {
                        //注意语法规则,必须 next 才能 remove
                        SelectionKey selectionKey = (SelectionKey) iterable.next();
                        iterable.remove();
                        if (selectionKey.isAcceptable()) {
                            //接受客户端请求
                            acceptable(selectionKey);
                        } else if (selectionKey.isReadable() && selectionKey.isValid()) {
                            //读取客户端传输的数据
                            readable(selectionKey, selector.keys());
                        } else if(!writeInfoQueue.isEmpty()){
                            String info = writeInfoQueue.poll();
                            if(StringUtils.isEmpty(info)){
                                continue;
                            }
                            Iterator<SelectionKey> iterator = selector.keys().iterator();
                            while(iterator.hasNext()){
                                SelectionKey target = iterator.next();
                                //iterable.remove();

                                if(target.isWritable()&&target.isValid()) {
                                    writable(target,"[服务端广播]:" + info);
                                }
                            }
                        }

                    }
                } else {
                    continue;
                }
            }

        } catch (IOException e) {
            System.out.println("error 3,获取 Selector 已注册 Channel 数量失败" + e);
        }

    }

    /**
     * 处理客户端请求
     * 将客户单 channel 注册到 selector
     *
     * @param selectionKey
     */
    private static void acceptable(SelectionKey selectionKey) {
        try {

            //获取客户端请求
            SocketChannel clientChannel = ((ServerSocketChannel) selectionKey.channel()).accept();
            clientChannel.configureBlocking(false);

            //可以将该map连同 channel 一起注册到 selector,map可以携带附属信息
            Map<String, String> map = new HashMap();
            map.put(CLIENTINFO, clientChannel.getRemoteAddress().toString());
            clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, map);
        } catch (ClosedChannelException e1) {
            System.out.println("error 4,将客户端请求 channel 注册到 selector 失败" + e1);
        } catch (IOException e2) {
            System.out.println("error 5,获取客户端请求失败" + e2);
        }
    }

    /**
     * 处理可读的channel
     * 读取客户端发送的信息
     *
     * @param selectionKey
     */
    private static void readable(SelectionKey selectionKey, Set<SelectionKey> set) {
        try {
            SocketChannel clientChannel = (SocketChannel) selectionKey.channel();
            //准备 1k 的缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            //将channel 内容写入缓存 byteBuffer
            int len = clientChannel.read(byteBuffer);
            //System.out.println("byteBuffer.remaining()="+byteBuffer.remaining()+";len="+len);

            //如果没有信息传入,说明客户端可能中断连接,则关闭这个客户端channel
            if (len == -1) {
                try {
                    clientChannel.close();
                    selector.wakeup();//唤起其他channel
                    System.out.println("line 133,客户端已关闭:" + clientChannel.socket().getRemoteSocketAddress());
                    System.out.println("可读--------------结束");
                    //continue;
                    return;
                } catch (Exception e) {
                    System.out.println("error 6,关闭客户端 channel 失败" + e);
                    System.out.println("可读--------------结束");
                    //continue;
                    return;
                }

            }
            //调用flip之后,读写指针指到缓存头部,并且设置了最多只能读出之前写入的数据长度(而不是整个缓存的容量大小)。
            byteBuffer.flip();
            //初始化一个 缓存区可读长度的数组
            byte[] bytes = new byte[byteBuffer.remaining()];

            byteBuffer.get(bytes, 0, byteBuffer.remaining());
            byteBuffer.clear();
            String info = new String(bytes, CHARSET);
            Map<String,String> attachment=(HashMap) selectionKey.attachment();
            System.out.println("客户端["+ attachment.get(CLIENTINFO)+"]" + info);
            Iterator<SelectionKey> iterator = set.iterator();

            while(iterator.hasNext()){
                SelectionKey target = iterator.next();

                if(target.isWritable()&&target.isValid()) {
                    Map<String,String> targetAttachment=(HashMap) target.attachment();
                    //iterator.remove();
                    if(targetAttachment.get(CLIENTINFO).equals(attachment.get(CLIENTINFO))){
                        continue;
                    }
                    writable(target,"客户端["+ attachment.get(CLIENTINFO)+"]" + info);
                }
            }

        } catch (IOException e) {
            System.out.println("error 7,出错了");
        }
    }

    /**
     * 处理可写的channel
     * 向客户端发送信息,该信息从控制台输入
     *
     * @param selectionKey
     */
    private static void writable(SelectionKey selectionKey, String info) {
        try {
            SocketChannel clientChannel = (SocketChannel) selectionKey.channel();

            int len = clientChannel.write(ByteBuffer.wrap(info.getBytes(CHARSET)));
            if (len == -1) {
                try {
                    clientChannel.close();
                    selector.wakeup();
                    System.out.println("line 182,客户端已关闭:" + clientChannel.socket().getRemoteSocketAddress());
                    System.out.println("可写--------------结束");
                    //continue;
                    return;
                } catch (Exception e) {
                    System.out.println("error 8" + e);
                    System.out.println("可写--------------结束");
                    //continue;
                    return;
                }
            }
            selector.wakeup();
        } catch (UnsupportedEncodingException e) {
            System.out.println("error 9,字符格式不被支持" + e);
        } catch (IOException e2) {
            System.out.println("error 10" + e2);
        }
    }


}

3.2、客户端1和客户端2(代码都一样,模拟多个客户端而已)

3.2.1、客户端1

import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @Title:Socket Client
 * @Description: NIO Socket
 * @Author: WuJie
 */
public class NioSocketClient {
    private final static String CHARSET="UTF8";
    /**降低一下CPU的负载~*/
    private final static long SLEEP_MILLIS = 100;
    private static Selector selector=null;
    /**临时存储输入的消息*/
    volatile static Queue<String> writeInfoQueue = new LinkedBlockingQueue();

    public static void main(String [] args){
        NioSocketClient nioSocketClient=new NioSocketClient();
        nioSocketClient.connectedServer();

        //处理控制台的输入信息
        ScannerThread myThread = new ScannerThread();
        myThread.start();

        if(selector!=null){
            nioSocketClient.handle(selector);
        }else{
            System.out.println("error 2,服务端启动失败");
        }
    }

    private static class ScannerThread extends Thread{

        @Override
        public void run() {
            while(true){
                Scanner scanner=new Scanner(System.in);
                String response=scanner.nextLine();
                writeInfoQueue.add(response);
            }
        }
    }

    /**
     * 处理客户端与服务器端的交互
     */
    public void connectedServer(){
        try{
            //初始化 nio Selector
            selector= SelectorProvider.provider().openSelector();
            //阻塞 selector 1000 毫秒,非必须
            selector.select(1000);

            //Channel 多线程安全、支持异步关闭
            //初始化 客户端 SocketChannel
            SocketChannel clientChannel=SocketChannel.open();
            //非阻塞模式,否则就不是NIO了
            clientChannel.configureBlocking(false);
            //连接服务端
            clientChannel.connect(new InetSocketAddress("127.0.0.1",10000));
            //将 clientChannel 注册到selector,感兴趣事件为连接
            clientChannel.register(selector,SelectionKey.OP_CONNECT);

            System.out.println("客户端已启动,客户端字符编码格式为"+CHARSET+",等待取得和服务器端的连接:");
        }catch(IOException e){
            System.out.println("error 1,获取 Selector 失败"+e);
        }
    }

    /**
     * 处理和服务器端的交互
     * @param selector
     */
    public void handle(Selector selector){
        try{
            for(;;){
                try {
                    Thread.sleep(SLEEP_MILLIS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //获取已注册 channel 数量
                int keyNumbers=selector.select();
                //如果已注册 channel 数量>0,则开始处理
                if(keyNumbers>0){
                    //获取 selector 中已注册 channel 的 SelectionKey 集合
                    Set<SelectionKey> selectionKeySet=selector.selectedKeys();

                    Iterator iterable=selectionKeySet.iterator();
                    while(iterable.hasNext()){
                        //注意基础语法,必须 next 后才能 remove
                        SelectionKey selectionKey=(SelectionKey)iterable.next();
                        iterable.remove();

                        if(selectionKey.isConnectable()){
                            //处理 连接 类型的channel
                            connectable(selectionKey);
                        }else if(selectionKey.isReadable()&&selectionKey.isValid()){
                            //读取服务端传输的数据
                            readable(selectionKey);
                        }else if(selectionKey.isWritable()&&selectionKey.isValid()) {
                            //返回给服务端信息
                            writable(selectionKey);
                        }

                    }
                }else{
                    continue;
                }
            }

        }catch(IOException e){
            System.out.println("error 3,获取 Selector 已注册 Channel 数量失败"+e);
        }
    }

    /**
     * 处理可连接的channel
     * 取得和服务端的连接,向服务端发送信息,该信息从控制台输入
     * @param selectionKey
     */
    private static void connectable(SelectionKey selectionKey){
        try{
            SocketChannel clientChannel=(SocketChannel)selectionKey.channel();
            if(clientChannel.isConnectionPending()){
                while(!clientChannel.finishConnect()){
                    System.out.println("当 channel 的 socket 连接上后,返回true");
                };
            }
            //客户端已连接上服务端,将感兴趣事件修改为 可读|可写
            selectionKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
            selector.wakeup();
        }catch(UnsupportedEncodingException e){
            System.out.println("error 5,字符格式不被支持"+e);
        }catch(IOException e2){
            System.out.println("error 6"+e2);
        }

    }

    /**
     * 处理可读的channel
     * 读取客户端发送的信息
     * @param selectionKey
     */
    private static void readable(SelectionKey selectionKey){
        try{

            SocketChannel clientChannel=(SocketChannel) selectionKey.channel();
            //准备 1k 的缓冲区
            ByteBuffer byteBuffer= ByteBuffer.allocate(1024);
            //将channel 内容写入缓存 byteBuffer
            int len=clientChannel.read(byteBuffer);
            //System.out.println("byteBuffer.remaining()="+byteBuffer.remaining()+";len="+len);

            if(len==-1){
                try{
                    clientChannel.close();
                    selector.wakeup();//唤起其他channel
                    System.out.println("line 160,服务端已关闭:"+clientChannel.socket().getRemoteSocketAddress());
                    System.out.println("可读--------------结束");
                    //continue;
                    return;
                }catch(Exception e){
                    System.out.println("error 7,关闭客户端 channel 失败"+e);
                    System.out.println("可读--------------结束");
                    //continue;
                    return;
                }

            }
            //调用flip之后,读写指针指到缓存头部,并且设置了最多只能读出之前写入的数据长度(而不是整个缓存的容量大小)。
            byteBuffer.flip();
            //初始化一个 缓存区可读长度的数组
            byte[] bytes=new byte[byteBuffer.remaining()];

            byteBuffer.get(bytes,0,byteBuffer.remaining());
            byteBuffer.clear();
            System.out.println("服务端:"+new String(bytes,CHARSET));
        }catch(IOException e){
            System.out.println("error 8,出错了");
        }
    }

    /**
     * 处理可写的channel
     * 向服务端发送信息,该信息从控制台输入
     * @param selectionKey
     */
    private static void writable(SelectionKey selectionKey){
        try{
            SocketChannel clientChannel=(SocketChannel) selectionKey.channel();

            if(writeInfoQueue.isEmpty()){
                return;
            }
            String info= writeInfoQueue.poll();
            if(StringUtils.isEmpty(info)){
                return;
            }
            int len=clientChannel.write(ByteBuffer.wrap(info.getBytes(CHARSET)));
            if(len==-1){
                try{
                    clientChannel.close();
                    selector.wakeup();
                    System.out.println("line 210,服务端已关闭:"+clientChannel.socket().getRemoteSocketAddress());
                    System.out.println("可写--------------结束");
                    //continue;
                    return ;
                }catch(Exception e){
                    System.out.println("error 9"+e);
                    System.out.println("可写--------------结束");
                    //continue;
                    return ;
                }
            }
            selector.wakeup();//唤醒其他key

        }catch(UnsupportedEncodingException e){
            System.out.println("error 9,字符格式不被支持"+e);
        }catch(IOException e2){
            System.out.println("error 10"+e2);
        }
    }
}

3.2.2、客户端2

import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * @Title:Socket Client
 * @Description: NIO Socket
 * @Author: WuJie
 */
public class NioSocketClient2 {
    private final static String CHARSET="UTF8";
    /**降低一下CPU的负载~*/
    private final static long SLEEP_MILLIS = 100;
    private static Selector selector=null;
    /**临时存储输入的消息*/
    volatile static Queue<String> writeInfoQueue = new LinkedBlockingQueue();

    public static void main(String [] args){
        NioSocketClient2 nioSocketClient=new NioSocketClient2();
        nioSocketClient.connectedServer();

        //处理控制台的输入信息
        ScannerThread myThread = new ScannerThread();
        myThread.start();

        if(selector!=null){
            nioSocketClient.handle(selector);
        }else{
            System.out.println("error 2,服务端启动失败");
        }
    }

    private static class ScannerThread extends Thread{

        @Override
        public void run() {
            while(true){
                Scanner scanner=new Scanner(System.in);
                String response=scanner.nextLine();
                writeInfoQueue.add(response);
            }
        }
    }

    /**
     * 处理客户端与服务器端的交互
     */
    public void connectedServer(){
        try{
            //初始化 nio Selector
            selector= SelectorProvider.provider().openSelector();
            //阻塞 selector 1000 毫秒,非必须
            selector.select(1000);

            //Channel 多线程安全、支持异步关闭
            //初始化 客户端 SocketChannel
            SocketChannel clientChannel=SocketChannel.open();
            //非阻塞模式,否则就不是NIO了
            clientChannel.configureBlocking(false);
            //连接服务端
            clientChannel.connect(new InetSocketAddress("127.0.0.1",10000));
            //将 clientChannel 注册到selector,感兴趣事件为连接
            clientChannel.register(selector,SelectionKey.OP_CONNECT);

            System.out.println("客户端已启动,客户端字符编码格式为"+CHARSET+",等待取得和服务器端的连接:");
        }catch(IOException e){
            System.out.println("error 1,获取 Selector 失败"+e);
        }
    }

    /**
     * 处理和服务器端的交互
     * @param selector
     */
    public void handle(Selector selector){
        try{
            for(;;){
                try {
                    Thread.sleep(SLEEP_MILLIS);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //获取已注册 channel 数量
                int keyNumbers=selector.select();
                //如果已注册 channel 数量>0,则开始处理
                if(keyNumbers>0){
                    //获取 selector 中已注册 channel 的 SelectionKey 集合
                    Set<SelectionKey> selectionKeySet=selector.selectedKeys();

                    Iterator iterable=selectionKeySet.iterator();
                    while(iterable.hasNext()){
                        //注意基础语法,必须 next 后才能 remove
                        SelectionKey selectionKey=(SelectionKey)iterable.next();
                        iterable.remove();

                        if(selectionKey.isConnectable()){
                            //处理 连接 类型的channel
                            connectable(selectionKey);
                        }else if(selectionKey.isReadable()&&selectionKey.isValid()){
                            //读取服务端传输的数据
                            readable(selectionKey);
                        }else if(selectionKey.isWritable()&&selectionKey.isValid()) {
                            //返回给服务端信息
                            writable(selectionKey);
                        }

                    }
                }else{
                    continue;
                }
            }

        }catch(IOException e){
            System.out.println("error 3,获取 Selector 已注册 Channel 数量失败"+e);
        }
    }

    /**
     * 处理可连接的channel
     * 取得和服务端的连接,向服务端发送信息,该信息从控制台输入
     * @param selectionKey
     */
    private static void connectable(SelectionKey selectionKey){
        try{
            SocketChannel clientChannel=(SocketChannel)selectionKey.channel();
            if(clientChannel.isConnectionPending()){
                while(!clientChannel.finishConnect()){
                    System.out.println("当 channel 的 socket 连接上后,返回true");
                };
            }
            //客户端已连接上服务端,将感兴趣事件修改为 可读|可写
            selectionKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
            selector.wakeup();
        }catch(UnsupportedEncodingException e){
            System.out.println("error 5,字符格式不被支持"+e);
        }catch(IOException e2){
            System.out.println("error 6"+e2);
        }

    }

    /**
     * 处理可读的channel
     * 读取客户端发送的信息
     * @param selectionKey
     */
    private static void readable(SelectionKey selectionKey){
        try{

            SocketChannel clientChannel=(SocketChannel) selectionKey.channel();
            //准备 1k 的缓冲区
            ByteBuffer byteBuffer= ByteBuffer.allocate(1024);
            //将channel 内容写入缓存 byteBuffer
            int len=clientChannel.read(byteBuffer);
            //System.out.println("byteBuffer.remaining()="+byteBuffer.remaining()+";len="+len);

            if(len==-1){
                try{
                    clientChannel.close();
                    selector.wakeup();//唤起其他channel
                    System.out.println("line 160,服务端已关闭:"+clientChannel.socket().getRemoteSocketAddress());
                    System.out.println("可读--------------结束");
                    //continue;
                    return;
                }catch(Exception e){
                    System.out.println("error 7,关闭客户端 channel 失败"+e);
                    System.out.println("可读--------------结束");
                    //continue;
                    return;
                }

            }
            //调用flip之后,读写指针指到缓存头部,并且设置了最多只能读出之前写入的数据长度(而不是整个缓存的容量大小)。
            byteBuffer.flip();
            //初始化一个 缓存区可读长度的数组
            byte[] bytes=new byte[byteBuffer.remaining()];

            byteBuffer.get(bytes,0,byteBuffer.remaining());
            byteBuffer.clear();
            System.out.println("服务端:"+new String(bytes,CHARSET));
        }catch(IOException e){
            System.out.println("error 8,出错了");
        }
    }

    /**
     * 处理可写的channel
     * 向服务端发送信息,该信息从控制台输入
     * @param selectionKey
     */
    private static void writable(SelectionKey selectionKey){
        try{
            SocketChannel clientChannel=(SocketChannel) selectionKey.channel();

            if(writeInfoQueue.isEmpty()){
                return;
            }
            String info= writeInfoQueue.poll();
            if(StringUtils.isEmpty(info)){
                return;
            }
            int len=clientChannel.write(ByteBuffer.wrap(info.getBytes(CHARSET)));
            if(len==-1){
                try{
                    clientChannel.close();
                    selector.wakeup();
                    System.out.println("line 210,服务端已关闭:"+clientChannel.socket().getRemoteSocketAddress());
                    System.out.println("可写--------------结束");
                    //continue;
                    return ;
                }catch(Exception e){
                    System.out.println("error 9"+e);
                    System.out.println("可写--------------结束");
                    //continue;
                    return ;
                }
            }
            selector.wakeup();//唤醒其他key

        }catch(UnsupportedEncodingException e){
            System.out.println("error 9,字符格式不被支持"+e);
        }catch(IOException e2){
            System.out.println("error 10"+e2);
        }
    }
}

3、展示

服务端

服务端已启动,服务器端字符编码格式为UTF8,等待客户端的访问:
广播测试
客户端[/127.0.0.1:50179]客户端1-1
客户端[/127.0.0.1:50179]客户端1-2
客户端[/127.0.0.1:50186]客户单2-1
客户端[/127.0.0.1:50186]客户端2-2
广播测试2

客户端1

客户端已启动,客户端字符编码格式为UTF8,等待取得和服务器端的连接:
服务端:[服务端广播]:广播测试
客户端1-1
客户端1-2
服务端:客户端[/127.0.0.1:50186]客户单2-1
服务端:客户端[/127.0.0.1:50186]客户端2-2
服务端:[服务端广播]:广播测试2

客户端2

客户端已启动,客户端字符编码格式为UTF8,等待取得和服务器端的连接:
服务端:[服务端广播]:广播测试
服务端:客户端[/127.0.0.1:50179]客户端1-1
服务端:客户端[/127.0.0.1:50179]客户端1-2
客户单2-1
客户端2-2
服务端:[服务端广播]:广播测试2

你可能感兴趣的:(Socket,nio,交互,java)