当前位置: 首页 > news >正文

网站建设分类怎么做自己的网站卖东西

网站建设分类,怎么做自己的网站卖东西,江西省工程造价信息网官网,wordpress付费预约插件网络协议的基本要素 一个完备的网络协议需要具备哪些基本要素 魔数#xff1a;魔数是通信双方协商的一个暗号#xff0c;通常采用固定的几个字节表示。魔数的作用是防止任何人随便向服务器的端口上发送数据。协议版本号#xff1a;随着业务需求的变化#xff0c;协议可能…网络协议的基本要素 一个完备的网络协议需要具备哪些基本要素 魔数魔数是通信双方协商的一个暗号通常采用固定的几个字节表示。魔数的作用是防止任何人随便向服务器的端口上发送数据。协议版本号随着业务需求的变化协议可能需要对结构或字段进行改动不同版本的协议对应的解析方法也是不同的。所以在生产级项目中强烈建议预留协议版本号这个字段。序列化算法表示数据发送方应该采用何种方法将请求的对象转化为二进制以及如何再将二进制转化为对象报文类型报文可能存在不同的类型。例如在 RPC 框架中有请求、响应、心跳等类型的报文在 IM 即时通信的场景中有登陆、创建群聊、发送消息、接收消息、退出群聊等类型的报文。长度域字段代表请求数据的长度接收方根据长度域字段获取一个完整的报文。请求数据通常为序列化之后得到的二进制流状态状态字段用于标识请求是否正常。一般由被调用方设置。例如一次 RPC 调用失败状态字段可被服务提供方设置为异常状态。保留字段保留字段是可选项为了应对协议升级的可能性可以预留若干字节的保留字段以备不时之需。 --------------------------------------------------------------- ​ | 魔数 2byte | 协议版本号 1byte | 序列化算法 1byte | 报文类型 1byte | ​ --------------------------------------------------------------- ​ | 状态 1byte |       保留字段 4byte     |     数据长度 4byte     | ​ --------------------------------------------------------------- ​ |                   数据内容 长度不定                         | ​ ---------------------------------------------------------------举例如下 如何实现自定义通信协议 Netty 作为一个非常优秀的网络通信框架已经为我们提供了非常丰富的编解码抽象基类帮助我们更方便地基于这些抽象基类扩展实现自定义协议。 Netty 常用编码器类型 MessageToByteEncoder 对象编码成字节流 MessageToMessageEncoder 一种消息类型编码成另外一种消息类型。 Netty 常用解码器类型 ByteToMessageDecoder/ReplayingDecoder 将字节流解码为消息对象 MessageToMessageDecoder 将一种消息类型解码为另外一种消息类型。 编解码器可以分为一次解码器和二次解码器一次解码器用于解决 TCP 拆包/粘包问题按协议解析后得到的字节数据。如果你需要对解析后的字节数据做对象模型的转换这时候便需要用到二次解码器同理编码器的过程是反过来的。 一次编解码器MessageToByteEncoder/ByteToMessageDecoder。 二次编解码器MessageToMessageEncoder/MessageToMessageDecoder。 抽象编码类 通过抽象编码类的继承图可以看出编码类是 ChanneOutboundHandler 的抽象类实现具体操作的是 Outbound 出站数据。 MessageToByteEncoder MessageToByteEncoder 用于将对象编码成字节流MessageToByteEncoder 提供了唯一的 encode 抽象方法我们只需要实现encode 方法即可完成自定义编码。 编码器实现非常简单不需要关注拆包/粘包问题。如下例子展示了如何将字符串类型的数据写入到 ByteBuf 实例ByteBuf 实例将传递给 ChannelPipeline 链表中的下一个 ChannelOutboundHandler。 public class StringToByteEncoder extends MessageToByteEncoderString {Overrideprotected void encode(ChannelHandlerContext channelHandlerContext, String data, ByteBuf byteBuf) throws Exception {byteBuf.writeBytes(data.getBytes());} }encode什么时候被调用的 MessageToByteEncoder 重写了 ChanneOutboundHandler 的 write() 方法其主要逻辑分为以下几个步骤 acceptOutboundMessage 判断是否有匹配的消息类型如果匹配需要执行编码流程如果不匹配直接继续传递给下一个 ChannelOutboundHandler 分配 ByteBuf 资源默认使用堆外内存 调用子类实现的 encode 方法完成数据编码一旦消息被成功编码会通过调用 ReferenceCountUtil.release(cast) 自动释放 如果 ByteBuf 可读说明已经成功编码得到数据然后写入 ChannelHandlerContext 交到下一个节点如果 ByteBuf 不可读则释放 ByteBuf 资源向下传递空的 ByteBuf 对象。 Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {ByteBuf buf null;try {if (acceptOutboundMessage(msg)) { // 1. 消息类型是否匹配SuppressWarnings(unchecked)I cast (I) msg;buf allocateBuffer(ctx, cast, preferDirect); // 2. 分配 ByteBuf 资源try {encode(ctx, cast, buf); // 3. 执行 encode 方法完成数据编码} finally {ReferenceCountUtil.release(cast);}if (buf.isReadable()) {ctx.write(buf, promise); // 4. 向后传递写事件} else {buf.release();ctx.write(Unpooled.EMPTY_BUFFER, promise);}buf null;} else {ctx.write(msg, promise);}} catch (EncoderException e) {throw e;} catch (Throwable e) {throw new EncoderException(e);} finally {if (buf ! null) {buf.release();}} }MessageToMessageEncoder MessageToMessageEncoder 与 MessageToByteEncoder 类似同样只需要实现 encode 方法。 MessageToMessageEncoder常用的实现子类有StringEncoder、LineEncoder、Base64Encoder等。 以StringEncoder为例看下MessageToMessageEncoder 的用法。 源码示例如下将 CharSequence 类型String、StringBuilder、StringBuffer 等转换成 ByteBuf 类型结合 StringDecoder 可以直接实现 String 类型数据的编解码。 Override protected void encode(ChannelHandlerContext ctx, CharSequence msg, ListObject out) throws Exception {if (msg.length() 0) {return;}out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset)); }抽象解码类 解码类是 ChanneInboundHandler 的抽象类实现操作的是 Inbound 入站数据。解码器实现的难度要远大于编码器因为解码器需要考虑拆包/粘包问题。 由于接收方有可能没有接收到完整的消息所以解码框架需要对入站的数据做缓冲操作直至获取到完整的消息。 ByteToMessageDecoder 使用 ByteToMessageDecoderNetty 会自动进行内存的释放我们不用操心太多的内存管理方面的逻辑。 首先我们看下 ByteToMessageDecoder 定义的抽象方法 public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, ListObject out) throws Exception;protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, ListObject out) throws Exception {if (in.isReadable()) {decodeRemovalReentryProtection(ctx, in, out);}} }我们只需要实现一下decode()方法这里的 in 大家可以看到传递进来的时候就已经是 ByteBuf 类型所以我们不再需要强转第三个参数是List类型我们通过往这个List里面添加解码后的结果对象就可以自动实现结果往下一个 handler 进行传递这样我们就实现了解码的逻辑 handler。 为什么存取解码后的数据是用List 由于 TCP 粘包问题ByteBuf 中可能包含多个有效的报文或者不够一个完整的报文。 Netty 会重复回调 decode() 方法直到没有解码出新的完整报文可以添加到 List 当中或者 ByteBuf 没有更多可读取的数据为止。 如果此时 List 的内容不为空那么会传递给 ChannelPipeline 中的下一个ChannelInboundHandler。 static void fireChannelRead(ChannelHandlerContext ctx, CodecOutputList msgs, int numElements) {for (int i 0; i numElements; i ) {//循环传播 有多少调用多少ctx.fireChannelRead(msgs.getUnsafe(i));} }decodeLast ByteToMessageDecoder 还定义了 decodeLast() 方法。为什么抽象解码器要比编码器多一个 decodeLast() 方法呢 因为 decodeLast 在 Channel 关闭后会被调用一次主要用于处理 ByteBuf 最后剩余的字节数据。Netty 中 decodeLast 的默认实现只是简单调用了 decode() 方法。如果有特殊的业务需求则可以通过重写 decodeLast() 方法扩展自定义逻辑。 ReplayingDecoder ByteToMessageDecoder 还有一个抽象子类是 ReplayingDecoder。它封装了缓冲区的管理在读取缓冲区数据时你无须再对字节长度进行检查。因为如果没有足够长度的字节数据ReplayingDecoder 将终止解码操作。ReplayingDecoder 的性能相比直接使用 ByteToMessageDecoder 要慢大部分情况下并不推荐使用 ReplayingDecoder。 MessageToMessageDecoder 与 ByteToMessageDecoder 不同的是 MessageToMessageDecoder 并不会对数据报文进行缓存它主要用作转换消息模型。 比较推荐的做法是使用 ByteToMessageDecoder 解析 TCP 协议解决拆包/粘包问题。解析得到有效的 ByteBuf 数据然后传递给后续的 MessageToMessageDecoder 做数据对象的转换具体流程如下图所示 案例如下 public class MyTcpDecoder extends ByteToMessageDecoder { ​Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, ListObject out) throws Exception {// 检查ByteBuf数据是否完整if (in.readableBytes() 4) {return;} ​// 标记ByteBuf读取索引位置in.markReaderIndex(); ​// 读取数据包长度int length in.readInt(); ​// 如果ByteBuf中可读字节数不足一个数据包长度则将读取索引位置恢复到标记位置等待下一次读取if (in.readableBytes() length) {in.resetReaderIndex();return;} ​// 读取数据ByteBuf data in.readBytes(length); ​// 将数据传递给下一个解码器进行转换转换后的数据对象添加到out中ctx.fireChannelRead(data);} } ​ public class MyDataDecoder extends MessageToMessageDecoderByteBuf { ​Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf msg, ListObject out) throws Exception {// 将读取到的ByteBuf数据转换为自定义的数据对象MyData data decode(msg);if (data ! null) {// 将转换后的数据对象添加到out中表示解码成功out.add(data);}} ​private MyData decode(ByteBuf buf) {// 实现自定义的数据转换逻辑// ...return myData;} }实战案例 如何判断 ByteBuf 是否存在完整的报文 最常用的做法就是通过读取消息长度 dataLength 进行判断。如果 ByteBuf 的可读数据长度小于 dataLength说明 ByteBuf 还不够获取一个完整的报文。在该协议前面的消息头部分包含了魔数、协议版本号、数据长度等固定字段共 14 个字节。 固定字段长度和数据长度可以作为我们判断消息完整性的依据具体编码器实现ByteToMessageDecoder逻辑示例如下 /* --------------------------------------------------------------- | 魔数 2byte | 协议版本号 1byte | 序列化算法 1byte | 报文类型 1byte | --------------------------------------------------------------- | 状态 1byte | 保留字段 4byte | 数据长度 4byte | --------------------------------------------------------------- | 数据内容 长度不定 | ---------------------------------------------------------------*/ Override public final void decode(ChannelHandlerContext ctx, ByteBuf in, ListObject out) {// 判断 ByteBuf 可读取字节if (in.readableBytes() 14) { return;}// 标记 ByteBuf 读指针位置in.markReaderIndex();// 跳过魔数in.skipBytes(2);// 跳过协议版本号in.skipBytes(1);byte serializeType in.readByte();// 跳过报文类型in.skipBytes(1);// 跳过状态字段in.skipBytes(1);// 跳过保留字段in.skipBytes(4);// 验证报文长度不对的话就重置指针位置int dataLength in.readInt();if (in.readableBytes() dataLength) {in.resetReaderIndex(); // 重置 ByteBuf 读指针位置这一步很重要return;}byte[] data new byte[dataLength];in.readBytes(data);// 方式一在解码器中就将数据解码成具体的对象SerializeService serializeService getSerializeServiceByType(serializeType);Object obj serializeService.deserialize(data);if (obj ! null) {out.add(obj);}// 方式二这一步可以不在解码器中处理将请求数据读取到一个新的byteBuf然后丢给handler处理// 创建新的 ByteBuf 对象来存储有效负载数据ByteBuf payload Unpooled.buffer((int) dataSize);// 读取有效负载数据并写入到 payload 中in.readBytes(payload);if (payload.isReadable()) {out.add(payload);} }扩展 什么是字节序 字节顺序是指数据在内存中的存放顺序 使用16进制表示0x12345678。在内存中有两种方法存储这个数字 不同在于对于某一个要表示的值是把值的低位存到低地址还是把值的高位存到低地址。 字节顺序分类 字节的排列方式有两种。例如将一个多字节对象的低位放在较小的地址处高位放在较大的地址处则称小端序反之则称大端序。 典型的情况是整数在内存中的存放方式小端/主机字节序和网络传输的传输顺序大端/网络字节序 1. 网络字节序(Network Order)TCP/IP各层协议将字节序定义为大端Big Endian 因此TCP/IP协议中使用的字节序通常称之为网络字节序。 所以当两台主机之间要通过TCP/IP协议进行通信的时候就需要调用相应的函数进行主机序列Little Endian和网络序Big Endian的转换。这样一来也就达到了与CPU、操作系统无关实现了网络通信的标准化。 2. 主机字节序(Host Order) 整数在内存中保存的顺序它遵循小端Little Endian规则不一定要看主机的CPU架构不过大多数都是小端。 同型号计算机上写的程序在相同的系统上面运行是没有问题的。 结论 Java中虚拟机屏蔽了大小端问题如果是Java之间通信则无需考虑只有在跨语言通信的场景下才需要处理大小端问题。 回到本文的重点我们在编解码时也要注意大小端的问题一般来说如果是小端序的话我们用Netty取值的时候都要用LE结尾的方法。
http://www.yutouwan.com/news/424368/

相关文章:

  • 青浦网站开发广西中小企业网站建设
  • 网站如何做导航条下拉菜单网页设计服务
  • 各类网站网站建设的目标是什么意思h5 移动 网站 开发
  • 做课件最好的素材网站饿了吗外卖网站怎么做
  • 珠宝网站模版怎么做网站注册推广
  • 有什么知名网站是用织梦做的外贸网站产品分析
  • 网络销售网站济南网站优化收费
  • 无锡网站制作优化排名网站建设只有一个空间吗
  • 对网站建设的建议网站上传附件目录格式
  • 一个网站建设的流程网站建设的研发项目
  • 重庆网站设计软件网站建设项目组织图
  • 广州网站建设易企我国省级档案网站建设状况
  • 无锡网站建设公司哪家好wordpress 悬浮网易云
  • 上市的网站设计公司如何做网站代理
  • 常平众展做网站男女做暧昧试看网站
  • 什么网站可以做片头建设网站技术数据策划书
  • 南昌师范学院网站建设的意义和目的无极网络
  • 浙江省建设网站网站301了不知道在哪做的
  • 没有网站怎样做搜索引擎推广域名免费查询
  • 西安网络营销学习网站网络推广这个工作怎么样
  • 网站推广烟台公司电话徐州做网站xlec
  • 网站链接做二维码苏州网站建设caiyiduo
  • 2018淘宝客网站开发广东深圳网站建设微信商城运营
  • 国涟建设集团有限公司网站房地产平面设计主要做什么
  • 大昌建设集团有限公司网站公司介绍模板ppt
  • asp.net网站开发技术做响应式网站的常用尺寸
  • 2017做那个网站能致富小创业公司网站怎么做
  • 定兴县住房和城乡建设局网站学校户网站建设方案
  • 已有网站 需要整改 怎么做如何做中介网站
  • 企业网站建设模版创建网站需要注意的问题