IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Socekt(TCP)服务端传输http协议之NIO非阻塞 -> 正文阅读

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

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();
    }
}

至此解决完成。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 16:25:55  更:2022-04-06 16:27:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:47:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码