Netty实现SSL双向验证完整实例
一、证书准备
要使用ssl双向验证,就必须先要生成服务端和客户端的证书,并相互添加信任,具体流程如下(本人调试这个用例的时候,花了很多时间来验证证书是否正确,以及握手失败的原因,这里证书生成过程只要按流程走,本人能保证绝对没有问题)
现在打开cmd,在哪个目录下打开,证书就会放在哪个目录下:
第一步: 生成Netty服务端私钥和证书仓库命令
keytool -genkey -alias securechat -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass sNetty -storepass sNetty -keystore sChat.jks
- -keysize 2048 密钥长度2048位(这个长度的密钥目前可认为无法被暴力破解)
- -validity 365 证书有效期365天
- -keyalg RSA 使用RSA非对称加密算法
- -dname "CN=localhost" 设置Common Name为localhost
- -keypass sNetty密钥的访问密码为sNetty
- -storepass sNetty密钥库的访问密码为sNetty(其实这两个密码也可以设置一样,通常都设置一样,方便记)
- -keystore sChat.jks 指定生成的密钥库文件为sChata.jks
第二步:生成Netty服务端自签名证书
keytool -export -alias securechat -keystore sChat.jks -storepass sNetty -file sChat.cer
第三步:生成客户端的密钥对和证书仓库,用于将服务端的证书保存到客户端的授信证书仓库中
keytool -genkey -alias smcc -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass sNetty -storepass sNetty -keystore cChat.jks
第四步:将Netty服务端证书导入到客户端的证书仓库中
keytool -import -trustcacerts -alias securechat -file sChat.cer -storepass sNetty -keystore cChat.jks
如果你只做单向认证,则到此就可以结束了,如果是双响认证,则还需继续往下走
第五步:生成客户端自签名证书
keytool -export -alias smcc -keystore cChat.jks -storepass sNetty -file cChat.cer
最后一步:将客户端的自签名证书导入到服务端的信任证书仓库中:
keytool -import -trustcacerts -alias smcc -file cChat.cer -storepass sNetty -keystore sChat.jks
到这里,证书就生成完毕了,我们就可以得到两个jks文件,一个是服务端的sChat.jks ,一个是客户端的cChat.jks ,这两个文件后面初始化sslCOntext的时候会用到
如果还想了解更多可以查看
http://dwj147258.iteye.com/blog/2339934
二、netty服务端
下面就直接贴代码了,首先是实例化SSLContext的类:
- package main.java.com.nionetty;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.security.KeyStore;
- import java.security.NoSuchAlgorithmException;
- import javax.net.ssl.KeyManager;
- import javax.net.ssl.KeyManagerFactory;
- import javax.net.ssl.SSLContext;
- import javax.net.ssl.TrustManager;
- import javax.net.ssl.TrustManagerFactory;
- import org.springframework.core.io.ClassPathResource;
- /**
- * 初始化sslcontext类
- *
- */
- public class ContextSSLFactory {
- private static final SSLContext SSL_CONTEXT_S ;
- private static final SSLContext SSL_CONTEXT_C ;
- static{
- SSLContext sslContext = null ;
- SSLContext sslContext2 = null ;
- try {
- sslContext = SSLContext.getInstance("SSLv3") ;
- sslContext2 = SSLContext.getInstance("SSLv3") ;
- } catch (NoSuchAlgorithmException e1) {
- e1.printStackTrace();
- }
- try{
- if(getKeyManagersServer() != null && getTrustManagersServer() != null ){
- sslContext.init(getKeyManagersServer(), getTrustManagersServer(), null);
- }
- if(getKeyManagersClient() != null && getTrustManagersClient() != null){
- sslContext2.init(getKeyManagersClient(), getTrustManagersClient(), null);
- }
- }catch(Exception e){
- e.printStackTrace() ;
- }
- sslContext.createSSLEngine().getSupportedCipherSuites() ;
- sslContext2.createSSLEngine().getSupportedCipherSuites() ;
- SSL_CONTEXT_S = sslContext ;
- SSL_CONTEXT_C = sslContext2 ;
- }
- public ContextSSLFactory(){
- }
- public static SSLContext getSslContext(){
- return SSL_CONTEXT_S ;
- }
- public static SSLContext getSslContext2(){
- return SSL_CONTEXT_C ;
- }
- private static TrustManager[] getTrustManagersServer(){
- FileInputStream is = null ;
- KeyStore ks = null ;
- TrustManagerFactory keyFac = null ;
- TrustManager[] kms = null ;
- try {
- // 获得KeyManagerFactory对象. 初始化位默认算法
- keyFac = TrustManagerFactory.getInstance("SunX509") ;
- is =new FileInputStream( (new ClassPathResource("main/java/conf/sChat.jks")).getFile() );
- ks = KeyStore.getInstance("JKS") ;
- String keyStorePass = "sNetty" ;
- ks.load(is , keyStorePass.toCharArray()) ;
- keyFac.init(ks) ;
- kms = keyFac.getTrustManagers() ;
- } catch (Exception e) {
- e.printStackTrace();
- }
- finally{
- if(is != null ){
- try {
- is.close() ;
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- return kms ;
- }
- private static TrustManager[] getTrustManagersClient(){
- FileInputStream is = null ;
- KeyStore ks = null ;
- TrustManagerFactory keyFac = null ;
- TrustManager[] kms = null ;
- try {
- // 获得KeyManagerFactory对象. 初始化位默认算法
- keyFac = TrustManagerFactory.getInstance("SunX509") ;
- is =new FileInputStream( (new ClassPathResource("main/java/conf/cChat.jks")).getFile() );
- ks = KeyStore.getInstance("JKS") ;
- String keyStorePass = "sNetty" ;
- ks.load(is , keyStorePass.toCharArray()) ;
- keyFac.init(ks) ;
- kms = keyFac.getTrustManagers() ;
- } catch (Exception e) {
- e.printStackTrace();
- }
- finally{
- if(is != null ){
- try {
- is.close() ;
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- return kms ;
- }
- private static KeyManager[] getKeyManagersServer(){
- FileInputStream is = null ;
- KeyStore ks = null ;
- KeyManagerFactory keyFac = null ;
- KeyManager[] kms = null ;
- try {
- // 获得KeyManagerFactory对象. 初始化位默认算法
- keyFac = KeyManagerFactory.getInstance("SunX509") ;
- is =new FileInputStream( (new ClassPathResource("main/java/conf/sChat.jks")).getFile() );
- ks = KeyStore.getInstance("JKS") ;
- String keyStorePass = "sNetty" ;
- ks.load(is , keyStorePass.toCharArray()) ;
- keyFac.init(ks, keyStorePass.toCharArray()) ;
- kms = keyFac.getKeyManagers() ;
- } catch (Exception e) {
- e.printStackTrace();
- }
- finally{
- if(is != null ){
- try {
- is.close() ;
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- return kms ;
- }
- private static KeyManager[] getKeyManagersClient(){
- FileInputStream is = null ;
- KeyStore ks = null ;
- KeyManagerFactory keyFac = null ;
- KeyManager[] kms = null ;
- try {
- // 获得KeyManagerFactory对象. 初始化位默认算法
- keyFac = KeyManagerFactory.getInstance("SunX509") ;
- is =new FileInputStream( (new ClassPathResource("main/java/conf/cChat.jks")).getFile() );
- ks = KeyStore.getInstance("JKS") ;
- String keyStorePass = "sNetty" ;
- ks.load(is , keyStorePass.toCharArray()) ;
- keyFac.init(ks, keyStorePass.toCharArray()) ;
- kms = keyFac.getKeyManagers() ;
- } catch (Exception e) {
- e.printStackTrace();
- }
- finally{
- if(is != null ){
- try {
- is.close() ;
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- return kms ;
- }
- }
服务端启动类:
- package main.java.com.nionetty;
- import javax.net.ssl.SSLEngine;
- import javax.print.attribute.standard.MediaSize.Engineering;
- import main.java.com.nettyTest.SecureChatServerHandler;
- import io.netty.bootstrap.ServerBootstrap;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.ChannelOption;
- import io.netty.channel.ChannelPipeline;
- import io.netty.channel.EventLoopGroup;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.SocketChannel;
- import io.netty.channel.socket.nio.NioServerSocketChannel;
- import io.netty.handler.logging.LogLevel;
- import io.netty.handler.logging.LoggingHandler;
- import io.netty.handler.ssl.SslHandler;
- import io.netty.handler.timeout.IdleState;
- import io.netty.handler.timeout.IdleStateEvent;
- import io.netty.handler.timeout.IdleStateHandler;
- public class NettySocketServer {
- private static SslHandler sslHandler = null ;
- private EventLoopGroup bossGroup = null ;
- private EventLoopGroup workerGroup = null ;
- public void start(){
- bossGroup = new NioEventLoopGroup() ;
- workerGroup = new NioEventLoopGroup() ;
- try{
- ServerBootstrap serverStrap = new ServerBootstrap() ;
- serverStrap.group(bossGroup , workerGroup)
- .channel(NioServerSocketChannel.class)
- .option(ChannelOption.SO_BACKLOG, 128)
- .option(ChannelOption.SO_KEEPALIVE, true)
- .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 5 * 60)
- .handler(new LoggingHandler(LogLevel.DEBUG))
- .childHandler(new ChannelInitializer<SocketChannel>() {
- @Override
- protected void initChannel(SocketChannel socketChannel) throws Exception {
- ChannelPipeline pie = socketChannel.pipeline() ;
- pie.addLast("decoder" , new MyDecoder()) ;
- pie.addLast("encoder" , new MyEncoder()) ;
- pie.addLast("handler" , new NettySocketSSLHandler()) ;
- SSLEngine engine = ContextSSLFactory.getSslContext().createSSLEngine();
- engine.setUseClientMode(false);
- engine.setNeedClientAuth(true);
- pie.addFirst("ssl", new SslHandler(engine));
- }
- });
- serverStrap.bind(161616).sync() ;
- System.out.println("服务已开启");
- }catch(Exception e){
- e.printStackTrace() ;
- bossGroup.shutdownGracefully() ;
- workerGroup.shutdownGracefully() ;
- }
- }
- private SslHandler getSslHandler(){
- if(sslHandler == null ){
- SSLEngine sslEngine = ContextSSLFactory.getSslContext().createSSLEngine() ;
- sslEngine.setUseClientMode(false) ;
- //false为单向认证,true为双向认证
- sslEngine.setNeedClientAuth(true) ;
- sslHandler = new SslHandler(sslEngine);
- }
- return sslHandler ;
- }
- public static void main(String[] args) {
- new NettySocketServer().start() ;
- }
- }
编码器:
- package main.java.com.nionetty;
- import io.netty.buffer.ByteBuf;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.handler.codec.MessageToByteEncoder;
- import java.nio.ByteBuffer;
- public class MyEncoder extends MessageToByteEncoder<ByteBuffer>{
- @Override
- protected void encode(ChannelHandlerContext ctx, ByteBuffer message,
- ByteBuf out) throws Exception {
- if(message==null){
- return;
- }
- if(message.hasArray()){
- byte[] msg =message.array();
- if(msg == null || msg.length <= 0){
- return;
- }
- out.writeBytes(msg) ;
- }
- }
- }
解码器:
- /*
- * Copyright (C) TD Tech<br>
- * All Rights Reserved.<br>
- *
- */
- package main.java.com.nionetty;
- import io.netty.buffer.ByteBuf;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.handler.codec.ByteToMessageDecoder;
- import java.nio.ByteBuffer;
- import java.util.List;
- /**
- * Create Date: 2014-11-4 下午02:42:21<br>
- * Create Author: lWX232692<br>
- * Description :
- */
- public class MyDecoder extends ByteToMessageDecoder {
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
- List<Object> out) throws Exception {
- //UnpooledUnsafeDirectByteBuf(ridx: 0, widx: 1, cap: 1024)
- if (buffer != null) {
- ByteBuffer msg = null;
- try {
- if(buffer.readableBytes() > 0 ){
- msg = ByteBuffer.allocate(buffer.readableBytes()) ;
- byte[] bb = new byte[buffer.readableBytes()] ;
- buffer.readBytes(bb) ;
- msg.put(bb);
- msg.flip();
- }
- } catch (Exception e) {
- e.printStackTrace();
- msg = null ;
- }
- if (msg != null) {
- out.add(msg);
- }
- }
- }
- }
业务实现类:
- package main.java.com.nionetty;
- import io.netty.channel.Channel;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.channel.SimpleChannelInboundHandler;
- import io.netty.handler.ssl.SslHandler;
- import io.netty.util.concurrent.Future;
- import io.netty.util.concurrent.GenericFutureListener;
- import java.net.InetAddress;
- import java.nio.ByteBuffer;
- import java.util.Arrays;
- public class NettySocketSSLHandler extends SimpleChannelInboundHandler<ByteBuffer>{
- @Override
- public void channelActive(final ChannelHandlerContext ctx) throws Exception {
- // Once session is secured, send a greeting and register the channel to the global channel
- // list so the channel received the messages from others.
- ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
- new GenericFutureListener<Future<Channel>>() {
- @Override
- public void operationComplete(Future<Channel> future) throws Exception {
- if(future.isSuccess()){
- System.out.println("握手成功");
- byte[] array = new byte[]{ (byte)7d, 04} ;
- ByteBuffer bu = ByteBuffer.wrap(array) ;
- ctx.channel().writeAndFlush(bu) ;
- }else{
- System.out.println("握手失败");
- }
- ctx.writeAndFlush(
- "Welcome to " + InetAddress.getLocalHost().getHostName() +
- " secure chat service!\n");
- ctx.writeAndFlush(
- "Your session is protected by " +
- ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite() +
- " cipher suite.\n");
- }
- });
- }
- @Override
- public void handlerAdded(ChannelHandlerContext ctx)
- throws Exception {
- System.out.println("服务端增加");
- }
- @Override
- public void handlerRemoved(ChannelHandlerContext ctx){
- System.out.println("移除:"+ctx.channel().remoteAddress());
- }
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
- System.out.println("Unexpected exception from downstream.");
- ctx.close();
- }
- @Override
- public void messageReceived(ChannelHandlerContext ctx, ByteBuffer msg) throws Exception {
- System.out.println("服务端receive msg ");
- byte[] array = new byte[]{00, 01, 00, 00, 00, 06, 05, 03, (byte)7d, 00, 00, 07} ;
- ByteBuffer bu = ByteBuffer.wrap(array) ;
- ctx.channel().writeAndFlush(bu) ;
- }
- }
三、客户端
客户端实现类
- package main.java.com.nionetty.client;
- import java.net.InetSocketAddress;
- import java.net.SocketAddress;
- import javax.net.ssl.SSLEngine;
- import io.netty.bootstrap.Bootstrap;
- import io.netty.channel.Channel;
- import io.netty.channel.ChannelFuture;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.ChannelOption;
- import io.netty.channel.ChannelPipeline;
- import io.netty.channel.EventLoopGroup;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.SocketChannel;
- import io.netty.channel.socket.nio.NioSocketChannel;
- import io.netty.handler.ssl.SslHandler;
- import main.java.com.nionetty.ContextSSLFactory;
- import main.java.com.nionetty.MyDecoder;
- import main.java.com.nionetty.MyEncoder;
- public class NettySocketClient {
- private EventLoopGroup group ;
- private Channel channel = null ;
- public void connect(String ip , int port){
- group = new NioEventLoopGroup();
- try{
- Bootstrap strap = new Bootstrap();
- strap.group(group)
- .channel(NioSocketChannel.class)
- .option(ChannelOption.TCP_NODELAY, true)
- .option(ChannelOption.SO_KEEPALIVE , true)
- .handler(new ChannelInitializer<SocketChannel>() {
- @Override
- protected void initChannel(SocketChannel socketChannel) throws Exception {
- ChannelPipeline pieple = socketChannel.pipeline() ;
- pieple.addLast("decoder" , new MyClientDecoder()) ;
- pieple.addLast("encoder" , new MyClientEncoder()) ;
- pieple.addLast("handler" , new NettySocketSSLClientHandler()) ;
- SSLEngine engine = ContextSSLFactory.getSslContext2().createSSLEngine();
- engine.setUseClientMode(true);
- pieple.addFirst("ssl", new SslHandler(engine));
- }
- });
- SocketAddress address = new InetSocketAddress(ip, port);
- final ChannelFuture future = strap.connect(address).sync();
- channel = future.awaitUninterruptibly().channel();
- System.out.println("连接成功, channel =" + channel.remoteAddress());
- }catch(Exception e ){
- e.printStackTrace();
- group.shutdownGracefully() ;
- }finally{
- }
- }
- private static SslHandler sslHandlerClient = null ;
- public static SslHandler getSslHandler(){
- if(sslHandlerClient == null){
- SSLEngine sslEngine = ContextSSLFactory.getSslContext2().createSSLEngine() ;
- sslEngine.setUseClientMode(true) ;
- sslHandlerClient = new SslHandler(sslEngine);
- }
- return sslHandlerClient ;
- }
- public static void main(String[] args) {
- new NettySocketClient().connect("192.168.10.256", 161616) ;
- }
- }
编码器:
- package main.java.com.nionetty.client;
- import io.netty.buffer.ByteBuf;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.handler.codec.MessageToByteEncoder;
- import java.nio.ByteBuffer;
- public class MyClientEncoder extends MessageToByteEncoder<ByteBuffer>{
- @Override
- protected void encode(ChannelHandlerContext ctx, ByteBuffer message,
- ByteBuf out) throws Exception {
- if(message==null){
- return;
- }
- if(message .hasArray()){
- byte[] msg =message.array();
- if(msg == null || msg.length <= 0){
- return;
- }
- out.writeBytes(msg);
- }
- }
- }
解码器:
- /*
- * Copyright (C) TD Tech<br>
- * All Rights Reserved.<br>
- *
- */
- package main.java.com.nionetty.client;
- import io.netty.buffer.ByteBuf;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.handler.codec.ByteToMessageDecoder;
- import java.nio.ByteBuffer;
- import java.util.List;
- /**
- * Create Date: 2014-11-4 下午02:42:21<br>
- * Create Author: lWX232692<br>
- * Description :
- */
- public class MyClientDecoder extends ByteToMessageDecoder {
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
- List<Object> out) throws Exception {
- //UnpooledUnsafeDirectByteBuf(ridx: 0, widx: 1, cap: 1024)
- if (buffer != null) {
- ByteBuffer msg = null;
- try {
- if(buffer.readableBytes() > 0 ){
- msg = ByteBuffer.allocate(buffer.readableBytes()) ;
- byte[] bb = new byte[buffer.readableBytes()] ;
- buffer.readBytes(bb) ;
- msg.put(bb);
- msg.flip();
- }
- } catch (Exception e) {
- e.printStackTrace();
- msg = null ;
- }
- if (msg != null) {
- out.add(msg);
- }
- }
- }
- }
业务handler:
- /*
- * Copyright (C) TD Tech<br>
- * All Rights Reserved.<br>
- *
- */
- package main.java.com.nionetty.client;
- import io.netty.buffer.ByteBuf;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.handler.codec.ByteToMessageDecoder;
- import java.nio.ByteBuffer;
- import java.util.List;
- /**
- * Create Date: 2014-11-4 下午02:42:21<br>
- * Create Author: lWX232692<br>
- * Description :
- */
- public class MyClientDecoder extends ByteToMessageDecoder {
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
- List<Object> out) throws Exception {
- //UnpooledUnsafeDirectByteBuf(ridx: 0, widx: 1, cap: 1024)
- if (buffer != null) {
- ByteBuffer msg = null;
- try {
- if(buffer.readableBytes() > 0 ){
- msg = ByteBuffer.allocate(buffer.readableBytes()) ;
- byte[] bb = new byte[buffer.readableBytes()] ;
- buffer.readBytes(bb) ;
- msg.put(bb);
- msg.flip();
- }
- } catch (Exception e) {
- e.printStackTrace();
- msg = null ;
- }
- if (msg != null) {
- out.add(msg);
- }
- }
- }
- }
测试通过,搞了因为在网上没有找到完整的实例,所以因为一个小问题,找了两天都没有找到原因,希望看到的同学能够有所收获
Netty实现SSL双向验证完整实例的更多相关文章
- 请给你的短信验证码接口加上SSL双向验证
序言 去年年底闲来几天,有位同事专门在网上找一些注册型的app和网站,研究其短信接口是否安全,半天下来找到30来家,一些短信接口由于分析难度原因,没有继续深入,但差不多挖掘到20来个,可以肆意被调用, ...
- MQTT研究之EMQ:【SSL双向验证】
EMQ是当前MQTT中,用于物联网领域中比较出色的一个broker,今天我这里要记录和分享的是关于SSL安全通信的配置和注意细节. 环境: 1. 单台Linux CentOS7.2系统,安装一个EMQ ...
- nginx配置ssl双向验证 nginx https ssl证书配置
1.安装nginx 参考<nginx安装>:http://www.ttlsa.com/nginx/nginx-install-on-linux/ 如果你想在单IP/服务器上配置多个http ...
- Struts2的手工自定义验证--完整实例代码
ActionSupport类实现了Validateable.ValidationAware接口, 其中Validateable接口就是验证器接口,该接口有一个validate()方法, validat ...
- MQTT研究之EMQ:【SSL证书链验证】
1. 创建证书链(shell脚本) 客户端证书链关系: rootCA-->chainca1-->chainca2-->chainca3 ca caCert1 caCert2 caCe ...
- netty集成ssl完整参考指南(含完整源码)
虽然我们在内部rpc通信中使用的是基于认证和报文头加密的方式实现安全性,但是有些时候仍然需要使用SSL加密,可能是因为对接的三方系统需要,也可能是由于open的考虑.中午特地测了下netty下集成ss ...
- 前后端API交互数据加密——AES与RSA混合加密完整实例
前言 前段时间看到一篇文章讲如何保证API调用时数据的安全性(传送门:https://blog.csdn.net/ityouknow/article/details/80603617),文中讲到利用R ...
- SSL 通信原理及Tomcat SSL 双向配置
SSL 通信原理及Tomcat SSL 双向配置 目录1 参考资料 .................................................................. ...
- Https双向验证与Springboot整合测试-人来人往我只认你
1 简介 不知不觉Https相关的文章已经写了6篇了,本文将是这个专题的最后一篇,起码近期是最后一篇.前面6篇讲的全都是单向的Https验证,本文将重点介绍一下双向验证.有兴趣的同学可以了解一下之前的 ...
随机推荐
- qt事件机制(转)
学习了一段时间的Qt之后,发现Qt的事件机制和其他语言的机制有些不同.Qt除了能够通过信号和槽机制来实现一些Action动作之外,还可以用对象所带的事件,或者用户自定义的事件来实现对象的一些行为处理. ...
- 海康相机开发(1) SDK安装和开发
1.1 安装包获取 从官网下载最新版本的MVS安装包,支持Windows xp.Windows 7.Windows 8.Windows 10的32和64位系统.安装过程默认即可. 官网下载链接:htt ...
- MongoDB TTL集合与固定集合
1.固定集合 MongoDB可以创建固定长度的集合,可以设置最大的集合空间或最大的集合数.创建集合的语法如下: db.createCollection("collection ...
- C# 监测每个方法的执行次数和占用时间(测试4)
今天也要做这个功能,就百度一下,结果搜索到了自己的文章.一开始还没注意,当看到里面的一个注释的方法时,一开始还以为自己复制错了代码,结果仔细一看网页的文章,我去,原来是自己写的,写的确实不咋地. 把自 ...
- [RN] React Native 查看console打印出来的内容
我们在调试React Native 程序的时候,经常会用到Js的打印语句Console.log等 但我们一脸蒙逼的时候,启动的时候完全看不到打印的内容在哪儿呢??? 原来还要在命令行下输入一个语句才能 ...
- 超级好用的excel导出方法,比phpexcel快n倍,并且无乱码
public function exportToExcel($filename, $tileArray=[], $dataArray=[]){ ini_set('memory_limit','512M ...
- 计蒜客 39279.Swap-打表找规律 (The 2019 ACM-ICPC China Shannxi Provincial Programming Contest L.) 2019ICPC西安邀请赛现场赛重现赛
Swap There is a sequence of numbers of length nn, and each number in the sequence is different. Ther ...
- JS实现Base64编码、解码,即window.atob,window.btoa功能
window.atob(),window.btoa()方法可以对字符串精选base64编码和解码,但是有些环境比如nuxt的服务端环境没法使用window,所以需要自己实现一个base64的编码解码功 ...
- P1099 树网的核——模拟+树形结构
P1099 树网的核 无根树,在直径上找到一条长度不超过s的路径,使得最远的点距离这条路径的距离最短: 首先两遍dfs找到直径(第二次找的时候一定要吧father[]清零) 在找到的直径下枚举长度不超 ...
- 【Beta】软件使用说明——致社长
目录 社团公众号关联上"北航社团帮"小程序 为什么要关联上"北航社团帮"小程序: 如何进行关联: 小程序中的社长相关功能 如何认证成为社长 如何管理社员.增删管 ...