Socekt(TCP)服务端传输http协议之NIO非阻塞

本文介绍了如何使用Java NIO(非阻塞I/O)来构建TCP服务器,以处理HTTP协议的客户端请求。在处理过程中,服务器能够应对客户端分批发送的数据,并对数据进行有效整合和响应。示例代码展示了从创建Selector、ServerSocketChannel到接受、读取和响应客户端数据的完整流程。
摘要由CSDN通过智能技术生成

Socekt(TCP)服务端传输http协议之NIO非阻塞

作为TCP服务端有三种写法,一是通过阻塞写法直接实例化socket;二是通过NIO非阻塞法实现;三是通过netty来实现(一般用于高并发这快)。我这边使用的是使用NIO非阻塞法实现。

需求:客户端通过socket发送http协议来传输数据,同时也会出现客户端分批发送数据(如以http协议Post形式发送数据,第一次发送请求头,第二次发送实体)如客户端这样发送数据。
在这里插入图片描述

解决:可以把发送过来的http协议作为一次平常发送过来的字符串进行处理。

TCP服务端代码:

public class NIOServer {
    private InetAddress addr;   //地址
    private int port;  //端口
    private Selector selector;

    private static int BUFF_SIZE = 1024*1024;  //用于缓存客户端发送的数据,同时防止客户端分批次发送数据
    // 通道的保存  key:IP  value:通道
    private static ConcurrentHashMap<String,SelectionKey>  keyPool=new ConcurrentHashMap<>();
    public NIOServer()  {
    }
    /**
     *
     * @param addr  可以传null
     * @param port 端口
     * @throws IOException
     */
    public NIOServer(InetAddress addr, int port)  {
        try{
            this.addr = addr;
            this.port = port;
            startServer();
        } catch (Exception e){
            log.info("启动失败");
        }

    }

    private void startServer() throws IOException {
        // 获得selector及通道(socketChannel)
        this.selector = Selector.open();
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);
        // 绑定地址及端口
        InetSocketAddress listenAddr = new InetSocketAddress(this.addr, this.port);
        serverChannel.socket().bind(listenAddr);
        serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
        log.info("NIOServer运行中...");
        while (true) {
            log.info("服务器等待新的连接和selector选择…");
            this.selector.select();
            // 选择key工作
            Iterator keys = this.selector.selectedKeys().iterator();
            while (keys.hasNext()) {
                SelectionKey key = (SelectionKey) keys.next();
                // 防止出现重复的key,处理完需及时移除
                keys.remove();
                //无效直接跳过
                if (!key.isValid()) {
                    continue;
                }
                if (key.isAcceptable()) {  //是否可接收
                    this.accept(key);  //刚开始连接
                } else if (key.isReadable()) {
                    this.read(key);  //是否可写
                } else if (key.isWritable()) {
                    this.write(key);   //是否可读
                } else if (key.isConnectable()) {  //是否可连接
                    this.connect(key);   //表示那些ip连接
                }
            }
        }
    }

    private void connect(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        if (channel.finishConnect()) {
            // 成功
            log.info("成功连接了");
        } else {
            keyPool.remove(channel.socket().getRemoteSocketAddress()+"");
            // 失败
            log.info("失败连接");
        }
    }

    private void accept(SelectionKey key) throws IOException {
        // 通过选择器键获取服务器套接字通道,通过 accept() 方法获取套接字通道连接
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        SocketChannel channel = serverChannel.accept();
        // 设置套接字通道为非阻塞模式
        channel.configureBlocking(false);
        // 为套接字通道注册选择器,该选择器为服务器套接字通道的选择器,即选择到该 SocketChannel 的选择器
        // 设置选择器关心请求为读操作,设置数据读取的缓冲器容量为处理器初始化时候的缓冲器容量
        channel.register(this.selector, SelectionKey.OP_READ);
        //获取客户端的ip与端口
        SocketAddress remoteAddr =  channel.socket().getRemoteSocketAddress();
        keyPool.put(remoteAddr+"",key);
        log.info("连接到: "+remoteAddr);
    }

    private void read(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(BUFF_SIZE);//开批一个空间存储发送过来的数据,一般用于存储分批发送的数据
        int numRead = channel.read(buffer);
        if (numRead == -1) {
            log.info("关闭客户端连接: "+channel.socket().getRemoteSocketAddress());
            keyPool.remove(channel.socket().getRemoteSocketAddress()+"");
            channel.close();
            return;
        }
        String msg = new String(buffer.array()).trim();
         log.info("客户端发送的数据: "+msg);
        //解析数据后返回数据
        String reMsg= "";  //针对客户端发送的数据进行解析同时反回要发送的数据
        log.info("要返回的数据: "+reMsg);
        // 回复客户端
        channel.write(ByteBuffer.wrap(reMsg.getBytes()));
    }
    //发送的数据
    private void write(SelectionKey key) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocate(BUFF_SIZE);
        byteBuffer.flip();
        SocketChannel clientChannel = (SocketChannel) key.channel();
        while (byteBuffer.hasRemaining()) {
            clientChannel.write(byteBuffer);
        }
        byteBuffer.compact();
    }
}

至此解决完成。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值