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

使用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 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 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 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());
        }
    }

}

 运行效果:

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

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

 

你可能感兴趣的:(netty,java,netty)