使用nio编写一个简单群聊系统,实现服务器端和客户端的数据通信

    技术2023-11-06  106

    使用NIO技术编写一个简单的群聊技术来了解ServerSocketChannel,SocketChannel,Selector,SelectionKey等技术的使用技巧与方法。

    服务器端的代码:

    服务器端代码 package com.yu.nio; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.Iterator; import java.util.Set; /** * @author yucheng * @create 2020-07-03 13:15 * 使用nio编写一个群聊系统,实现服务器端和客户端的数据通信, * 服务器端的功能:接收客户端发送的信息,并且转发给其余的客户端 */ public class GroupChatServerDemo { //定义属性信息 private ServerSocketChannel serverSocketChannel; //在服务器端监听新的sockerChannnel连接 private Selector selector; //选择器 private static final Integer PORT = 8080; //监听的端口信息 /** * 定义构造方法 */ public GroupChatServerDemo(){ try { serverSocketChannel = ServerSocketChannel.open(); selector = Selector.open(); serverSocketChannel.configureBlocking(false); //设置管道为非阻塞模式 serverSocketChannel.socket().bind(new InetSocketAddress(PORT));//设置端口信息 //把通道注册到selector上,事件为监听事件 serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT); System.out.println("服务器端准备好了"); }catch (Exception e){ e.printStackTrace(); } } /** * 定义监听客户端有连接请求的方法 */ public void seleorSocketChannel(){ try { //循环进行处理 while(true){ //如果连接大于0,有新的连接请求 if(selector.select() > 0){ Iterator<SelectionKey> selectionKeyIterator = selector.selectedKeys().iterator(); while (selectionKeyIterator.hasNext()) { SelectionKey selectionKey = selectionKeyIterator.next(); //如果事件是监听事件,则把事件注册到选择器,事件为读 if(selectionKey.isAcceptable()){ SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector,SelectionKey.OP_READ); //有新的用户上线 System.out.println("有新用户上线了!!!"); } //如果事件是读的事件,把读取获取到的数据信息 if(selectionKey.isReadable()){ readData(selectionKey); } } //移除使用过的selectionKeyIterator selectionKeyIterator.remove(); } } }catch (Exception e){ e.printStackTrace(); } } /** * 读取传输过来的数据 * @param selectionKey 注册关系 */ private void readData(SelectionKey selectionKey) { SocketChannel channel = null; try { channel = (SocketChannel)selectionKey.channel();//获取通道信息 //ByteBuffer buffer = (ByteBuffer)selectionKey.attachment();//获取缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); //开始读取数据信息 if(channel.read(buffer) > 0){ String msg = new String(buffer.array()); System.out.println(channel.getLocalAddress().toString().substring(1)+":说" + msg); //把消息转发给其他客户端 sendOtherClients(channel,msg); } }catch (Exception e){ try { System.out.println(channel.getLocalAddress().toString().substring(1)+"离线了!!!"); selectionKey.cancel();//关闭selectionKey channel.close();//关闭channel } catch (Exception e1) { e1.printStackTrace(); } e.printStackTrace(); } } /** * 转发信息给塔器客户端,但是要排除自己 * @param channel 通道 * @param msg 发送的信息 */ private void sendOtherClients(SocketChannel channel, String msg) { //selector.keys()代表注册到选择器上的所有通道 //selector.selectedKeys()代表监听的时候注册到选择器上的所有通道 try { Set<SelectionKey> keys = selector.keys(); for (SelectionKey key : keys) { SelectableChannel selectableChannel = key.channel(); if(selectableChannel instanceof SocketChannel && selectableChannel != channel) { SocketChannel socketChannel = (SocketChannel)selectableChannel; ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes()); socketChannel.write(byteBuffer); } } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { GroupChatServerDemo groupChatServerDemo = new GroupChatServerDemo(); groupChatServerDemo.seleorSocketChannel(); } }

    客户端的代码:

    package com.yu.nio; 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; /** * @author yucheng * @create 2020-07-03 13:15 * 使用nio编写一个群聊系统,实现服务器端和客户端的数据通信, *服务器端的功能:接收客户端发送的信息,并且转发给其余的客户端 */ public class GroupChatClientDemo { //定义属性信息 private SocketChannel socketChannel;//定义网络IO通道 private Selector selector;//定义选择器 private static final Integer PORT = 8080; //监听的端口信息 private final String HOST = "127.0.0.1";//定义ip地址 private String userName;//用户名 /** * 定义构造方法 */ public GroupChatClientDemo(){ try { socketChannel = SocketChannel.open(new InetSocketAddress(HOST,PORT)); selector = Selector.open(); socketChannel.configureBlocking(false); socketChannel.register(selector,SelectionKey.OP_READ); userName = socketChannel.getLocalAddress().toString().substring(1); System.out.println(userName + ":准备好了"); }catch (Exception e){ e.printStackTrace(); } } /** * 定义发送消息的方法 * @param msg 消息 */ public void sendMsg(String msg){ try { msg = userName + "说:" + msg; ByteBuffer byteBuffer = ByteBuffer.wrap(msg.getBytes()); socketChannel.write(byteBuffer); } catch (IOException e) { e.printStackTrace(); } } /** * 获取服务器转发的消息 */ public void readMsg(){ try { //大于0代表有通道信息 if(selector.select() > 0){ Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while(iterator.hasNext()){ SelectionKey selectionKey = iterator.next(); //获取读的操作 if(selectionKey.isReadable()){ SocketChannel socketChannel = (SocketChannel)selectionKey.channel(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); socketChannel.read(byteBuffer); System.out.println("服务器转发的信息为:" + new String(byteBuffer.array())); } } iterator.remove(); } }catch (Exception e){ e.printStackTrace(); } } public static void main(String[] args) { final GroupChatClientDemo groupChatClientDemo = new GroupChatClientDemo(); //启动一个线程循环去读取服务器转发的信息 new Thread(new Runnable() { public void run() { while(true){ groupChatClientDemo.readMsg(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); //调用发送信息的方法 Scanner scanner = new Scanner(System.in); if(scanner.hasNextLine()){ groupChatClientDemo.sendMsg(scanner.nextLine()); } } }

     运行效果:

     

    Processed: 0.019, SQL: 9