美文网首页
JAVA NIO模拟HTTP请求

JAVA NIO模拟HTTP请求

作者: 斯科特的一天 | 来源:发表于2019-11-03 16:39 被阅读0次

前言

使用JAVA NIO模拟请求百度,需要了解的概念


JAVA NIO 模拟请求百度

首先用阻塞方式实现,下面是完整代码

/**
 * @author scott
 * @create 2019-10-31
 */
public class SocketClientDemo {
    SocketChannel socketChannel = null;
    try {
        //第1步 建立通道,此时并未连接网络
        //open接口创建的Channel并没有进行连接网络,需要使用connect接口
        socketChannel = SocketChannel.open();
        
        //第2步 设置SocketChannel的阻塞模式,这里是阻塞模式
        //socketChannel默认阻塞,可以不设置
        socketChannel.configureBlocking(true);
        
        //第3步 建立连接,这里请求百度的80端口
        //此时才真正的连上网络
        //阻塞模式,此时线程会阻塞,直到连接成功或者失败
        socketChannel.connect(new InetSocketAddress("www.baidu.com",80));
        
        //第4步 判断是否连接成功
        //调用isConnected接口,当且仅当Channel是open状态且已经连接成功是才会返回true
        if (socketChannel.isConnected()) {
            //说明已经连接上
            //准备请求数据,
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("GET / HTTP/1.1\r\n");
            stringBuffer.append("Host: www.baidu.com\r\n");
            stringBuffer.append("\r\n");
            //\r\n\r\n表示HTTP头部结束
            //将数据写进写入通道的缓冲区
            ByteBuffer writeBuffer = ByteBuffer.wrap(stringBuffer.toString().getBytes());
        
            //将写入通道缓冲区中的数据写入通道
            while (writeBuffer.hasRemaining()) {
                socketChannel.write(writeBuffer);
            }
            //清空准备写入通道的缓冲区
            writeBuffer.clear();
        
            //创建一个读取的缓冲区
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);
        
            StringBuffer result = new StringBuffer();
        
            int read = socketChannel.read(readBuffer);
        
            while (read > 0) {
        
                result.append(new String(readBuffer.array(),0,read,"UTF-8"));
        
                // 清空缓冲区,继续写入
                readBuffer.clear();
        
                //当socketChannel没有数据时,线程阻塞
                read = socketChannel.read(readBuffer);
            }
        
            System.out.println("##########最后的结果\n");
            System.out.println(result.toString());
        } else {
            System.out.println("链接失败");
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //最后要关闭socketChannel
        if (socketChannel != null) {
            try {
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

非阻塞方式实现,SocketChannel + Selector,下面是完整代码

/**
 * @author scott
 * @create 2019-10-31
 */
public class SocketClientDemo {

    protected SocketChannel socketChannel;

    protected Selector selector;

    @Test
    public void socketClientDemo () {
        try {
            //建立通道
            socketChannel = SocketChannel.open();
            //非阻塞通道
            socketChannel.configureBlocking(false);

            //建立连接
            socketChannel.connect(new InetSocketAddress("www.baidu.com",
                                                        80));
            //创建选择器
            selector = Selector.open();

            /**
             * 第二个参数指定该选择器感兴趣的事件
             * 事件主要有:
             * {@link SelectionKey}
             * SelectionKey.OP_CONNECT 连接就绪
             * SelectionKey.OP_READ 读就绪
             * SelectionKey.OP_WRITE 写就绪
             * SelectionKey.OP_ACCEPT 接受就绪
             */
            socketChannel.register(selector,
                                   SelectionKey.OP_CONNECT);

            //监听通道事件
            Boolean flg = true;
            while (flg) {
                /**
                 * selector选择就绪的通道
                 * selector.select(); 没有就绪通道时会阻塞
                 * selector.selectNow(); 没有就绪通道直接立即返回0
                 * selector.select(long timeout); 没有就绪通道,超时后立即返回0
                 */
                int readyChannels = selector.selectNow();
                if (readyChannels == 0) {
                    continue;
                }

                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

                //遍历事件
                while (keyIterator.hasNext()) {
                    SelectionKey key = keyIterator.next();
                    if (key.isConnectable()) {
                        System.out.println("连接就绪\r");
                        SocketChannel channel = (SocketChannel) key.channel();
                        channel.configureBlocking(false);

                        if (channel.finishConnect()) {
                            write(channel);
                            //数据写入完成后,设置监听read
                            channel.register(selector, SelectionKey.OP_READ);
                        }
                    } else if (key.isReadable()) {
                        System.out.println("读取就绪");
                        SocketChannel channel = (SocketChannel) key.channel();

                        channel.configureBlocking(false);

                        if (channel.isConnected()) {
                            read((SocketChannel) key.channel());
                        }
                        //表示完成一次读写操作,退出循环
                        flg = false;
                    }
                    keyIterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //最后关闭通道,关闭选择器
            try {
                if (socketChannel != null) {
                    socketChannel.close();
                }
                if (selector != null) {
                    selector.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 向通道内写入数据
     * @param socketChannel
     * @throws IOException
     */
    private void write(SocketChannel socketChannel) throws IOException {
        if (socketChannel.isConnected()) {
            //写入
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("GET / HTTP/1.1\r\n");
            stringBuffer.append("Host: www.baidu.com\r\n");
            stringBuffer.append("\r\n");

            ByteBuffer writeBuffer = ByteBuffer.wrap(stringBuffer.toString().getBytes());

            System.out.println("开始写入通道");
            System.out.println(stringBuffer.toString());
            while (writeBuffer.hasRemaining()) {
                socketChannel.write(writeBuffer);
            }
        }
    }

    /**
     * 从通道内读取数据
     * @param socketChannel
     * @throws IOException
     */
    private void read(SocketChannel socketChannel) throws IOException {
        if (socketChannel.isConnected()) {
            //读取
            ByteBuffer readBuffer = ByteBuffer.allocate(1024);

            StringBuffer result = new StringBuffer();

            int read = socketChannel.read(readBuffer);

            while (read > 0) {

                result.append(new String(readBuffer.array(),
                                         0,
                                         read,
                                         "UTF-8"));

                // 清空缓冲,继续写入
                readBuffer.clear();

                read = socketChannel.read(readBuffer);
            }

            System.out.println("开始读取数据\r");
            System.out.println(result.toString());
        }
    }
}

相关文章

网友评论

      本文标题:JAVA NIO模拟HTTP请求

      本文链接:https://www.haomeiwen.com/subject/uxocbctx.html