ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

一个NIO的C/S模型

2021-07-31 20:35:02  阅读:161  来源: 互联网

标签:java NIO 一个 模型 selector key import ByteBuffer String


在学习NIO的过程中看了很多博客和其他人的demo,现在算是对NIO有了一些了解,在此记录

本demo中只有服务端是NIO,客户端不是,而且客户端是简单的new线程而不是使用线程池,在生产实践中并不使用

与此同时,本demo涉及JDBC连接,但因为博文重点为C/S因此没有放上来

以下为客户端代码

package com.hsq.client;


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;

public class Client2 {
    //https://www.cnblogs.com/jing99/p/12000371.html
//    public static void client() throws IOException {
    public static void main(String[] args) throws IOException {



        SocketChannel channel = SocketChannel.open();
        InetSocketAddress socketAddress = new InetSocketAddress("localhost", 8080);
        boolean connect = channel.connect(socketAddress);
        if (connect) {
            System.out.println("客户端2启动");
        }

        String uname = "老王";
        String upass = "1";

        while (true) {
            for (int i = 0; i < 5; i++) {
                Thread thread = new Thread();
                thread.start();
//                System.out.println("第" + i + "个客户端2的线程");
                String post = uname + "/" + upass;
                byte[] bytes1 = post.getBytes(StandardCharsets.UTF_8);
                if (bytes1 != null && bytes1.length > 0) {
                    ByteBuffer writeBuffer = ByteBuffer.allocate(bytes1.length);
                    writeBuffer.put(bytes1);
                    writeBuffer.flip();
                    channel.write(writeBuffer);
                }

                ByteBuffer readbuffer = ByteBuffer.allocate(1024);
                int readbytes = channel.read(readbuffer);
                if (readbytes > 0) {
                    readbuffer.flip();
                    byte[] bytes = new byte[readbuffer.remaining()];
                    readbuffer.get(bytes);
                    String reply = new String(bytes);
                    System.out.println("服务器回复 :" + reply);
                }
            }
        }
//        socket.close();

    }
}

以下为服务端代码

package com.hsq.server;


import com.hsq.bean.User;
import com.hsq.jdbc.SqlOperation;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class LoginServer implements Runnable {

    private ServerSocketChannel serverSocketChannel;
    private Selector selector;
    private volatile boolean stop = false;

    public static void main(String[] args) throws IOException {
        int port = 8081;
        LoginServer loginServer = new LoginServer(port);
        new Thread(loginServer, "nioserver-001").start();
    }


    public LoginServer(int port) {
            try {
                serverSocketChannel = ServerSocketChannel.open();
                //获得一个serverChannel
                selector = Selector.open();
                ////创建选择器  获得一个多路复用器
                serverSocketChannel.configureBlocking(false);
                //设置为非阻塞模式 如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式
                serverSocketChannel.socket().bind(new InetSocketAddress(port), 1024);
                //绑定一个端口和等待队列长度
                serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
                //把selector注册到channel,关注链接事件
            } catch (IOException e) {
                e.printStackTrace();
                System.exit(1);
            }
        }


    public void stop() {
        this.stop = true;
    }


    @Override
    public void run() {
        while (!stop) {
            try {
                //无论是否有读写事件发生,selector每隔1s被唤醒一次。如果一定时间内没有事件,就需要做些其他的事情,就可以使用带超时的
                int client = selector.select(1000);
                System.out.println("1:" + client);
                // 阻塞,只有当至少一个注册的事件发生的时候才会继续.
                // int client = selector.select(); 不设置超时时间为线程阻塞,但是IO上支持多个文件描述符就绪
                if (client == 0) {
                    continue;
                }
                System.out.println("2:" + client);
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> it = selectionKeys.iterator();
                SelectionKey key = null;
                while (it.hasNext()) {
                    key = it.next();
                    it.remove();
                    try {
                        //处理事件
                        handle(key);
                    } catch (Exception e) {
                        if (key != null) {
                            key.cancel();
                            if (key.channel() != null) {
                                key.channel().close();
                            }
                        }
                    }
                }
            } catch (Throwable e) {
                e.printStackTrace();
            } finally {

            }
        }

        if (selector != null) {
            // selector关闭后会自动释放里面管理的资源
            try {
                selector.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void handle(SelectionKey key) throws IOException {
        if (key.isValid()) {
            //连接事件
            if (key.isAcceptable()) {
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                // 通过ServerSocketChannel的accept创建SocketChannel实例
                // 完成该操作意味着完成TCP三次握手,TCP物理链路正式建立
                SocketChannel sc = ssc.accept();
                //3次握手
                sc.configureBlocking(false);
                sc.register(selector, SelectionKey.OP_READ);
                //连接建立后关注读事件F
            }

            //读事件
            if (key.isReadable()) {
                SocketChannel socketChannel = (SocketChannel) key.channel();
//                ByteBuffer readbuffer = ByteBuffer.allocate(1024);//写 0 1024  1024
                // 申请直接内存,也就是堆外内存
                ByteBuffer readbuffer = ByteBuffer.allocateDirect(1024);
                // 读取请求码流,返回读取到的字节数
                int readBytes = socketChannel.read(readbuffer);
                // 读取到字节,对字节进行编解码
                if (readBytes > 0) {
                    // 将缓冲区当前的limit设置为position=0,用于后续对缓冲区的读取操作
                    readbuffer.flip();
                    //读写模式反转
                    // 将缓冲区可读字节数组复制到新建的数组中
                    byte[] bytes = new byte[readbuffer.remaining()];
                    readbuffer.get(bytes);
                    String body = new String(bytes);
//                    String body = "登录成功";
//                    res(socketChannel, body);
                    // 这下服务器回复的也是字符串了,且按/分割
                    String[] arr = body.split("/");
                    String name = arr[0];
                    String password = arr[1];
                    //调用查询方法
                    User user = new User(name, password);
                    try {
                        int type = SqlOperation.loginModele(user);
                        String response;
                        if (type >= 1 && type <= 3) {
                            response = "登录成功";
                        } else {
                            response = "登录失败";
                        }
                        System.out.println(response);
                        res(socketChannel, response);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else if (readBytes < 0) {
                    // 链路已经关闭 释放资源
                    key.cancel();
                    socketChannel.close();
                } else {
                    // 没有读到字节忽略
                }
            }

        }
    }

    private void res(SocketChannel channel, String response) throws IOException {
        if (response != null && response.length() > 0) {
            byte[] bytes = response.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            channel.write(writeBuffer);
            System.out.println("res end");
        }
    }


}


标签:java,NIO,一个,模型,selector,key,import,ByteBuffer,String
来源: https://www.cnblogs.com/donkeyAndConcise/p/15085118.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有