网络I/o编程模型7 Nio实现聊天室

Posted 健康平安的活着

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络I/o编程模型7 Nio实现聊天室相关的知识,希望对你有一定的参考价值。

一 nio实现聊天室

1.1 功能描述

服务器端:可以检测用户上线、离线,并实现消息的转发功能。
客户端:通过channel可以五阻塞发送消息给其它所有用户,同时可以接受其它用户发送的消息(有服务器转发得到)
使用NIO聊天系统,实现服务器端和客户端之间的数据非阻塞的简单通讯。

1.2 代码

1.服务端

package com.ljf.netty.nio.chat;

import org.omg.Messaging.SyncScopeHelper;

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

/**
 * @ClassName: CharNioserver
 * @Description: TODO
 * @Author: liujianfu
 * @Date: 2022/05/15 15:31:37
 * @Version: V1.0
 **/
public class CharNioServer 
    //定义属性
    private Selector selector;
    private ServerSocketChannel listenChannel;
    private static final int PORT=6666;
    //构造函数,初始化工作
    public CharNioServer() throws IOException 
        //得到选择器
        selector=Selector.open();
        listenChannel=ServerSocketChannel.open();
        //绑定端口
        listenChannel.socket().bind(new InetSocketAddress(PORT));
        //设置非阻塞模式
        listenChannel.configureBlocking(false);
        //将该listenchannel注册到selector
        listenChannel.register(selector, SelectionKey.OP_ACCEPT);
    
    //监听
       public void listen()
                 try 
                     //循环处理
                     while(true) 
                         int count = selector.select();
                         if(count>0)//有事件需要处理
                             Iterator<SelectionKey> iterator=selector.selectedKeys().iterator();
                             while (iterator.hasNext())
                                 //取出selectionKey
                                 SelectionKey key=iterator.next();
                                 //监听到是接收状态
                                 if(key.isAcceptable())
                                     SocketChannel sc=listenChannel.accept();
                                     sc.configureBlocking(false);//这句话千万不能少不然,报  java.nio.channels.IllegalBlockingModeException
                                     //将该sc注册到selector
                                     sc.register(selector,SelectionKey.OP_READ);
                                     System.out.println(sc.getRemoteAddress()+"已经上线");
                                 
                                 if(key.isReadable())//通道发送read事件,即通道是可读状态
                                     //处理读写的方法,处理业务的方法
                                     readDataHandler(key);
                                 
                                 //当前的key删除,防止重复处理
                                 iterator.remove();
                             

                         
                         else
                             System.out.println("等待......");
                         
                     
                  catch (IOException e) 
                     e.printStackTrace();
                 
                 finally 
                     System.out.println("==========");
                 

       

       //读取客户端消息
    private   void readDataHandler(SelectionKey key)
        //取到关联的channle
        SocketChannel channel=null;
        try 
        //得到channel
        channel=(SocketChannel)key.channel();
        //创建buffer
        ByteBuffer buffer=ByteBuffer.allocate(1024);
            int count =channel.read(buffer);
            //根据count的值做处理
            if(count>0)
                //把缓存区的数据转成字符串
                String msg=new String(buffer.array());
                //输出信息
                System.out.println("from 客户端:"+msg);
                //向其他客户端转发消息(去掉自己),独立一个方法来处理此业务
                sendInfoToOtherClients(msg,channel);
            
         catch (IOException e) 
            //e.printStackTrace();
            try 
                System.out.println(channel.getRemoteAddress()+"离线了....");
                //取消注册
                key.cancel();;
                //关闭通道
                channel.close();
             catch (IOException ex) 
                ex.printStackTrace();
            
        
    
    //转发消息给其他客户端
    private   void  sendInfoToOtherClients(String msg,SocketChannel self) throws IOException 
        System.out.println("服务器转发消息中......");
        //遍历所有注册到selector上的socketchannel,并排查自己self
        for(SelectionKey key:selector.keys())
            //通过key,取出对应的socketchannel
            Channel targetChannel=key.channel();
             //排查自己
            if(targetChannel instanceof SocketChannel &&targetChannel!=self)
                //转型
                SocketChannel dest=(SocketChannel)targetChannel;
                //将msg存储到buffer中
                ByteBuffer buffer=ByteBuffer.wrap(msg.getBytes());
                //将buffer的数据写入到通道
                dest.write(buffer);
            

        
    

    public static void main(String[] args) throws IOException 
        //创建调用
        CharNioServer charNioServer=new CharNioServer();
        charNioServer.listen();
    

2.客户端

package com.ljf.netty.nio.chat;

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;

/**
 * @ClassName: CharNioClient
 * @Description: TODO
 * @Author: liujianfu
 * @Date: 2022/05/15 17:00:08
 * @Version: V1.0
 **/
public class CharNioClient 
    //定义相关的属性
    private final String HOST="127.0.0.1";
    private final  int PORT=6666;//服务器端口
    private Selector selector;
    private SocketChannel socketChannel;
    private String username;
    //构造器,完成初始化工作
    public CharNioClient() throws IOException 
        selector=Selector.open();
        //连接服务器
        socketChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",PORT));
        //设置非阻塞
        socketChannel.configureBlocking(false);
        //将channel注册到selector
        socketChannel.register(selector, SelectionKey.OP_READ);
        //得到username
        username=socketChannel.getLocalAddress().toString().substring(1);
        System.out.println(username+" is ok ......");

    
    //向服务器发送消息
    public  void  sendInfo(String info) throws IOException 
        info=username+" 说: "+info;
        socketChannel.write(ByteBuffer.wrap(info.getBytes()));
    
    //读取从服务器端回复的消息
    public void readInfo()
        try 
            int readChannels=selector.select();
            if(readChannels>0)//有可以用的通道
                Iterator<SelectionKey> iterator=selector.selectedKeys().iterator();
                while(iterator.hasNext())
                    SelectionKey key=iterator.next();
                    if(key.isReadable())
                        //得到相关的通道
                        SocketChannel sc=(SocketChannel)key.channel();
                        //得到一个Buffer
                        ByteBuffer buffer=ByteBuffer.allocate(1024);
                        //读取
                        sc.read(buffer);
                        //把读到的缓冲区的数据转成字符串
                        String msg=new String(buffer.array());
                        System.out.println("msg:"+msg.trim());
                    
                
                iterator.remove();;//删除当前的selectionKey,防止重复操作
            
            else
                System.out.println("没有可用通道....");
            
         catch (IOException e) 
            e.printStackTrace();
        
    

    public static void main(String[] args) throws IOException 
        CharNioClient charNioClient=new CharNioClient();
        //启动一个线程,每个3秒,读取从服务器发送数据
        new Thread(new Runnable() 
            @Override
            public void run() 
                while(true)
                    charNioClient.readInfo();
                    try 
                        Thread.currentThread().sleep(3000);
                     catch (InterruptedException e) 
                        e.printStackTrace();
                    
                
            
        ).start();
       //发送数据
        Scanner scanner=new Scanner(System.in);
        while (scanner.hasNext())
            String s=scanner.nextLine();
            charNioClient.sendInfo(s);
        
    

3.测试结果,启动多个客户端

1.客户端 2209收到客户端2225发送信息

 2.客户端2225收到2209发送的消息

 3.服务端信息

 

创作打卡挑战赛 赢取流量/现金/CSDN周边激励大奖

以上是关于网络I/o编程模型7 Nio实现聊天室的主要内容,如果未能解决你的问题,请参考以下文章

网络I/o编程模型6 Nio之Selector以及NIO客户服务通讯

网络I/o编程模型5 Nio之buffer的操作和常用方法

Java网络编程和NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型

网络通信之 AIO 和 BIO和 NIO

一站式学习Java网络编程 全面理解BIO/NIO/AIO

五种I/O模型和Java NIO源码分析