LengthFieldBasedFrameDecoder/LengthFieldPrepender
大多数的协议(私有或者公有),协议头中会携带长度字段,用于标识消息体或者整包消息的长度,例如SMPP、HTTP协议等。由于基于长度解码需求 的通用性,Netty提供了LengthFieldBasedFrameDecoder
/LengthFieldPrepender
,自动屏蔽TCP底层的拆包和粘包问题,只需要传入正确的参数,即可轻松解决“读半包“问题。
发送方使用LengthFieldPrepender给实际内容Content进行编码添加报文头Length字段,接受方使用LengthFieldBasedFrameDecoder进行解码。协议格式如下所示:
+--------+----------+
| Length | Content |
+--------+----------+
Length字段:
表示Conent部分的字节数,例如Length值为100,那么意味着Conent部分占用的字节数就是100。
Length字段本身是个整数,也要占用字节,一般会使用固定的字节数表示。例如我们指定使用2个字节(有符号)表示length,那么可以表示的最大值为32767(约等于32K),也就是说,Content部分占用的字节数,最大不能超过32767。当然,Length字段存储的是Content字段的真实长度。
Content字段:
是我们要处理的真实二进制数据。 在发送Content内容之前,首先需要获取其真实长度,添加在内容二进制流之前,然后再发送。Length占用的字节数+Content占用的字节数,就是我们总共要发送的字节。
事实上,我们可以把Length部分看做报文头,报文头包含了解析报文体(Content字段)的相关元数据,例如Length报文头表示的元数据就是Content部分占用的字节数。当然,LengthFieldBasedFrameDecoder并没有限制我们只能添加Length报文头,我们可以在Length字段前或后,加上一些其他的报文头,此时协议格式如下所示:
+---------+--------+----------+----------+|........ | Length | ....... | Content |+---------+--------+----------+----------+
不过对于LengthFieldBasedFrameDecoder而言,其关心的只是Length字段。因此当我们在构造一个LengthFieldBasedFrameDecoder时,最主要的就是告诉其如何处理Length字段。
2 LengthFieldPrepender参数详解
LengthFieldPrepender提供了多个构造方法,最终调用的都是:
public LengthFieldPrepender(int lengthFieldLength, boolean lengthIncludesLengthFieldLength) {this(lengthFieldLength, 0, lengthIncludesLengthFieldLength);
}public LengthFieldPrepender(ByteOrder byteOrder, int lengthFieldLength,int lengthAdjustment, boolean lengthIncludesLengthFieldLength)
其中:
byteOrder:表示Length字段本身占用的字节数使用的是大端还是小端编码
lengthFieldLength:表示Length字段本身占用的字节数,只可以指定 1, 2, 3, 4, 或 8
lengthAdjustment:表示Length字段调整值
lengthIncludesLengthFieldLength:表示Length字段本身占用的字节数是否包含在Length字段表示的值中。
例如:对于以下包含12个字节的报文
+----------------+| "HELLO, WORLD" |+----------------+
假设我们指定Length字段占用2个字节,lengthIncludesLengthFieldLength指定为false,即不包含本身占用的字节,那么Length字段的值为0x000C(即12)。
+--------+----------------+
+ 0x000C | "HELLO, WORLD" |
+--------+----------------+
如果我们指定lengthIncludesLengthFieldLength指定为true,那么Length字段的值为:0x000E(即14)=Length(2)+Content字段(12)
+--------+----------------+
+ 0x000E | "HELLO, WORLD" |
+--------+----------------+
关于lengthAdjustment字段的含义,参见下面的LengthFieldBasedFrameDecoder。
LengthFieldPrepender尤其值得说明的一点是,其提供了实现零拷贝的另一种思路(实际上编码过程,是零拷贝的一个重要应用场景
)。
在Netty中我们可以使用ByteBufAllocator.directBuffer()创建直接缓冲区实例,从而避免数据从堆内存(用户空间)向直接内存(内核空间)的拷贝,这是系统层面的零拷贝;
也可以使用
CompositeByteBuf
把两个ByteBuf合并在一起,例如一个存放报文头,另一个存放报文体。而不是创建一个更大的ByteBuf,把两个小ByteBuf合并在一起,这是应用层面的零拷贝。
而LengthFieldPrepender,由于需要在原来的二进制数据之前添加一个Length字段,因此就需要对二者进行合并发送。但是LengthFieldPrepender并没有采用CompositeByteBuf,其编码过程如下:
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {//1 获得Length字段的值:真实数据可读字节数+Length字段调整值int length = msg.readableBytes() + lengthAdjustment;if (lengthIncludesLengthFieldLength) {length += lengthFieldLength;}...//2 根据lengthFieldLength指定的值(1、2、3、4、8),创建一个ByteBuffer实例,写入length的值,//并添加到List类型的out变量中switch (lengthFieldLength) {case 1:if (length >= 256) {throw new IllegalArgumentException("length does not fit into a byte: " + length);}out.add(ctx.alloc().buffer(1).order(byteOrder).writeByte((byte) length));break;... case 8:out.add(ctx.alloc().buffer(8).order(byteOrder).writeLong(length));break;default:throw new Error("should not reach here");}//3 最后,再将msg本身添加到List中(msg.retain是增加一次引用,返回的还是msg本身)out.add(msg.retain());}
}
可以看到,LengthFieldPrepender实际上是先把Length字段(报文头)添加到List中,再把msg本身(报文)添加到List中。而在发送数据时,LengthFieldPrepender的父类MessageToMessageEncoder会按照List中的元素下标按照顺序发送,因此相当于间接的把Length字段添加到了msg之前。从而避免了创建一个更大的ByteBuf将Length字段和msg内容合并到一起。作为开发者的我们,在编写编码器的时候,这种一种重要的实现零拷贝的参考思路。
3 LengthFieldBasedFrameDecoder参数详解
LengthFieldBasedFrameDecoder提供了多个构造方法,最终调用的都是:
public LengthFieldBasedFrameDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,int lengthAdjustment, int initialBytesToStrip, boolean failFast) {this( ByteOrder.BIG_ENDIAN, maxFrameLength, lengthFieldOffset, lengthFieldLength,lengthAdjustment, initialBytesToStrip, failFast);
}public LengthFieldBasedFrameDecoder(ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,int lengthAdjustment, int initialBytesToStrip, boolean failFast)
其中:
byteOrder:
表示协议中Length字段的字节是大端还是小端
maxFrameLength:
表示协议中Content字段的最大长度,如果超出,则抛出TooLongFrameException异常。
lengthFieldOffset:
表示Length字段的偏移量,即在读取一个二进制流时,跳过指定长度个字节之后的才是Length字段。如果Length字段之前没有其他报文头,指定为0即可。如果Length字段之前还有其他报文头,则需要跳过之前的报文头的字节数。
lengthFieldLength:
表示Length字段占用的字节数。指定为多少,需要看实际要求,不同的字节数,限制了Content字段的最大长度。
- 如果lengthFieldLength是1个字节,那么限制为128bytes;
- 如果lengthFieldLength是2个字节,那么限制为32767(约等于32K);
- 如果lengthFieldLength是3个字节,那么限制为8388608(约等于8M);
- 如果lengthFieldLength是4个字节,那么限制为2147483648(约等于2G)。
lengthFieldLength与maxFrameLength并不冲突。例如我们现在希望限制报文Content字段的最大长度为32M。显然,我们看到了上面的四种情况,没有任何一个值,能刚好限制Content字段最大值刚好为32M。那么我们只能指定lengthFieldLength为4个字节,其最大限制2G是大于32M的,因此肯定能支持。但是如果Content字段长度真的是2G,server端接收到这么大的数据,如果都放在内存中,很容易造成内存溢出。
为了避免这种情况,我们就可以指定maxFrameLength字段,来精确的指定Content部分最大字节数,显然,其值应该小于lengthFieldLength指定的字节数最大可以表示的值。
lengthAdjustment:
Length字段补偿值。对于绝大部分协议来说,Length字段的值表示的都是Content字段占用的字节数。但是也有一些协议,Length字段表示的是Length字段本身占用的字节数+Content字段占用的字节数。由于Netty中在解析Length字段的值是,默认是认为其只表示Content字段的长度,因此解析可能会失败,所以要进行补偿。在后面的案例3中进行了演示。
主要用于处理Length字段前后还有其他报文头的情况。具体作用请看后面的案例分析。
initialBytesToStrip:
解码后跳过的初始字节数,表示获取完一个完整的数据报文之后,忽略前面指定个数的字节。例如报文头只有Length字段,占用2个字节,在解码后,我们可以指定跳过2个字节。这样封装到ByteBuf中的内容,就只包含Content字段的字节内容不包含Length字段占用的字节。
failFast:
如果为true,则表示读取到Length字段时,如果其值超过maxFrameLength,就立马抛出一个 TooLongFrameException,而为false表示只有当真正读取完长度域的值表示的字节之后,才会抛出 TooLongFrameException,默认情况下设置为true,建议不要修改,否则可能会造成内存溢出。
下面通过再几个案例,来说明这些参数是如何控制LengthFieldBasedFrameDecoder解码行为的,首先我们讨论报文只包含Length字段和Content字段的情况;接着讨论报文头除了包含Length字段,还有其他报文头字段的情况。
3.1 报文只包含Length字段和Content字段
报文只包含Length字段和Content字段时,协议格式如下:
+--------+----------+
| Length | Content |
+--------+----------+
假设Length字段占用2个字节,其值为0x000C,意味着Content字段长度为12个字节,假设其内容为”HELLO, WORLD”。下面演示指定不同解析参数时,解码后的效果。
案例1:
lengthFieldOffset = 0 //因为报文以Length字段开始,不需要跳过任何字节,所以offset为0
lengthFieldLength = 2 //因为我们规定Length字段占用字节数为2,所以这个字段值传入的是2
lengthAdjustment = 0 //这里Length字段值不需要补偿,因此设置为0
initialBytesToStrip = 0 //不跳过初始字节,意味着解码后的ByteBuf中,包含Length+Content所有内容
解码前 (14 bytes) 解码后 (14 bytes)+--------+----------------+ +--------+----------------+| Length | Actual Content |----->| Length | Actual Content || 0x000C | "HELLO, WORLD" | | 0x000C | "HELLO, WORLD" |+--------+----------------+ +--------+----------------+
案例2:
lengthFieldOffset = 0 //参见案例1
lengthFieldLength = 2 //参见案例1
lengthAdjustment = 0 //参见案例1
initialBytesToStrip = 2 //这里跳过2个初始字节,也就是Length字段占用的字节数,意味着解码后的ByteBuf中,只包含Content字段
BEFORE DECODE (14 bytes) AFTER DECODE (12 bytes)+--------+----------------+ +----------------+| Length | Actual Content |----->| Actual Content || 0x000C | "HELLO, WORLD" | | "HELLO, WORLD" |+--------+----------------+ +----------------+
案例3:
lengthFieldOffset = 0 // 参见案例1
lengthFieldLength = 2 // 参见案例1
lengthAdjustment = -2 // Length字段补偿值指定为-2
initialBytesToStrip = 0 // 参见案例1
BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes)+--------+----------------+ +--------+----------------+| Length | Actual Content |----->| Length | Actual Content || 0x000E | "HELLO, WORLD" | | 0x000E | "HELLO, WORLD" |+--------+----------------+ +--------+----------------+
这个案例需要进行一下特殊说明,其Length字段值表示:Length字段本身占用的字节数+Content字节数。所以我们看到解码前,其值为0x000E(14),而不是0x000C(12)。而真实Content字段内容只有2个字节,因此我们需要用:Length字段值0x000E(14),减去lengthAdjustment指定的值(-2),表示的才是Content字段真实长度。
3.2 报文头包含Length字段以外的其他字段,同时包含Content字段
通常情况下,一个协议的报文头除了Length字段,还会包含一些其他字段,例如协议的版本号,采用的序列化协议,是否进行了压缩,甚至还会包含一些预留的头字段,以便未来扩展。这些字段可能位于Length之前,也可能位于Length之后,此时的报文协议格式如下所示:
+---------+--------+----------+----------+
|........ | Length | ....... | Content |
+---------+--------+----------+----------+
当然,对于LengthFieldBasedFrameDecoder来说,其只关心Length字段。按照Length字段的值解析出一个完整的报文放入ByteBuf中,也就是说,LengthFieldBasedFrameDecoder只负责粘包、半包的处理,而ByteBuf中的实际内容解析,则交由后续的解码器进行处理。
下面依然通过案例进行说明:
案例4:
这个案例中,在Length字段之前,还包含了一个Header字段,其占用2个字节,Length字段占用3个字节。
lengthFieldOffset = 2 // 需要跳过Header字段占用的2个字节,才是Length字段
lengthFieldLength = 3 //Length字段占用3个字节
lengthAdjustment = 0 //由于Length字段的值为12,表示的是Content字段长度,因此不需要调整
initialBytesToStrip = 0 //解码后,不裁剪字节
BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes)+----------+----------+----------------+ +----------+----------+----------------+| Header | Length | Actual Content |----->| Header | Length | Actual Content || 0xCAFE | 0x00000C | "HELLO, WORLD" | | 0xCAFE | 0x00000C | "HELLO, WORLD" |+----------+----------+----------------+ +----------+----------+----------------+
案例5:
在这个案例中,Header字段位于Length字段之后
lengthFieldOffset = 0 // 由于一开始就是Length字段,因此不需要跳过
lengthFieldLength = 3 // Length字段占用3个字节,其值为0x000C,表示Content字段长度
lengthAdjustment = 2 // 由于Length字段之后,还有Header字段,因此需要+2个字节,读取Header+Content的内容
initialBytesToStrip = 0 //解码后,不裁剪字节
BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes)+----------+----------+----------------+ +----------+----------+----------------+| Length | Header | Actual Content |----->| Length | Header | Actual Content || 0x00000C | 0xCAFE | "HELLO, WORLD" | | 0x00000C | 0xCAFE | "HELLO, WORLD" |+----------+----------+----------------+ +----------+----------+----------------+
案例6 :
这个案例中,Length字段前后各有一个报文头字段HDR1、HDR2,各占1个字节
lengthFieldOffset = 1 //跳过HDR1占用的1个字节读取Length
lengthFieldLength = 2 //Length字段占用2个字段,其值为0x000C(12),表示Content字段长度
lengthAdjustment = 1 //由于Length字段之后,还有HDR2字段,因此需要+1个字节,读取HDR2+Content的内容
initialBytesToStrip = 3 //解码后,跳过前3个字节
BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes)+------+--------+------+----------------+ +------+----------------+| HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content || 0xCA | 0x000C | 0xFE | "HELLO, WORLD" | | 0xFE | "HELLO, WORLD" |+------+--------+------+----------------+ +------+----------------+
案例7:
//这个案例中,Length字段前后各有一个报文头字段HDR1、HDR2,各占1个字节。Length占用2个字节,表示的是整个报文的总长度。
lengthFieldOffset = 1 //跳过HDR1占用的1个字节读取Length
lengthFieldLength = 2 //Length字段占用2个字段,其值为0x0010(16),表示HDR1+Length+HDR2+Content长度
lengthAdjustment = -3 //由于Length表示的是整个报文的长度,减去HDR1+Length占用的3个字节后,读取HDR2+Content长度
initialBytesToStrip = 3 //解码后,跳过前3个字节
BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes)+------+--------+------+----------------+ +------+----------------+| HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content || 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" | | 0xFE | "HELLO, WORLD" |+------+--------+------+----------------+ +------+----------------+
4 代码案例
了解了LengthFieldPrepender和LengthFieldBasedFrameDecoder的作用后,我们编写一个实际的案例。client发送一个请求,使用LengthFieldPrepender进行编码,server接受请求,使用LengthFieldBasedFrameDecoder进行解码。
我们采用最简单的的通信协议格式,不指定其他报文头:
+--------+-----------+
| Length | Content |
+--------+-----------+
Client端代码
public class LengthFieldBasedFrameDecoderClient {public static void main(String[] args) throws Exception {EventLoopGroup workerGroup = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap(); // (1)b.group(workerGroup); // (2)b.channel(NioSocketChannel.class); // (3)b.option(ChannelOption.SO_KEEPALIVE, true); // (4)b.handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new LengthFieldPrepender(2,0,false));ch.pipeline().addLast(new StringEncoder());ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {// 在于server建立连接后,即发送请求报文public void channelActive(ChannelHandlerContext ctx) {ctx.writeAndFlush("i am request!");ctx.writeAndFlush("i am a anther request!");}});}});// Start the client.ChannelFuture f = b.connect("127.0.0.1", 8080).sync(); // (5)// Wait until the connection is closed.f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();}}
}
在Client端我们自定义了一个ChannelInboundHandler,在连接被激活时,立即发送两个请求:"i am request!”、"i am a anther request!" 。另外注意,我们是分两次调用ctx.writeAndFlush,每次调用都会导致当前请求数据经过StringEncoder进行编码,得到包含这个请求内容ByteBuf实例, 然后再到LengthFieldPrepender进行编码添加Length字段。
因此我们发送的实际上是以下2个报文
Length(13) Content
+--------+-------------------+
+ 0x000D | "i am request!" |
+--------+-------------------+
Length(23) Content
+--------+-----------------------------+
+ 0x0017 | "i am a anther request!" |
+--------+-----------------------------+
Server端代码代码
public class LengthFieldBasedFrameDecoderServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap(); // (2)b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // (3).childHandler(new ChannelInitializer<SocketChannel>() { // (4)@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(16384, 0, 2, 0, 2));ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("receive req:"+msg);}});}});// Bind and start to accept incoming connections.ChannelFuture f = b.bind(8080).sync(); // (7)System.out.println("LengthFieldBasedFrameDecoderServer Started on 8080...");f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}
}
在Server端,我们通过LengthFieldBasedFrameDecoder进行解码,并删除Length字段的2个字节,交给之后StringDecoder转换为字符串,最后在我们的自定义的ChannelInboundHandler进行打印。
分别启动server端与client端,在server端,我们将看到输出:
LengthFieldBasedFrameDecoderServer Started on 8080...
receive req:i am request!
receive req:i am a anther request!
注意这里打印了2次,表示LengthFieldBasedFrameDecoder的确解码成功。
部分读者可能不信任这个结果,那么可以尝试将Server端的LengthFieldBasedFrameDecoder和Client端的LengthFieldPrepender注释掉,再次运行的话,大概率你在server端控制台将看到:
LengthFieldBasedFrameDecoderServer Started on 8080...
receive req:i am request!i am a anther request!
也就是说,在没有使用LengthFieldBasedFrameDecoder和LengthFieldPrepender的情况下,发生了粘包,而服务端无法区分。
5 总结
LengthFieldBasedFrameDecoder作用实际上只是帮我们处理粘包和半包的问题,其只负责将可以构成一个完整有效的请求报文封装到ByteBuf中,之后还要依靠其他的解码器对报文的内容进行解析,例如上面编写的String将其解析为字符串,只不过在后续的解码器中,不需要处理粘包半包问题了,认为ByteBuf中包含的内容肯定是一个完整的报文即可。
对于请求和响应都是字符串的情况下,LengthFieldBasedFrameDecoder/LengthFieldPrepender的威力还没有完全展示出来。我们甚至可以将自定义的POJO类作为请求/响应,在发送数据前对其序列化字节数组,然后通过LengthFieldPrepender为其制定Length;服务端根据Length解析得到二进制字节流,然后反序列化再得到POJO类实例。在下一节我们将会详细的进行介绍。
作者:沉沦2014
链接:https://www.jianshu.com/p/82d223ab74d5
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- docker——Docker没有域名解析的三种原生网络(bridge,host,none)(5)
文章目录1.什么是Docker网络?2.原生网络2.1 Bridge模式2.2 Host模式2.3 Container模式2.4 None模式 1.什么是Docker网络? 安装Docker时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 、host2.原生网络 当你安装完Docker时,它会自动创建三个网络。你可…...
2024/4/28 5:30:09 - 饺子播放器的自定义(弹幕)
饺子播放器的自定义(弹幕) 自定义包含弹幕的播放器准备工作需要的开源库1.饺子视频播放器地址(主角) 2.烈焰弹幕使地址 3.eventBus地址引入饺子播放器后,编写类继承饺子播放器的JzvdStdclass MyJzvd extends JzvdStd实现该有的构造方法,并重写父类getLayoutId方法。换上自己的自…...
2024/4/27 23:03:33 - 【华为云技术分享】我眼中的华为公有云AI平台--ModelArts
【摘要】 时常有人问我,“为什么会有ModelArts存在”,这是一个很有价值的问题,结合国外专家对AWS Sagemaker的评价,我决定写一篇文章,讲讲我对华为云ModelArts全流程AI平台的理解。前言AWS Sagemaker has been a great deal for most data scientists who would want to a…...
2024/4/27 22:47:45 - VS下PDF开发库PODOFO的安装编译、cmake使用的超详细说明
wxleasyland@sina.com2020.5用WIN7 X64,VS2012。参考了https://blog.csdn.net/zxxSsdsd/article/details/9454629一、依赖库:freetype 简要:在“主目录”\builds\windows\vc2010 ,VS2012用这个目录的sln,选成release static方式, 这时就是变成生成lib,“多线程/MT”,生…...
2024/4/28 0:23:49 - Java Base64位加密和解密(包括其他加密参考)
Java Base64位加密和解密(包括其他加密参考)链接https://blog.csdn.net/longguangfu8/article/details/78948213常用加密解密算法【RSA、AES、DES、MD5】介绍和使用https://blog.csdn.net/u013565368/article/details/53081195java 加密工具类(MD5、RSA、AES等加密方式https…...
2024/4/28 15:33:26 - 教育行业的RPA应用:切实提高教务人员效率
教育行业的RPA应用:切实提高教务人员效率眼下,RPA技术在教育行业中正发挥着至关重要的作用。RPA不仅可以降低成本、节省时间,还可以提高组织内部的效率和生产率,为组织提供更多附加值,释放更多的人力并完成其他关键任务。 教育行业必须要跟上科技的发展,甚至需要比科技的…...
2024/4/28 2:05:44 - [NLP --- 25] Jieba 分词及关键词提取
一. 分词1. 全模式import jiebaseg_list = jieba.cut("我来到北京清华大学", cut_all=True) print("Full Mode: " + "/ ".join(seg_list)) # 全模式2. 精确模式import jieba# seg_list = jieba.cut("我来到北京清华大学") seg_list =…...
2024/4/28 13:21:53 - Linux基本操作
第一部分: 1:linux基础简介 1:操作系统(Operating System) 1:操作系统是管理计算机硬件与软件资源的程序 2:典型操作系统 1:UNIX、LINUX、Mac OS X、Windows iOS、Android、MS-DOS 3:操作方式 1:命令行界面(Command Line Interface CLI) 1:用户通过键盘输入指令,计…...
2024/4/28 4:19:17 - 13. 事件(1)
事件:JavaScript 与HTML 之间的交互是通过事件实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码。这种在传统软件工程中被称为观察员模式的模型,支持页面的行为(JavaScript 代码)与页…...
2024/4/28 5:03:52 - VMware安装Linux虚拟机之NAT模式网络配置图文详解
VMware安装CentOS 6.8教程: ---传送门--> 可能是史上最详细的VMware安装Centos 6.8 minimal版图文教程 1.VMware中虚拟网络编辑器设置查看系统网关IP地址2.编辑虚拟机设置,添加网络适配器若没有网络适配器则添加即可3.Windows端网络配置4.虚拟机中网络配置[root@VOS3000 ~…...
2024/4/28 16:26:13 - SpringBoot框架03(整合jdbc,mybatis、异常处理与单元测试、服务端数据校验(实体类和Controller其他参数校验)、Springboot热部署)
一、Spring Boot 整合持久层技术 1.整合 JDBC spring boot整合jdbc: https://blog.csdn.net/weixin_44911308/article/details/106372179 2.整合mybatis spring boot 整合mybatis https://blog.csdn.net/weixin_44911308/article/details/106410424 二、SpringBoot 中异常处理…...
2024/4/28 15:22:45 - 计算机视觉知识点-select search
select serach 提出的时间是2013年,面对分割问题,这个方法得到了极高的recall, 13年的rcnn的区域选取方法就是用的这个方法. select serach 假设区域分割依赖*颜色*/*纹理*/*大小*/*形状*传统方法最好能做到过分割,就是不希望有一个区域有两个物体的情况,但是仅仅依靠颜色是不行…...
2024/4/28 17:36:00 - [c++] constexpr用法
constexpr 表示在编译期就求值。 -----------------------#define PI 3.14; constexpr int Inc(int i){return i+1; } int func(int i){return i+1; }constexpr int a = 10; constexpr int a = sizeof(int); constexpr int a = Inc(1); constexpr int a = PI;//上述a都是合法的…...
2024/4/27 8:18:37 - 数据库系统上,慕课第五讲测验答案
1单选(1分) 集合R与S的"差"表示为________ 得分/总分 A. B. C. 1.00/1.00 D. 2单选(1分) 集合R与S的笛卡尔积表示为________ 得分/总分 A. B. C. 1.00/1.00 D. 3单选(1分) 已知关系Student(S#,Sname,Sage,D#), Dept(D#, Dname,Dean),利用元组演算查询“计算机系所有学…...
2024/4/24 10:00:01 - 计算机网络方向
计算机网络 计算机网络运行原理 计算机网络搭建 计算机网络安全...
2024/4/24 9:59:58 - TCP和SCTP协议的详解
TCP 1、TCP:(Transmission Control Protocol)传输控制协议,是一种面向连接的可靠协议,工作在传输层。 2、面向连接:指的是在发送数据之前,双方都要保证链路的畅通,而且双方都是可以发送数据的。 3、TCP协议的作用:就是用来保证面向连接。 4、报文格式以及每个字段的作…...
2024/4/24 10:00:00 - input按顺序上传文件
this.ImgArr = [] this.ImgNum = 0/*** 上传文件* **/ fileUpload(event){let _this = this;_this.initCallBack(); //初始化腾讯云上传this.fileInfo = Array.from(event.target.files)this.fileInfo.forEach((val,index)=>{let file = new File([val], new Date().getTime…...
2024/4/24 9:59:56 - RHEL6.5(RedHat6.5)双网卡绑定bonding配置文档
1.最好先关闭NetworkManager,并将其设置为开机不启动,方法自行百度即可。 2.修改配置文件: 2.1 新建bond0文件: vim /etc/sysconfig/network-scripts/ifcfg-bond0 DEVICE=bond0 ONBOOT=yes BOOTPROTO=static TYPE=Ethernet IPADDR=10.12.63.**(自己使用的IP地址) NETMASK=2…...
2024/4/24 9:59:56 - 手把手教你使用 CompletableFuture
背景在jdk5中,我们通过使用Future和Callable,可以在任务执行完毕后得到任务执行结果。可以使用isDone检测计算是否完成,使用cancle停止执行任务,使用阻塞方法get阻塞住调用线程来获取返回结果,使用阻塞方式获取执行结果,有违异步编程的初衷,而且Future的异常只能自己内部…...
2024/4/24 9:59:54 - LQH入职第16天--创建账号
1、创建账号 db := libs.GetMysqlDb("document")db.Begin()defer func() {if err != nil {db.Rollback()} else {db.Commit()}}()注释: (1)获取数据库的orm对象 (2)db.Begin()数据库开始 (3)db.Rollback()数据库回滚 (4)db.Commit()数据库提交 尝试从数据库…...
2024/4/24 9:59:54
最新文章
- Web程序设计-实验04 JavaScript对象
题目 【实验主题】 个人所得税计算 【实验任务】 1、根据【任务提示】和【参考资源】材料,自学2012版月工资、年终奖个人所得税计算规则。 2、新建 .js文件,以JSON格式定义个人所得税对象。 其中属性涉及三个层次: 1)第一层…...
2024/4/29 1:28:32 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/3/20 10:50:27 - 蓝桥杯第十五届抱佛脚(十)贪心算法
蓝桥杯第十五届抱佛脚(十)贪心算法 贪心算法基本概念 贪心算法是一种在算法设计中常用的方法,它在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。 贪…...
2024/4/19 0:49:59 - 使用阿里云试用Elasticsearch学习:1.3 基础入门——搜索-最基本的工具
现在,我们已经学会了如何使用 Elasticsearch 作为一个简单的 NoSQL 风格的分布式文档存储系统。我们可以将一个 JSON 文档扔到 Elasticsearch 里,然后根据 ID 检索。但 Elasticsearch 真正强大之处在于可以从无规律的数据中找出有意义的信息——从“大数…...
2024/4/23 6:25:50 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/4/28 13:52:11 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/4/28 3:28:32 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/4/26 23:05:52 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/4/28 13:51:37 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/4/27 17:58:04 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/4/27 14:22:49 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/4/28 1:28:33 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/4/28 15:57:13 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/4/27 17:59:30 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/4/25 18:39:16 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/4/28 1:34:08 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/4/26 19:03:37 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/4/28 1:22:35 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/4/25 18:39:14 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/4/26 23:04:58 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/4/27 23:24:42 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/4/28 5:48:52 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/4/26 19:46:12 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/4/27 11:43:08 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/4/27 8:32:30 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57