java简单聊天 网络编程

ぐ巨炮叔叔 提交于 2020-01-28 08:35:42

demo1(单线程简单通讯)io流

服务端 

public class Server {
    public static void main(String[] args) throws Exception {
        //5.创建ServerSocket、Socket、OutputStream、InputStream以及端口号并初始化
        ServerSocket serverSocket = null;
        boolean flag = true;
        Socket socket = null;
        OutputStream out = null;
        InputStream in = null;
        try {
            //6.开启服务器并接收客户端发送的数据
            serverSocket = new ServerSocket(8000);//创建服务器套接字
            System.out.println("服务器开启,等待连接...");
            socket = serverSocket.accept();//获得连接
            //接收客户端发送的内容
            in = socket.getInputStream();
            //7.使用输出流对象将信息返回给客户端
            out = socket.getOutputStream();
            System.out.println(socket.getInetAddress().getHostAddress() + "---连接成功");
            out.write("连接成功".getBytes());
            out.flush();
            while (flag) {
                byte[] b = new byte[1024];
                int len = in.read(b);
                System.out.println("client:" + new String(b, 0, len));
                //控制台输入
                Scanner src = new Scanner(System.in);
                String str = src.nextLine();
                out.write(str.getBytes());
                out.flush();
                if (str.equals("bye")) {
                    flag = false;
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //8.关闭流对象、ServerSocket对象以及Socket对象
            in.close();
            out.close();
            serverSocket.close();
            socket.close();
        }
    }
}

客户端

public class Client {
    public static void main(String[] args) throws Exception {
        //1.定义 Socket 对象、OutputStream 对象和一个 InputStream 对象并完成初始化
        Socket socket = null;
        OutputStream out = null;
        InputStream in = null;
        boolean flag = true;
        //定义服务器端的 IP 地址和端口号
        String serverIP = "127.0.0.1";//服务器端 IP 地址
        int port = 8000;//服务器端端口号
        try {
            //2.建立与服务器端的连接并将数据发送到服务器端
            socket = new Socket(serverIP, port);//建立连接
            out = socket.getOutputStream();//发送数据
            while (flag) {
                //3.从输入流中读出服务器的反馈信息并输出到控制台
                byte[] b = new byte[1024];
                in = socket.getInputStream();
                int len = in.read(b);
                String response = new String(b, 0, len);
                System.out.println("server:" + response);
                if ("bye".equals(response)) {
                    flag = false;
                    break;
                }
                //控制台输入
                Scanner src = new Scanner(System.in);
                String str = src.nextLine();
                out.write(str.getBytes());
                out.flush();

            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //4.关闭流及 Socket 对象
            in.close();
            out.close();
            socket.close();
        }
    }
}

demo1(单线程简单通讯)nio

服务端

public class NIOServer {
    // 用于存放客户端SocketChannel集合
    private static  Map<String, SocketChannel> clientMap = new HashMap();

    private  static ByteBuffer sBuffer = ByteBuffer.allocate(1024);

    // 通道管理器(Selector)
    private static Selector selector;

    public static void main(String[] args) throws IOException {
        // 创建通道管理器(Selector)
        selector = Selector.open();

        // 创建通道ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 将通道设置为非阻塞
        serverSocketChannel.configureBlocking(false);

        // 将ServerSocketChannel对应的ServerSocket绑定到指定端口(port)
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(new InetSocketAddress(8000));
        /**
         * 将通道(Channel)注册到通道管理器(Selector),并为该通道注册selectionKey.OP_ACCEPT事件
         * 注册该事件后,当事件到达的时候,selector.select()会返回,
         * 如果事件没有到达selector.select()会一直阻塞。
         */
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务已启动等待连接。。。");
        // 循环处理
        while (true) {
            // 当注册事件到达时,方法返回,否则该方法会一直阻塞
            selector.select();

            // 获取监听事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            // 迭代处理
            while (iterator.hasNext()) {
                // 获取事件
                SelectionKey key = iterator.next();
                // 移除事件,避免重复处理
                iterator.remove();

                // 检查是否是一个就绪的可以被接受的客户端请求连接
                if (key.isAcceptable()) {
                    handleAccept(key);
                } else if (key.isReadable()) {// 检查套接字是否已经准备好读数据
                    handleRead(key);
                }

            }


        }
    }

    /**
     * 处理客户端连接成功事件
     */
    private static void handleAccept(SelectionKey key) throws IOException {
        // 获取客户端连接通道
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        SocketChannel socketChannel = server.accept();
        socketChannel.configureBlocking(false);

        // 信息通过通道发送给客户端
        String msg = "连接成功!";
        socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
        // 启动线程监听客户端输入
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        sBuffer.clear();
                        //控制台输入
                        Scanner src = new Scanner(System.in);
                        String str = src.nextLine();
//                        System.out.println(sendText);
                        sBuffer.put(Charset.forName("UTF-8").encode(str));
                        sBuffer.flip();
                        socketChannel.write(sBuffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        clientMap.put(getClientName(socketChannel), socketChannel);
        // 给通道设置读事件,客户端监听到读事件后,进行读取操作
        socketChannel.register(selector, SelectionKey.OP_READ);
    }

    /**
     * 监听到读事件,读取客户端发送过来的消息
     */
    private static void handleRead(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        // 从通道读取数据到缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(128);
        channel.read(buffer);

        // 输出客户端发送过来的消息
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        System.out.println("client:" + channel.toString() + "   " + msg);
        //转发给其他客户端
//        dispatch(channel, msg);
    }
    /**
     * 转发消息给各个客户端
     */
    private static void dispatch(SocketChannel client, String info) throws IOException {
        if (!clientMap.isEmpty()) {
            for (Map.Entry<String, SocketChannel> entry : clientMap.entrySet()) {
                SocketChannel temp = entry.getValue();
                if (!client.equals(temp)) {
                    sBuffer.clear();
                    sBuffer.put(Charset.forName("UTF-8").encode(getClientName(client) + ":" + info));
                    sBuffer.flip();
                    temp.write(sBuffer);
                }
            }
        }
    }

    /**
     * 生成客户端名字
     */
    private static  String getClientName(SocketChannel client){
        Socket socket = client.socket();
        return "[" + socket.getInetAddress().toString().substring(1) + ":" + Integer.toHexString(client.hashCode()) + "]";
    }

}

客户端


public class NIOClient {
    // 通道管理器(Selector)
    private static Selector selector;
    private static Charset charset = Charset.forName("UTF-8");
 
    public static void main(String[] args) throws IOException {
        // 创建通道管理器(Selector)
        selector = Selector.open();
 
        // 创建通道SocketChannel
        SocketChannel channel = SocketChannel.open();
        // 将通道设置为非阻塞
        channel.configureBlocking(false);
 
        // 客户端连接服务器,其实方法执行并没有实现连接,需要在handleConnect方法中调channel.finishConnect()才能完成连接
        channel.connect(new InetSocketAddress("localhost", 8000));
 
        /**
         * 将通道(Channel)注册到通道管理器(Selector),并为该通道注册selectionKey.OP_CONNECT
         * 注册该事件后,当事件到达的时候,selector.select()会返回,
         * 如果事件没有到达selector.select()会一直阻塞。
         */
        channel.register(selector, SelectionKey.OP_CONNECT);
        // 循环处理
        while (true) {
            /*
             * 选择一组可以进行I/O操作的事件,放在selector中,客户端的该方法不会阻塞,
             * selector的wakeup方法被调用,方法返回,而对于客户端来说,通道一直是被选中的
             * 这里和服务端的方法不一样,查看api注释可以知道,当至少一个通道被选中时。
             */
            selector.select();
 
            // 获取监听事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
 
            // 迭代处理
            while (iterator.hasNext()) {
                // 获取事件
                SelectionKey key = iterator.next();
 
                // 移除事件,避免重复处理
                iterator.remove();
 
                // 检查是否是一个就绪的已经连接服务端成功事件
                if (key.isConnectable()) {
                    handleConnect(key);
                } else if (key.isReadable()) {// 检查套接字是否已经准备好读数据
                    handleRead(key);
                }
 
            }
        }
    }
 
    /**
     * 处理客户端连接服务端成功事件
     */
    private static void handleConnect(SelectionKey key) throws IOException {
        // 获取与服务端建立连接的通道
        SocketChannel channel = (SocketChannel) key.channel();
        if (channel.isConnectionPending()) {
            // channel.finishConnect()才能完成连接
            channel.finishConnect();
        }
 
        channel.configureBlocking(false);
        ByteBuffer sBuffer = ByteBuffer.allocate(1024);
        // 数据写入通道
        String msg = "Hello Server!";
        channel.write(ByteBuffer.wrap(msg.getBytes()));
        // 启动线程监听客户端输入
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        sBuffer.clear();
                        //控制台输入
                        Scanner src = new Scanner(System.in);
                        String str = src.nextLine();
//                        System.out.println(sendText);
                        sBuffer.put(charset.encode(str));
                        sBuffer.flip();
                        channel.write(sBuffer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        // 通道注册到选择器,并且这个通道只对读事件感兴趣
        channel.register(selector, SelectionKey.OP_READ);

    }
 
    /**
     * 监听到读事件,读取客户端发送过来的消息
     */
    private static void handleRead(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
 
        // 从通道读取数据到缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(128);
        channel.read(buffer);
 
        // 输出服务端响应发送过来的消息
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        System.out.println("server:" + msg);
    }
}

 

netty demo

服务端配置

public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel channel) {
        // 基于换行符号
//        channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
        // 解码转String,注意调整自己的编码格式GBK、UTF-8
        channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
        // 编码转String,注意调整自己的编码格式GBK、UTF-8
        channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
        // 在管道中添加我们自己的接收数据实现方法
        channel.pipeline().addLast(new MyServerHandler());
    }

}

server回调处理

public class MyServerHandler extends SimpleChannelInboundHandler {

    public static HashMap<String, Channel> channelList = new HashMap();

    /**
     * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        SocketChannel channel = (SocketChannel) ctx.channel();
        System.out.println("链接报告开始");
        System.out.println("链接报告信息:有一客户端链接到本服务端");
        System.out.println("链接报告IP:" + channel.localAddress().getHostString());
        System.out.println("链接报告Port:" + channel.localAddress().getPort());
        System.out.println("链接报告完毕");
        //通知客户端链接建立成功
        String str = "通知客户端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n";
        ctx.writeAndFlush(str);
    }

    /**
     * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端断开链接" + ctx.channel().localAddress().toString());
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        channelList.put("1",ctx.channel());
        //接收msg消息{与上一章节相比,此处已经不需要自己进行解码}
        System.out.println(" client:" + msg);
        //通知客户端链消息发送成功{不需要通过ByteBuf,可以直接发送字符串}
//        String str = "服务端收到:" + new Date() + " " + msg + "\r\n";
//        ctx.writeAndFlush(str);
    }

    /**
     * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        System.out.println("异常信息:\r\n" + cause.getMessage());
    }

}

server端启动

public class NettyServer {

    public static void main(String[] args) {
        new NettyServer().bing(7397);
    }

    private void bing(int port) {
        //配置服务端NIO线程组
        EventLoopGroup parentGroup = new NioEventLoopGroup(); //NioEventLoopGroup extends MultithreadEventLoopGroup Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
        EventLoopGroup childGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)    //非阻塞模式
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childHandler(new ServerChannelInitializer());
            //绑定端口,同步等待成功.
            ChannelFuture f = b.bind(port).sync();
            System.out.println("netty server start done wait conn...");

            boolean flag=true;

            while (flag) {

                //控制台输入
                Scanner src = new Scanner(System.in);
                String str = src.nextLine();
                Channel channel= MyServerHandler.channelList.get("1");
                channel.writeAndFlush(str);
                if("byebye".equals(str)){
                    f.channel().close();
                    break;
                }
            }
            //等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            childGroup.shutdownGracefully();
            parentGroup.shutdownGracefully();
        }

    }

}

客户端

public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
//        NioEventLoopGroup group = new NioEventLoopGroup();

       try{
           Bootstrap bootstrap = new Bootstrap();

           bootstrap.group(group)
                   .channel(NioSocketChannel.class)
                   .handler(new ChannelInitializer<Channel>() {
                       @Override
                       protected void initChannel(Channel ch) {
//                           ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
                           ch.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
                           // 编码转String,注意调整自己的编码格式GBK、UTF-8
                           ch.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
                           ch.pipeline().addLast(new ClientHandler());
                       }
                   });


           ChannelFuture future = bootstrap.connect("127.0.0.1", 7397).sync();
           boolean flag=true;
           while (flag) {
               //控制台输入
               Scanner src = new Scanner(System.in);
               String str = src.nextLine();
               future.channel().writeAndFlush(str);
               if("bye".equals(str)){
                   future.channel().close();
                   break;
               }
           }
           future.channel().closeFuture().sync();
       }finally {
           group.spliterator();
       }

    }

客户端回调处理

public class ClientHandler extends SimpleChannelInboundHandler {

    /**
     * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        SocketChannel channel = (SocketChannel) ctx.channel();
        //通知客户端链接建立成功
//        String str = "通知客户端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n";
        System.out.println("连接成功");
        ctx.writeAndFlush("");
    }

    /**
     * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端断开链接" + ctx.channel().localAddress().toString());
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        //接收msg消息{与上一章节相比,此处已经不需要自己进行解码}
        System.out.println( "server:" + msg);
        //通知客户端链消息发送成功{不需要通过ByteBuf,可以直接发送字符串}
        String str = "--客户端收到--" + "\r\n";
        ctx.writeAndFlush(str);
    }

    /**
     * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        System.out.println("异常信息:\r\n" + cause.getMessage());
    }

}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!