java NIO小案例

作业要求:
使用java nio 实现一个CS 架构的通讯工具,服务器端:监控客户 端上线、离线、转发消息,客户端:发送、接受消息


一、功能需求

  • 客户端: 发送消息,接收消息 ------ 发送数据与接收数据
  • 服务器端: 监控客户端上线,离线,转发消息 ------ Selector(选择器)

二、代码

1.Server端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.ClosedChannelException;
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.Set;


public class ChatServer {

    private Selector selector;
    private ServerSocketChannel ssc;

    private void init(){
        try {
            //创建serverChannel
            ssc = ServerSocketChannel.open();
            //设置非阻塞
            ssc.configureBlocking(false);
            ssc.bind(new InetSocketAddress(9090));
            //打开选择器
            selector = Selector.open();
            //注册监听器
            ssc.register(selector,SelectionKey.OP_ACCEPT);

            //监听器监听
            monitor(ssc);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * selector监听事件方法
     * @throws IOException
     * @throws ClosedChannelException
     */
    private void monitor(ServerSocketChannel ssc) throws IOException, ClosedChannelException {
        System.out.println("服务器已经启动...");
        // selector.select();会阻塞 直到有事件产生
        while(selector.select()>0){
            //获取所有有事件的selectionKey
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while(it.hasNext()){
                SelectionKey key = it.next();
                //判断如果是accept事件 进行操作
                if(key.isAcceptable()){
                    //从selectionKey中获取channel
                    SocketChannel socketChannel = ssc.accept();
                    //将连接进来的客户端 设置为非阻塞 并注册到seletor上
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println(socketChannel.getRemoteAddress().toString()+"上线");
//					it.remove();
//					printOnlineEvent(socketChannel);
                }
                //监听读事件  也就是从客户端发来的数据
                if(key.isReadable()){
                    //打印数据
                    handle(key);
                }
                //将该socketChannel事件处理完以后要从selector中移除掉,防止死循环
                it.remove();
            }
        }
    }


    /**
     * 获取客户地址
     * @param key
     * @return
     * @throws IOException
     */
    private String getRemoteAddress(SelectionKey key){
        String str = null;
        try {
            SocketChannel  sc = (SocketChannel)key.channel();
            str = sc.getRemoteAddress().toString();
        } catch (Exception e) {
            // TODO: handle exception
        }
        return str;
    }

    /**
     * 处理从客户端发来的消息
     * 并排除自己
     * @param key
     */
    private void handle(SelectionKey key) {
        SocketChannel sc = (SocketChannel)key.channel();
        String addr = getRemoteAddress(key);
        try {
            StringBuilder sb = new StringBuilder();
            sb.append(addr+" :");
            ByteBuffer bf = ByteBuffer.allocate(1024);
            int len = 0;
            while((len=sc.read(bf)) >0){
                bf.flip();
                sb.append(new String(bf.array(),0,len));
                bf.clear();
            }
            if(sb.toString().length()>0){
                System.out.println(sb.toString());
                //转发数据
                sendHandle(key,sb.toString());
            }
        } catch (Exception e) {
            //如果接受消息失败表示离线
            key.cancel();
            System.out.println(addr+"客户端关闭");
            try {
                sc.close();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
    }
    /**
     * 给客户端分发数据 排除自己
     * @param key
     * @param msg
     */
    private void sendHandle(SelectionKey key,String msg){
        try {
            Set<SelectionKey> set = selector.keys();
            for(SelectionKey clientKey : set){
                Channel channel = clientKey.channel();
                if(channel instanceof SocketChannel && clientKey != key){
                    SocketChannel socketChannel = (SocketChannel)channel;
                    System.out.println("给这个客户端转发消息:"+socketChannel.getRemoteAddress());
                    socketChannel.write(ByteBuffer.wrap(String.valueOf(msg).getBytes()));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        ChatServer cs = new ChatServer();
        cs.init();
    }

}

2.Client端代码

package demo;

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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 聊天室 client 使用selector监听read消息
 * @author zhang
 *
 */
public class ChatClient {

    private Selector selector;
    private final String HOST = "127.0.0.1";
    private final int PORT = 9090;
    ExecutorService pool = Executors.newSingleThreadExecutor();

    private void init(){
        try {
            //创建channel
            final SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(HOST,PORT));
            socketChannel.configureBlocking(false);
            selector = Selector.open();
            //注册监听器 监听读消息
            socketChannel.register(selector, SelectionKey.OP_READ);

            //异步输入
            pool.execute(new Runnable() {

                @Override
                public void run() {
                    Scanner scanner = new Scanner(System.in);
                    while(scanner.hasNextLine()){
                        String str = scanner.nextLine();
                        try {
                            socketChannel.write(ByteBuffer.wrap(str.getBytes()));
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }

                }
            });

            System.out.println("我是:"+socketChannel.getLocalAddress());
            while(selector.select()>0){
                Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                while(it.hasNext()){
                    SelectionKey key = it.next();
                    if(key.isReadable()){
                        SocketChannel channel = (SocketChannel) key.channel();
                        ByteBuffer buf = ByteBuffer.allocate(1024);
                        StringBuilder sb = new StringBuilder();
                        while(channel.read(buf)>0){
                            buf.flip();
                            sb.append(new String(buf.array(),0,buf.limit()));
                            buf.clear();
                        }
                        System.out.println(sb.toString());
                    }
                }
                it.remove();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        ChatClient2 cc = new ChatClient2();
        cc.init();
    }

}



总结

自己没写出来,学习NIO之后知道这个东西的原理是什么了,但是代码就只能很磕磕绊绊的写出来服务器的初始化和监听,别的没写出来,看代码的话能理解大概……
学习NIO参考的是这个:原文链接:https://blog.csdn.net/guorui_java/article/details/123589971 和 微信图书
代码参考的是:原文链接:https://blog.csdn.net/guorui_java/article/details/123589971 和 原文链接:https://blog.csdn.net/RenJianLeLe/article/details/106977914

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值