netty入门(六)——实现群聊系统

服务器端代码

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
/**
 * 服务器端启动并监听6667端口,服务器接收客户端消息并实现转发,提示上线和下线
 */
public class GroupChatServer {

    private static Selector selector;
    //定义属性
    private static ServerSocketChannel lisenChannel;//serversocketchannel监听是否有客户端连接
    private static final int PORT=6667;//定义服务器的端口号


    public GroupChatServer() {
        try {
            //得到选择器
            selector= Selector.open();
            //获取监听连接
            lisenChannel=ServerSocketChannel.open();
            //绑定端口
            lisenChannel.socket().bind(new InetSocketAddress(PORT));
            //设置非阻塞模式
            lisenChannel.configureBlocking(false);
            //注册
            lisenChannel.register(selector, SelectionKey.OP_ACCEPT);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //监听方法
    public void lisen(){
        //循环监听

            try {
                while (true){

                    int select = selector.select();
                    if(select>0){//说明有事件处理
                        //遍历得到selectionKey集合
                        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                        while (iterator.hasNext()){
                            //取出selectionKey
                            SelectionKey selectionKey = iterator.next();
                            //分不同的事件类型处理事件
                            if(selectionKey.isAcceptable()){//accept事件
                                SocketChannel accept = lisenChannel.accept();
                                accept.configureBlocking(false);
                                //将此socketchannel注册到selector中
                                accept.register(selector,SelectionKey.OP_READ);
                                //提示已经连接过来了
                                System.out.println(accept.getRemoteAddress()+"上线了");
                            }

                            if(selectionKey.isReadable()){//通道发送read事件,即通道是可读的状态
                                //处理读
                                readData(selectionKey);
                            }
                            //移出此selectionKey,防止重复操作
                            iterator.remove();
                        }
                    }else {
                        System.out.println("等待......");
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {

            }

    }

    /**
     * 读取客户端消息
     * @param key 根据key反向获取用户通道
     */
    public void readData(SelectionKey key) throws IOException {
        //取到关联的channel
        SocketChannel socketChannel=null;


        try {
            //得到channel
            socketChannel= (SocketChannel) key.channel();
            //创建buffer
            ByteBuffer buffer=ByteBuffer.allocate(1024);
            //读取数据,将数据读取到buffer中
            int read = socketChannel.read(buffer);
            //根据read的值处理
            if(read>0){
                //把缓冲区的数据转成字符串
                java.lang.String msg = new java.lang.String(buffer.array());
                //输出该消息
                System.out.println("form客户端:"+msg);

                //转发改消息给其他客户端
                sendInforToOtherCilent(msg,socketChannel);
            }
        } catch (IOException e) {//如果捕获到异常,说明用户离线
            System.out.println(socketChannel.getRemoteAddress()+"离线了。。。");
            //取消注册
            key.cancel();
            //关闭通道
            socketChannel.close();
        }
    }

    /**
     * 转发消息给其他客户端
     * @param msg 转发的消息
     * @param self 转发前自己的channel,由于服务器接收到以后,没有其他客户端这个概念,转发针对的是所有的客户端,
     *             所以再这里要排除自己(不能自己发之后还自己接收)
     */
    public void sendInforToOtherCilent(java.lang.String msg, SocketChannel self) throws IOException {
        System.out.println("服务器转发消息");
        //遍历所有的注册到seletor的socketchannel,并排除自己
        for(SelectionKey key:selector.keys()){
            //通过key取出对应的channel(要转发的channel)
            Channel targetChannel= (Channel) key.channel();
            //排除自己,由于key.channel中有各种类型的channel,所以需要判断一下是否是socketchannel
            if(targetChannel instanceof SocketChannel &&targetChannel!=self){
                SocketChannel target= (SocketChannel) targetChannel;
                //将msg存储到buffer
                ByteBuffer buffer=ByteBuffer.wrap(msg.getBytes());
                //将buffer的数据写入通道
                target.write(buffer);
            }
        }
    }
}

客户端代码:

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;

public class GroupChatClient {
    //定义相关的属性
    private final String HOST="127.0.0.1";
    private final int PORT=6667;
    private Selector selector;
    private SocketChannel socketChannel;
    private String username;

    //构造器,完成初始化
    public GroupChatClient() throws IOException {
        selector = selector.open();
        //连接服务器
        socketChannel= SocketChannel.open(new InetSocketAddress(HOST,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 sendMesToServer(String info){
        try {
            info=username+"说:"+info;
            socketChannel.write(ByteBuffer.wrap(info.getBytes()));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    //从服务器获取消息
    public void recvFromServer(){
        try {
            int select = selector.select();
            if(select>0){//有可以用的通道
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){//考虑的是可能客户端有很多的通道,所以一直while
                    SelectionKey selectionKey = iterator.next();
                    if(selectionKey.isReadable()){
                        //得到相关的通道
                        SocketChannel sc= (SocketChannel) selectionKey.channel();
                        //设置非阻塞
                        sc.configureBlocking(false);
                        //得到一个buffer
                        ByteBuffer buffer=ByteBuffer.allocate(1024);
                        //读取
                        sc.read(buffer);
                        //把读到的缓冲区字符串显示出来
                        String s = new String(buffer.array());
                        System.out.println(s.trim());//去除头尾空格
                        iterator.remove();//删除当前的seletionKey,防止重复操作
                    }else {
                        System.out.println("没有可以用的通道....");
                    }
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws IOException {
        //启动客户端
        GroupChatClient groupChatClient=new GroupChatClient();
        //开启线程,每隔三秒读取可能从服务器端发送的数据
        new Thread(){
            public void run(){
                while (true){//启动一个线程一直读
                    groupChatClient.recvFromServer();
                    //每读一次休眠三秒
                    try {
                        Thread.currentThread().sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        //发送数据给服务器
        Scanner scanner=new Scanner(System.in);
        while (scanner.hasNextLine()){
            String next = scanner.nextLine();
            groupChatClient.sendMesToServer(next);
        }
    }
}

先启动服务端,再启动多个客户端

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值