简介

在之前的文章中,我们介绍了在同一个netty程序中支持多个不同的服务,它的逻辑很简单,就是在一个主程序中启动多个子程序,每个子程序通过一个BootStrap来绑定不同的端口,从而达到访问不同端口就访问了不同服务的目的。

但是多个端口虽然区分度够高,但是使用起来还是有诸多不便,那么有没有可能只用一个端口来统一不同的协议服务呢?

今天给大家介绍一下在netty中使用同一端口运行不同协议的方法,这种方法叫做port unification。

SocksPortUnificationServerHandler

在讲解自定义port unification之前,我们来看下netty自带的port unification,比如SocksPortUnificationServerHandler。

我们知道SOCKS的主要协议有3中,分别是SOCKS4、SOCKS4a和SOCKS5,他们属于同一种协议的不同版本,所以肯定不能使用不同的端口,需要在同一个端口中进行版本的判断。

具体而言,SocksPortUnificationServerHandler继承自ByteToMessageDecoder,表示是将ByteBuf转换成为对应的Socks对象。

那他是怎么区分不同版本的呢?

在decode方法中,传入了要解码的ByteBuf in,首先获得它的readerIndex:

int readerIndex = in.readerIndex();

我们知道SOCKS协议的第一个字节表示的是版本,所以从in ByteBuf中读取第一个字节作为版本号:

byte versionVal = in.getByte(readerIndex);

有了版本号就可以通过不同的版本号进行处理,具体而言,对于SOCKS4a,需要添加Socks4ServerEncoder和Socks4ServerDecoder:

case SOCKS4a:
logKnownVersion(ctx, version);
p.addAfter(ctx.name(), null, Socks4ServerEncoder.INSTANCE);
p.addAfter(ctx.name(), null, new Socks4ServerDecoder());
break;

对于SOCKS5来说,需要添加Socks5ServerEncoder和Socks5InitialRequestDecoder两个编码和解码器:

case SOCKS5:
logKnownVersion(ctx, version);
p.addAfter(ctx.name(), null, socks5encoder);
p.addAfter(ctx.name(), null, new Socks5InitialRequestDecoder());
break;

这样,一个port unification就完成了,其思路就是通过传入的同一个端口的ByteBuf的首字节,来判断对应的SOCKS的版本号,从而针对不同的SOCKS版本进行处理。

自定义PortUnificationServerHandler

在本例中,我们将会创建一个自定义的Port Unification,用来同时接收HTTP请求和gzip请求。

在这之前,我们先看一下两个协议的magic word,也就是说我们拿到一个ByteBuf,怎么能够知道这个是一个HTTP协议,还是传输的一个gzip文件呢?

先看下HTTP协议,这里我们默认是HTTP1.1,对于HTTP1.1的请求协议,下面是一个例子:

GET / HTTP/1.1
Host: www.flydean.com

HTTP请求的第一个单词就是HTTP请求的方法名,具体而言有八种方法,分别是:

OPTIONS

返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。

HEAD

向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。

GET

向特定的资源发出请求。注意:GET方法不应当被用于产生“副作用”的操作中,例如在Web Application中。其中一个原因是GET可能会被网络蜘蛛等随意访问。

POST

向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。

PUT

向指定资源位置上传其最新内容。

DELETE

请求服务器删除Request-URI所标识的资源。

TRACE

回显服务器收到的请求,主要用于测试或诊断。

CONNECT

HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。

那么需要几个字节来区分这八个方法呢?可以看到一个字节是不够的,因为我们有POST和PUT,他们的第一个字节都是P。所以应该使用2个字节来作为magic word。

对于gzip协议来说,它也有特殊的格式,其中gzip的前10个字节是header,其中第一个字节是0x1f,第二个字节是0x8b。

这样我们用两个字节也能区分gzip协议。

这样,我们的handler逻辑就出来了。首先从byteBuf中取出前两个字节,然后对其进行判断,区分出是HTTP请求还是gzip请求:

    private boolean isGzip(int magic1, int magic2) {
return magic1 == 31 && magic2 == 139;
} private static boolean isHttp(int magic1, int magic2) {
return
magic1 == 'G' && magic2 == 'E' || // GET
magic1 == 'P' && magic2 == 'O' || // POST
magic1 == 'P' && magic2 == 'U' || // PUT
magic1 == 'H' && magic2 == 'E' || // HEAD
magic1 == 'O' && magic2 == 'P' || // OPTIONS
magic1 == 'P' && magic2 == 'A' || // PATCH
magic1 == 'D' && magic2 == 'E' || // DELETE
magic1 == 'T' && magic2 == 'R' || // TRACE
magic1 == 'C' && magic2 == 'O'; // CONNECT
}

对应的,我们还需要对其添加相应的编码和解码器,对于gzip来说,netty提供了ZlibCodecFactory:

p.addLast("gzipEncoder", ZlibCodecFactory.newZlibEncoder(ZlibWrapper.GZIP));
p.addLast("gzipDecoder", ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));

对于HTTP来说,netty也提供了HttpRequestDecoder和HttpResponseEncoder还有HttpContentCompressor来对HTTP消息进行编码解码和压缩。

p.addLast("decoder", new HttpRequestDecoder());
p.addLast("encoder", new HttpResponseEncoder());
p.addLast("compressor", new HttpContentCompressor());

总结

添加了编码和解码器之后,如果你想自定义一些操作,只需要再添加自定义的对应的消息handler即可,非常的方便。

本文的例子可以参考:learn-netty4

本文已收录于 http://www.flydean.com/38-netty-cust-port-unification/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

netty系列之:一口多用,使用同一端口运行不同协议的更多相关文章

  1. Netty 系列目录

    Netty 系列目录 二 Netty 源码分析(4.1.20) 1.1 Netty 源码(一)Netty 组件简介 2.1 Netty 源码(一)服务端启动 2.2 Netty 源码(二)客户端启动 ...

  2. Netty系列(四)TCP拆包和粘包

    Netty系列(四)TCP拆包和粘包 一.拆包和粘包问题 (1) 一个小的Socket Buffer问题 在基于流的传输里比如 TCP/IP,接收到的数据会先被存储到一个 socket 接收缓冲里.不 ...

  3. Netty 系列(三)Netty 入门

    Netty 系列(三)Netty 入门 Netty 是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更多请参考:Netty Github 和 Netty中文 ...

  4. 【读后感】Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ?

    [读后感]Netty 系列之 Netty 高性能之道 - 相比 Mina 怎样 ? 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商 ...

  5. 1. 彤哥说netty系列之开篇(有个问卷调查)

    你好,我是彤哥,本篇是netty系列的第一篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 本文主要讲述netty系列的整体规划,并调查一下大家喜欢的学习方式. 知识点 ne ...

  6. 2. 彤哥说netty系列之IO的五种模型

    你好,我是彤哥,本篇是netty系列的第二篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 本文将介绍linux中的五种IO模型,同时也会介绍阻塞/非阻塞与同步/异步的区别. ...

  7. 3. 彤哥说netty系列之Java BIO NIO AIO进化史

    你好,我是彤哥,本篇是netty系列的第三篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们介绍了IO的五种模型,实际上Java只支持其中的三种,即BIO/NIO/ ...

  8. 4. 彤哥说netty系列之Java NIO实现群聊(自己跟自己聊上瘾了)

    你好,我是彤哥,本篇是netty系列的第四篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们一起学习了Java中的BIO/NIO/AIO的故事,本章将带着大家一起使 ...

  9. 5. 彤哥说netty系列之Java NIO核心组件之Channel

    你好,我是彤哥,本篇是netty系列的第五篇. 简介 上一章我们一起学习了如何使用Java原生NIO实现群聊系统,这章我们一起来看看Java NIO的核心组件之一--Channel. 思维转变 首先, ...

随机推荐

  1. Typora软件使用教程

    Typora软件的使用教程 Typora软件简介 Typora是一款轻便简洁的Markdown编辑器,支持即时渲染技术,这也是与其他Markdown编辑器最显著的区别.当然重点是免费. Typora编 ...

  2. [ZJCTF 2019]Login

    学了一段时间的堆溢出现在继续做题, 例行检查一下 64位的程序放入ida中 shift+f12查看程序函数 可以看到非常明显的后门程序 查看主函数 发现了程序给的账号和密码,但是没有看到明显的栈溢出漏 ...

  3. how2heap libc2.31学习

    今天是四月十九,想在五月份之前把how2heap中的高版本(2.31)的例子过一遍.所以这个系列目前还是在更新中.如果比较简单就几句话带过了,遇到难一点的会写的详细一点. fastbin_dup 源代 ...

  4. [BUUCTF]PWN——gyctf_2020_borrowstack

    gyctf_2020_borrowstack 附件 步骤: 例行检查,64位程序,开启NX保护 本地运行一下程序,看看大概的情况 64位ida载入,直接从main函数开始看程序, buf可以溢出0x1 ...

  5. C51单片机0~60计数器

    源码 #include<reg51.h> unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f, ...

  6. vue+uniapp实现照录像,相册选择 | 图片裁剪压缩,视频压缩

    一.插件简介 Zhimi-Camera(智密 - 智密 - 相机图册插件-视频/图片选择器)是一个支持拍照,录像,相册选择功能,自带图片裁剪,图片压缩,视频压缩,选择数量限制的uniapp原生插件.平 ...

  7. JAVA生成订单编号工具类

    JAVA根据时间戳和随机数方式生成订单编号工具类 OrderUtils.java package com.util; import java.text.DateFormat; import java. ...

  8. 【LeetCode】1119. Remove Vowels from a String 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 判断字符是否是aeiou 日期 题目地址:https: ...

  9. 【LeetCode】958. Check Completeness of a Binary Tree 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 BFS DFS 日期 题目地址:https://le ...

  10. 【LeetCode】36. Valid Sudoku 解题报告(Python)

    [LeetCode]36. Valid Sudoku 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址 ...