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 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Netty 实现socket 连接和WebSocket 同时连接,并相互通信 -> 正文阅读

[网络协议]Netty 实现socket 连接和WebSocket 同时连接,并相互通信

1、maven 依赖

<dependency>
   <groupId>io.netty</groupId>
   <artifactId>netty-all</artifactId>
   <version>4.1.65.Final</version>
</dependency>

2、Server 端

public class HttpServer {
    private int port;

    public HttpServer(int port) {
        this.port = port;
    }

    public void  bind() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new NettyServerChannelInitializer())
                    .option(ChannelOption.SO_BACKLOG, 500)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            // 绑定端口,开始接收进来的连接
            ChannelFuture future = bootstrap.bind(port).sync();
            //关闭channel和块,直到它被关闭
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

NettyServerChannelInitializer

public class NettyServerChannelInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
        channel.pipeline().addLast("socketChoose",new SocketChooseHandle());
        channel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
        channel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));
        channel.pipeline().addLast("commonhandler",new WebSocketHandler());
    }
}

SocketChooseHandle

public class SocketChooseHandle extends ByteToMessageDecoder {
    /** 默认暗号长度为23 */
    private static final int MAX_LENGTH = 23;
    /** WebSocket握手的协议前缀 */
    private static final String WEBSOCKET_PREFIX = "GET /";

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        String protocol = getBufStart(in);
        if (protocol.startsWith(WEBSOCKET_PREFIX)) {
            ctx.pipeline().addBefore("commonhandler","http-codec",new HttpServerCodec());
            // HttpObjectAggregator:将HTTP消息的多个部分合成一条完整的HTTP消息
            ctx.pipeline().addBefore("commonhandler","aggregator",new HttpObjectAggregator(65535));

            // ChunkedWriteHandler:向客户端发送HTML5文件,文件过大会将内存撑爆
            ctx.pipeline().addBefore("commonhandler","http-chunked",new ChunkedWriteHandler());
            ctx.pipeline().addBefore("commonhandler","WebSocketAggregator",new WebSocketFrameAggregator(65535));
            //用于处理websocket, /ws为访问websocket时的uri
            ctx.pipeline().addBefore("commonhandler","ProtocolHandler", new WebSocketServerProtocolHandler("/ws"));
????????????// 此次要移除socket 相关的编码
            ctx.pipeline().remove(StringDecoder.class);
            ctx.pipeline().remove(StringEncoder.class);
        }

        in.resetReaderIndex();
        ctx.pipeline().remove(this.getClass());
    }
    private String getBufStart(ByteBuf in){
        int length = in.readableBytes();
        if (length > MAX_LENGTH) {
            length = MAX_LENGTH;
        }

        // 标记读位置
        in.markReaderIndex();
        byte[] content = new byte[length];
        in.readBytes(content);
        return new String(content);
    }
}
WebSocketHandler
public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {

    private static Map<String, Channel> map = new ConcurrentHashMap<>();
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object object) throws Exception {
        String msg = "";
        if (object instanceof TextWebSocketFrame){
            TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame)object;
            msg = textWebSocketFrame.text();
        }else {
            msg = String.valueOf(object);
        }
        System.out.println("msg:"+msg);

        for (String key: map.keySet()) {
            if (key.equals(ctx.channel().id().toString())){
                continue;
            }
            Channel channel = map.get(key);
            ChannelFuture channelFuture = channel.writeAndFlush(msg);
            if (!channelFuture.isSuccess()) {
                channel.writeAndFlush(new TextWebSocketFrame(msg));
            }
        }

    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded::"+ctx.channel().id().asLongText());
        InetSocketAddress socketAddress = (InetSocketAddress)ctx.channel().remoteAddress();
        String hostAddress = socketAddress.getAddress().getHostAddress();
        logger.info("IP:{}",hostAddress);
        String clientId = ctx.channel().id().toString();
        map.put(clientId,ctx.channel());
        logger.info("map:{}",map.toString());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerRemoved::"+ctx.channel().id().asLongText());
        String clientId = ctx.channel().id().toString();
        map.remove(clientId);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("exceptionCaught::"+cause.getMessage());
        String clientId = ctx.channel().id().toString();
        map.remove(clientId);
        ctx.close();
    }
}

最后通过spring 的CommandLineRunner 实现服务启动

@Component
@Order(1)
public class TaskRunner implements CommandLineRunner {

    private HttpServer httpServer = new HttpServer(10050);

    @Override
    public void run(String... args) throws Exception {
        httpServer.bind();
    }
}

3、Client 端

? ? ? ? 3.1 websocket client

? ? ? ? ? ? ? ? hello.html代码如下 直接游览器允许

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    var webSocket;
    if (window.WebSocket) {
        webSocket = new WebSocket("ws://localhost:10050/ws");

        webSocket.onmessage = function (ev) {
            console.log("=====");
            var res = document.getElementById("response");
            res.value = res.value + "\n" + ev.data;
        }

        webSocket.onopen = function (ev) {
            var req = document.getElementById("response");
            req.value = "链接开启...."+"\n";
        }

        webSocket.onclose = function (ev) {
            var req = document.getElementById("response");
            req.value += "链接关闭...."+"\n";
        }

        function sendMessage(msg) {
            //alert("send?");
            if (!window.WebSocket){
                return;
            }
            if (webSocket.readyState === WebSocket.OPEN) {
                //alert("send!");
                webSocket.send(msg);
                document.getElementById("requestComment").value='';
            }else {
                alert("链接未开启!");
            }
        }

    } else {
        alert("浏览器不支持websocket")
    }
</script>
<form οnsubmit="false">
    <textarea style="height: 300px;width: 300px" id="requestComment" name="reqestcom"></textarea>
    <button type="button" οnclick="sendMessage(this.form.reqestcom.value)">发送</button>
    <textarea style="height: 300px;width: 300px" id="response"></textarea>
    <button type="button" οnclick="document.getElementById('response').value=''">清空</button>
</form>
</body>
</html>

? ? ? ? 3.2 socket client

public class TestClient {
    private final String host;
    private final int port;

    public TestClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void run() throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap().group(bossGroup).channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            channel.pipeline().addLast("decoder",new StringDecoder(CharsetUtil.UTF_8));
                            channel.pipeline().addLast("encoder",new StringEncoder(CharsetUtil.UTF_8));
                            channel.pipeline().addLast(new ClientHandler());
                        }
                    });

            ChannelFuture future = bootstrap.connect(host, port).sync();
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()){
                String msg = scanner.nextLine();
                future.channel().writeAndFlush(msg);
            }
        }finally {
            bossGroup.shutdownGracefully();
        }

    }

    public static void main(String[] args)throws Exception {
        new TestClient("127.0.0.1",10050).run();
    }
}

ClientHandler

/**
 * @author xiaocg
 */
public class ClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        System.out.println(s.trim());
    }
}

4、最终测试截图

websocket 端发送

socket client 接收

socket client 发送

websocket 接收

代码工程详见

https://gitee.com/xiaochangg/study.git?下的data-transfer 工程

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-07-22 14:35:20  更:2021-07-22 14:37:52 
 
开发: 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年12日历 -2024/12/27 14:01:42-

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