写TCP 服务器和客户端

vert.x能够使你很容易写出非阻塞的TCP客户端和服务器

创建一个TCP服务

最简单的创建TCP服务的方法是使用默认的配置:如下

  1. NetServer server = vertx.createNetServer();

配置TCP服务

如果你不想使用默认配置,当创建服务时,可以通过传递NetServerOptions实例进行配置

  1. NetServerOptions options = new NetServerOptions().setPort(4321);
  2. NetServer server = vertx.createNetServer(options);

开启服务监听

为了告诉服务去监听输入的请求,你可以使用listen方法

可以告诉服务监听的主机和端口号通过在在options中指定

  1. NetServer server = vertx.createNetServer();
  2. server.listen();

或者通过指定端口号和主机在调用listen时,忽略options中的配置

  1. NetServer server = vertx.createNetServer();
  2. server.listen(1234, "localhost");

默认的主机是0.0.0.0,意味着监听一切可以用的地址。默认的主机是0,是一个特殊值,可以指挥服务去发现任意一个没有用的本地端口并且使用它

实际的绑定是异步的,所以服务可能不在监听状态,直到对listen方法的调用返回之后。

如果你想被通知当服务实际监听时,你可以提供一个处理器对于listen的调用。例如:

  1. NetServer server = vertx.createNetServer();
  2. server.listen(1234, "localhost", res -> {
  3. if (res.succeeded()) {
  4. System.out.println("Server is now listening!");
  5. } else {
  6. System.out.println("Failed to bind!");
  7. }
  8. });

监听任意端口

如果0正在被用于监听端口,服务将发现一个任意的未用的端口去监听

为了弄清楚服务监听的实际端口,可以调用actualPort 方法

  1. NetServer server = vertx.createNetServer();
  2. server.listen(0, "localhost", res -> {
  3. if (res.succeeded()) {
  4. System.out.println("Server is now listening on actual port: " + server.actualPort());
  5. } else {
  6. System.out.println("Failed to bind!");
  7. }
  8. });

得到通知对于进来的连接

为了被通知,当一个连接建立时,你需要设置一个connectHandler

  1. NetServer server = vertx.createNetServer();
  2. server.connectHandler(socket -> {
  3. // Handle the connection in here
  4. });

当一个连接建立,handle将会被调用通过实例NetSocket

这是一个类似socket接口 对于实际的连接,允许你读和写数据,也可以做各种不同的事情,例如关闭socket

从socket中读数据

为了能够从socket中读书节,你需要设置 handler 在socket上

handler将会被Buffer实例调用,当每次socket接收数据时。

  1. NetServer server = vertx.createNetServer();
  2. server.connectHandler(socket -> {
  3. socket.handler(buffer -> {
  4. System.out.println("I received some bytes: " + buffer.length());
  5. });
  6. });

给socket写数据

你可以给socket写数据使用write

  1. Buffer buffer = Buffer.buffer().appendFloat(12.34f).appendInt(123);
  2. socket.write(buffer);
  3.  
  4. // Write a string in UTF-8 encoding
  5. socket.write("some data");
  6.  
  7. // Write a string using the specified encoding
  8. socket.write("some data", "UTF-16");

写操作时异步的,可能不发生,直到对write的调用返回时。

关闭handler

如果你想被通知当socket关闭时,可以设置一个 closeHandler在它上面

  1. socket.closeHandler(v -> {
  2. System.out.println("The socket has been closed");
  3. });

处理异常

你可以设置一个exceptionHandler 对于接收的任意的发生在socket上面的异常,

你可以设置一个exceptionHandler 对于接收的任意的,发生在连接传递给connectHandler之前的异常,例如,在TLS 握手期间

Event bus 写处理器

每个socket 将会自动注册一个处理器在event bus上,当任何buffers被接收在这个处理器上,它将会写它们给它自己。

这能够使你写数据给一个socket,socket潜在的可能在一个完全不同的verticle中,甚至在完全不同的Vert.x实例中,通过发送buffer给这个地址去处理。

handler地址可以通过writeHandlerID 获得。

本地和远程地址

NetSocket本地地址可以通过localAddress获得。

NetSocket本地地址可以通过remoteAddress获得。

发送文件和资源从类路径。

文件和类路径下的资源可以直接写进socket,通过使用sendFile。这是很有效的发送文件的方式,因为它能直接被操作系统内核处理,操作系统支持。

请看这一章节,关于serving files from the classpath 对于类路径解决方法的限制或者使之失效

  1. socket.sendFile("myfile.dat");

流sockets

NetSocket实例也是ReadStreamWriteStream实例,所以它们能够被用来推拉数据给/从其它的读取流和写入流

更多的信息看这个章节streams and pumps

升级SSL/TLS连接

一个非SSL/TLS连接能够升级到SSL/TLS连接使用 upgradeToSsl

服务或者客户端对于SSL/TLS必须配置使工作正确,请看这一章节chapter on SSL/TLS得到更多的信息。

关闭TCP服务

调用close 去关闭服务,关闭服务可以关闭任何打开的连接,并且释放所有的服务资源

关闭是异步的, 可能一直没有完成直到调用返回。如果你想被通知当这个实际的连接关闭时,那么你可以传递一个handler

handler将会被调用当关闭完全完成

  1. server.close(res -> {
  2. if (res.succeeded()) {
  3. System.out.println("Server is now closed");
  4. } else {
  5. System.out.println("close failed");
  6. }
  7. });

自动清理verticles

如果你创建了TCP服务和客户端从verticles内部,服务和客户端将会自动关闭当verticle发布时。

可扩展性-共享TCP服务

任何TCP服务的处理器总是执行在相同的event loop 线程上

这意味着,如果你运行了一个服务在很多核心上,你仅仅有一个实例发布,你将至少有一个核可用在服务上

为了使你的服务利用更多的核,你需要发布更多的服务实例。

你可以实例化更多的实例程序在你的代码中

  1. for (int i = 0; i < 10; i++) {
  2. NetServer server = vertx.createNetServer();
  3. server.connectHandler(socket -> {
  4. socket.handler(buffer -> {
  5. // Just echo back the data
  6. socket.write(buffer);
  7. });
  8. });
  9. server.listen(1234, "localhost");
  10. }

或者,如果你使用verticles,你可以简单的发布更多的服务实例,通过使用 -instances 选项在命令行中

vertx run com.mycompany.MyVerticle -instances 10

或者在程序中发布你的verticle

  1. DeploymentOptions options = new DeploymentOptions().setInstances(10);
  2. vertx.deployVerticle("com.mycompany.MyVerticle", options);

一旦你做了这些,你将发现服务工作的作用明显比以前,但是所有的内核都能够使用,更多的工作能够被处理

你可能会问自己,怎么有超过一个服务监听在相同的主机和端口号上,是否会有端口冲突当你发布超过一个实例时?

Vert.x 在这里做了一些魔法

当你发布另一个服务时在相同的已经存在服务的主机和端口号上,它实际上不需要创建一个新的服务监听在相同的服务和端口上。

相反,它内部仅维持一个单一的服务,当连接进来时,它分配它们给连接处理器用轮询的方式,

接下来,Vert.x TCP服务可以扩展可用的核心 ,每个实例保持单一的线程

创建一个TCP客户端

最简单的创建TCP客户端的方式是,全部使用默认选项

  1. NetClient client = vertx.createNetClient();

配置TCP 客户端

如果你不想使用默认的配置,当创建客户端时,可以通过传递一个NetClientOptions实例进行配置。

  1. NetClientOptions options = new NetClientOptions().setConnectTimeout(10000);
  2. NetClient client = vertx.createNetClient(options);

建立连接

为了与服务建立一个连接,使用connect ,指定端口号和服务主机,一个handler 将会被调用,一个结果包含NetSocket 当连接成功时,或者当连接失败时。

  1. NetClientOptions options = new NetClientOptions().setConnectTimeout(10000);
  2. NetClient client = vertx.createNetClient(options);
  3. client.connect(4321, "localhost", res -> { //handler with a result
  4. if (res.succeeded()) {
  5. System.out.println("Connected!");
  6. NetSocket socket = res.result();
  7. } else {
  8. System.out.println("Failed to connect: " + res.cause().getMessage());
  9. }
  10. });

配置连接

一个客户端可以配置自动重新获得与服务的连接,配置如下:

setReconnectInterval 和 setReconnectAttempts.

  1. 注意:目前Vert.x 将不会试图重新获得连接如果一个连接失败,重新获得的连接和时间间隔仅仅作用于初始连接
  1. NetClientOptions options = new NetClientOptions().
  2. setReconnectAttempts(10).
  3. setReconnectInterval(500);
  4.  
  5. NetClient client = vertx.createNetClient(options);

默认情况下,多重连接尝试时无效的。

网络日志活动

调试目的,网络活动可以被打印成日志

  1. NetServerOptions options = new NetServerOptions().setLogActivity(true);
  2.  
  3. NetServer server = vertx.createNetServer(options);

对于客户端

  1. NetClientOptions options = new NetClientOptions().setLogActivity(true);
  2.  
  3. NetClient client = vertx.createNetClient(options);

网络活动被打印成日志通过Netty,使用DEBUG级别,用io.netty.handler.logging.LoggingHandler 的名字。当使用网络活动日志,这里有几件事情需要牢记心中

*日志不是通过Vert.x执行而是Netty

*这不是一个生产特性

你应该阅读Netty logging 部分

配置服务和客户端工作于 SSL/TLS

TCP客户端和服务器能够被配置使用 Transport Layer Security  更早的TLS版本以SSL知名

服务端和客户端APIs 通过是否使用SSL/TLS被区分,通过配置NetClientOptions 或者NetServerOptions 实例使其生效。

使SSL/TLS生效在服务上

SSL/TLS是有效的用 ssl

默认是无效的。

指定 键/验证 在服务端。

SSL/TLS服务通常提供验证给客户端去验证它们的标识在客户端。

Certificates/keys 验证能够为服务端配置通过几种方式:

第一种方法是通过指定java 键的存储位置,它包含验证和私钥。

java 密钥存储可以通过 keytool  工具管理,来自于 JDK

对于存储密钥的密码也应该被提供:

  1. NetServerOptions options = new NetServerOptions().setSsl(true).setKeyStoreOptions(
  2. new JksOptions().
  3. setPath("/path/to/your/server-keystore.jks").
  4. setPassword("password-of-your-keystore")
  5. );
  6. NetServer server = vertx.createNetServer(options);

你可以读的自己存储的密钥作为一个流并且提供它

  1. Buffer myKeyStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/server-keystore.jks");
  2. JksOptions jksOptions = new JksOptions().
  3. setValue(myKeyStoreAsABuffer).
  4. setPassword("password-of-your-keystore");
  5. NetServerOptions options = new NetServerOptions().
  6. setSsl(true).
  7. setKeyStoreOptions(jksOptions);
  8. NetServer server = vertx.createNetServer(options);

密钥/验证 在PKCS#12格式http://en.wikipedia.org/wiki/PKCS_12 经常使用.pfx或者.p12扩展,能够被加载以更相似流行的方式比JKS密钥存储。

  1. NetServerOptions options = new NetServerOptions().setSsl(true).setPfxKeyCertOptions(
  2. new PfxOptions().
  3. setPath("/path/to/your/server-keystore.pfx").
  4. setPassword("password-of-your-keystore")
  5. );
  6. NetServer server = vertx.createNetServer(options);

缓冲流配置也是支持的:

  1. Buffer myKeyStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/server-keystore.pfx");
  2. PfxOptions pfxOptions = new PfxOptions().
  3. setValue(myKeyStoreAsABuffer).
  4. setPassword("password-of-your-keystore");
  5. NetServerOptions options = new NetServerOptions().
  6. setSsl(true).
  7. setPfxKeyCertOptions(pfxOptions);
  8. NetServer server = vertx.createNetServer(options);

另外一个提供服务私钥和验证 可以使用 .pem文件。

  1. NetServerOptions options = new NetServerOptions().setSsl(true).setPemKeyCertOptions(
  2. new PemKeyCertOptions().
  3. setKeyPath("/path/to/your/server-key.pem").
  4. setCertPath("/path/to/your/server-cert.pem")
  5. );
  6. NetServer server = vertx.createNetServer(options);

缓冲流配置也是支持的:

  1. Buffer myKeyAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/server-key.pem");
  2. Buffer myCertAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/server-cert.pem");
  3. PemKeyCertOptions pemOptions = new PemKeyCertOptions().
  4. setKeyValue(myKeyAsABuffer).
  5. setCertValue(myCertAsABuffer);
  6. NetServerOptions options = new NetServerOptions().
  7. setSsl(true).
  8. setPemKeyCertOptions(pemOptions);
  9. NetServer server = vertx.createNetServer(options);

PKCS8, PKCS1 和 X.509 验证环绕在PEM块格式也是支持的。

  1. 警告:记住 pem配置,私钥不是地下墓室

指明对服务的信任

SSL/TLS服务能够使用官方验证去证明客户端标识。

官方验证能够配置在服务端用一些以下 几种方式:

java 信任存储能够用keytool  工具管理,来自于 JDK

还应该提供信任存储库的密码。

  1. NetServerOptions options = new NetServerOptions().
  2. setSsl(true).
  3. setClientAuth(ClientAuth.REQUIRED).
  4. setTrustStoreOptions(
  5. new JksOptions().
  6. setPath("/path/to/your/truststore.jks").
  7. setPassword("password-of-your-truststore")
  8. );
  9. NetServer server = vertx.createNetServer(options);

你可以选择读取信任存储库作为一个流,直接提供

  1. Buffer myTrustStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/truststore.jks");
  2. NetServerOptions options = new NetServerOptions().
  3. setSsl(true).
  4. setClientAuth(ClientAuth.REQUIRED).
  5. setTrustStoreOptions(
  6. new JksOptions().
  7. setValue(myTrustStoreAsABuffer).
  8. setPassword("password-of-your-truststore")
  9. );
  10. NetServer server = vertx.createNetServer(options);

权威验证 用 PKCS#12格式 (http://en.wikipedia.org/wiki/PKCS_12),通常用.pfx或者.p12作为扩展名。以一种比JKS信任存储更流行的方式加载。

  1. NetServerOptions options = new NetServerOptions().
  2. setSsl(true).
  3. setClientAuth(ClientAuth.REQUIRED).
  4. setPfxTrustOptions(
  5. new PfxOptions().
  6. setPath("/path/to/your/truststore.pfx").
  7. setPassword("password-of-your-truststore")
  8. );
  9. NetServer server = vertx.createNetServer(options);

另外提供服务端权威验证可以使用.pem列表文件。

  1. NetServerOptions options = new NetServerOptions().
  2. setSsl(true).
  3. setClientAuth(ClientAuth.REQUIRED).
  4. setPemTrustOptions(
  5. new PemTrustOptions().
  6. addCertPath("/path/to/your/server-ca.pem")
  7. );
  8. NetServer server = vertx.createNetServer(options);

缓冲流配置也是支持的:

  1. Buffer myCaAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/server-ca.pfx");
  2. NetServerOptions options = new NetServerOptions().
  3. setSsl(true).
  4. setClientAuth(ClientAuth.REQUIRED).
  5. setPemTrustOptions(
  6. new PemTrustOptions().
  7. addCertValue(myCaAsABuffer)
  8. );
  9. NetServer server = vertx.createNetServer(options);

使SSL/TLS在客户端生效

网络客户端能够容易配置去使用SSL. 它们有正确相同的API 当使用SSL作为标准的sockets时。

为了使SSL在网络客户端生效,函数setSSL(true) 被调用

客户端信任配置

如果trustALl 在客户端设置为true,那么客户端将会信任所有的服务验证。连接将会加密,但是模式是易受攻击的对于中间侵害,例如,你可能不相信连接的人。使用这些要小心,默认值是false

  1. NetClientOptions options = new NetClientOptions().
  2. setSsl(true).
  3. setTrustAll(true);
  4. NetClient client = vertx.createNetClient(options);

如果trustAll 没有被设置,那么客户端信任存储必须被配置并且应该包含服务端(客户端信任的服务端)的验证

默认情况下,在客户端,主机验证是无效的,为了使主机验证有效,在客户端设置算法去使用。(目前,仅HTTPS和LDAPS是支持的)

  1. NetClientOptions options = new NetClientOptions().
  2. setSsl(true).
  3. setHostnameVerificationAlgorithm("HTTPS");
  4. NetClient client = vertx.createNetClient(options);

除了服务配置,客户端信任应该被配置用几种方式:

第一种方法应该指定java 信任存储所在的位置,它应该包含权威验证。

它仅仅是一个标准的java 密钥存储,与服务端的密钥存储是相同的。服务端存储的位置应该被设置通过使用函数path 在jks options 上。如果一个服务含有一个验证当连接不包含客户端信任存储的时候,这个连接尝试将不会成功。

  1. NetClientOptions options = new NetClientOptions().
  2. setSsl(true).
  3. setTrustStoreOptions(
  4. new JksOptions().
  5. setPath("/path/to/your/truststore.jks").
  6. setPassword("password-of-your-truststore")
  7. );
  8. NetClient client = vertx.createNetClient(options);

缓冲流配置也是支持的:

  1. Buffer myTrustStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/truststore.jks");
  2. NetClientOptions options = new NetClientOptions().
  3. setSsl(true).
  4. setTrustStoreOptions(
  5. new JksOptions().
  6. setValue(myTrustStoreAsABuffer).
  7. setPassword("password-of-your-truststore")
  8. );
  9. NetClient client = vertx.createNetClient(options);

权威验证用PKCS#12格式(http://en.wikipedia.org/wiki/PKCS_12),经常使用.pfx或者.p12扩展,能够更流行的方式被加载比JKS信任存储

  1. NetClientOptions options = new NetClientOptions().
  2. setSsl(true).
  3. setPfxTrustOptions(
  4. new PfxOptions().
  5. setPath("/path/to/your/truststore.pfx").
  6. setPassword("password-of-your-truststore")
  7. );
  8. NetClient client = vertx.createNetClient(options);

缓冲流配置也是支持的

  1. Buffer myTrustStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/truststore.pfx");
  2. NetClientOptions options = new NetClientOptions().
  3. setSsl(true).
  4. setPfxTrustOptions(
  5. new PfxOptions().
  6. setValue(myTrustStoreAsABuffer).
  7. setPassword("password-of-your-truststore")
  8. );
  9. NetClient client = vertx.createNetClient(options);

另外一个提供服务端权威验证的方法是使用.pem文件

  1. NetClientOptions options = new NetClientOptions().
  2. setSsl(true).
  3. setPemTrustOptions(
  4. new PemTrustOptions().
  5. addCertPath("/path/to/your/ca-cert.pem")
  6. );
  7. NetClient client = vertx.createNetClient(options);

缓冲流配置也是支持的:

  1. Buffer myTrustStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/ca-cert.pem");
  2. NetClientOptions options = new NetClientOptions().
  3. setSsl(true).
  4. setPemTrustOptions(
  5. new PemTrustOptions().
  6. addCertValue(myTrustStoreAsABuffer)
  7. );
  8. NetClient client = vertx.createNetClient(options);

指定密钥/验证 为客户端

如果服务器需要客户端验证,那么客户端必须出示它自己的验证给服务器,当建立连接的时候。客户端可以通过以下几种方式配置:

第一种方式是指定java 密钥存储的位置,它应该包含密钥和验证。再一次,它仅仅是一个规则的java 键存储。客户端密钥存储位置被设置通过使用函数pathjks options 上。

  1. NetClientOptions options = new NetClientOptions().setSsl(true).setKeyStoreOptions(
  2. new JksOptions().
  3. setPath("/path/to/your/client-keystore.jks").
  4. setPassword("password-of-your-keystore")
  5. );
  6. NetClient client = vertx.createNetClient(options);

缓冲流配置也是支持的:

  1. Buffer myKeyStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/client-keystore.jks");
  2. JksOptions jksOptions = new JksOptions().
  3. setValue(myKeyStoreAsABuffer).
  4. setPassword("password-of-your-keystore");
  5. NetClientOptions options = new NetClientOptions().
  6. setSsl(true).
  7. setKeyStoreOptions(jksOptions);
  8. NetClient client = vertx.createNetClient(options);

密钥/验证 用PKCS#12格式(http://en.wikipedia.org/wiki/PKCS_12),通常使用.pfx或者.p12扩展,以一种比JKS密钥存储更流行的方式加载。

  1. NetClientOptions options = new NetClientOptions().setSsl(true).setPfxKeyCertOptions(
  2. new PfxOptions().
  3. setPath("/path/to/your/client-keystore.pfx").
  4. setPassword("password-of-your-keystore")
  5. );
  6. NetClient client = vertx.createNetClient(options);

缓冲流配置也是支持的:

  1. Buffer myKeyStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/client-keystore.pfx");
  2. PfxOptions pfxOptions = new PfxOptions().
  3. setValue(myKeyStoreAsABuffer).
  4. setPassword("password-of-your-keystore");
  5. NetClientOptions options = new NetClientOptions().
  6. setSsl(true).
  7. setPfxKeyCertOptions(pfxOptions);
  8. NetClient client = vertx.createNetClient(options);

另一种提供服务端私钥和验证的方法可以分别使用.pem文件

  1. NetClientOptions options = new NetClientOptions().setSsl(true).setPemKeyCertOptions(
  2. new PemKeyCertOptions().
  3. setKeyPath("/path/to/your/client-key.pem").
  4. setCertPath("/path/to/your/client-cert.pem")
  5. );
  6. NetClient client = vertx.createNetClient(options);

缓冲流配置也是支持的:

  1. Buffer myKeyAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/client-key.pem");
  2. Buffer myCertAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/client-cert.pem");
  3. PemKeyCertOptions pemOptions = new PemKeyCertOptions().
  4. setKeyValue(myKeyAsABuffer).
  5. setCertValue(myCertAsABuffer);
  6. NetClientOptions options = new NetClientOptions().
  7. setSsl(true).
  8. setPemKeyCertOptions(pemOptions);
  9. NetClient client = vertx.createNetClient(options);

记住:pem配置,私钥没有被加密

用于测试和开发目的的自签名证书。

  1. 小心:不要使用生产设置,记住产生的密钥是不安全的。

经常出现需要自签名验证的情况,使它成为单元/继承测试 或者 运行开发版本为一个应用

SelfSignedCertificate 能够被用来提供自签名PEM验证帮助和给出KeyCertOptions 和TrustOptions 配置

  1. SelfSignedCertificate certificate = SelfSignedCertificate.create();
  2.  
  3. NetServerOptions serverOptions = new NetServerOptions()
  4. .setSsl(true)
  5. .setKeyCertOptions(certificate.keyCertOptions())
  6. .setTrustOptions(certificate.trustOptions());
  7.  
  8. NetServer server = vertx.createNetServer(serverOptions)
  9. .connectHandler(socket -> socket.write("Hello!").end())
  10. .listen(1234, "localhost");
  11.  
  12. NetClientOptions clientOptions = new NetClientOptions()
  13. .setSsl(true)
  14. .setKeyCertOptions(certificate.keyCertOptions())
  15. .setTrustOptions(certificate.trustOptions());
  16.  
  17. NetClient client = vertx.createNetClient(clientOptions);
  18. client.connect(1234, "localhost", ar -> {
  19. if (ar.succeeded()) {
  20. ar.result().handler(buffer -> System.out.println(buffer));
  21. } else {
  22. System.err.println("Woops: " + ar.cause().getMessage());
  23. }
  24. });

客户端可以配置成信任所有的验证:

  1. NetClientOptions clientOptions = new NetClientOptions()
  2. .setSsl(true)
  3. .setTrustAll(true);

注意自签名验证也可以工作于其它的TCP协议,像HTTPS:

  1. SelfSignedCertificate certificate = SelfSignedCertificate.create();
  2.  
  3. vertx.createHttpServer(new HttpServerOptions()
  4. .setSsl(true)
  5. .setKeyCertOptions(certificate.keyCertOptions())
  6. .setTrustOptions(certificate.trustOptions()))
  7. .requestHandler(req -> req.response().end("Hello!"))
  8. .listen(8080);

撤销权威验证

信任可以被配置,通过使用验证撤回列表(CRL) 撤销验证,应该不再被信任。crlPath 配置列表去使用。

  1. NetClientOptions options = new NetClientOptions().
  2. setSsl(true).
  3. setTrustStoreOptions(trustOptions).
  4. addCrlPath("/path/to/your/crl.pem");
  5. NetClient client = vertx.createNetClient(options);

缓冲区配置也是支持的:

  1. Buffer myCrlAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/crl.pem");
  2. NetClientOptions options = new NetClientOptions().
  3. setSsl(true).
  4. setTrustStoreOptions(trustOptions).
  5. addCrlValue(myCrlAsABuffer);
  6. NetClient client = vertx.createNetClient(options);

配置密码套件

默认情况下,TLS配置将会使用JVM的密码套件运行Vert.x, 密码套件能够被配置使用一套有效的密码。

  1. NetServerOptions options = new NetServerOptions().
  2. setSsl(true).
  3. setKeyStoreOptions(keyStoreOptions).
  4. addEnabledCipherSuite("ECDHE-RSA-AES128-GCM-SHA256").
  5. addEnabledCipherSuite("ECDHE-ECDSA-AES128-GCM-SHA256").
  6. addEnabledCipherSuite("ECDHE-RSA-AES256-GCM-SHA384").
  7. addEnabledCipherSuite("CDHE-ECDSA-AES256-GCM-SHA384");
  8. NetServer server = vertx.createNetServer(options);

密码套件能够被指定通过NetServerOptions 或 NetClientOptions 配置。

配置TLS 协议版本

默认情况下,LTS配置将使用下面的协议版本,SSLv2Hello, TLSv1, TLSv1.1 和 TLSv1.2。协议版本将会直接被配置通过添加有效的协议:

  1. NetServerOptions options = new NetServerOptions().
  2. setSsl(true).
  3. setKeyStoreOptions(keyStoreOptions).
  4. removeEnabledSecureTransportProtocol("TLSv1").
  5. addEnabledSecureTransportProtocol("TLSv1.3");
  6. NetServer server = vertx.createNetServer(options);

协议版本能够被指定通过 NetServerOptions 或  NetClientOptions 配置。

SSL 引擎

引擎实现能够被配置去使用 OpenSSL 代替 JDK实现,OpenSSL 提供更好的性能 和 CPU利用率 比JDK引擎,和JDK版本依赖。

引擎选项使用:

getSslEngineOptions 选项当其设置时

否者用  JdkSSLEngineOptions

  1. NetServerOptions options = new NetServerOptions().
  2. setSsl(true).
  3. setKeyStoreOptions(keyStoreOptions);
  4.  
  5. // Use JDK SSL engine explicitly
  6. options = new NetServerOptions().
  7. setSsl(true).
  8. setKeyStoreOptions(keyStoreOptions).
  9. setJdkSslEngineOptions(new JdkSSLEngineOptions());
  10.  
  11. // Use OpenSSL engine
  12. options = new NetServerOptions().
  13. setSsl(true).
  14. setKeyStoreOptions(keyStoreOptions).
  15. setOpenSslEngineOptions(new OpenSSLEngineOptions());

服务器名字指示

服务器名字指示是一个TLS扩展,客户端指定试图连接的主机名:当TLS握手期间,客户端给出服务器名字和服务器能使用的和服务器通信发布的证书,不使用默认发布的证书。如果服务器需要客户端证明服务器能使用指定信任的CA证书,依赖指示服务器名字。

当SNI激活服务器使用时:

CN 或者 SAN DNS 证书 做额外的匹配,例如 www.example.com

证书 CN or SAN DNS 验证 匹配 通配符名,例如  *.example.com

另外 ,第一个证书,当客户端 不表示服务器名字时或者表示服务器名字不能被匹配。

当服务器额外需要客户端验证时:

如果 JksOptions 被使用设置信任选项(options) 那么一个正确的匹配对于信任存储别名被完成。

另外,可用的CA证书被使用用同样的方式,如果没有SNI在合适的位置。

你能够开启SNI在服务端通过设置 setSni 为true,配置服务端用多重密钥/验证对。

Java 密钥存储文件或PKCS12文件能存储多重密钥/证书对,开箱即用。

  1. JksOptions keyCertOptions = new JksOptions().setPath("keystore.jks").setPassword("wibble");
  2.  
  3. NetServer netServer = vertx.createNetServer(new NetServerOptions()
  4. .setKeyStoreOptions(keyCertOptions)
  5. .setSsl(true)
  6. .setSni(true)
  7. );

PemKeyCertOptions 能够被配置拥有多重实体:

  1. PemKeyCertOptions keyCertOptions = new PemKeyCertOptions()
  2. .setKeyPaths(Arrays.asList("default-key.pem", "host1-key.pem", "etc..."))
  3. .setCertPaths(Arrays.asList("default-cert.pem", "host2-key.pem", "etc...")
  4. );
  5.  
  6. NetServer netServer = vertx.createNetServer(new NetServerOptions()
  7. .setPemKeyCertOptions(keyCertOptions)
  8. .setSsl(true)
  9. .setSni(true)
  10. );

客户端隐含发送连接的主机作为SNI服务端名字对于全限定名。

你可以直接提供一个服务器名字当连接一个socket时

  1. NetClient client = vertx.createNetClient(new NetClientOptions()
  2. .setTrustStoreOptions(trustOptions)
  3. .setSsl(true)
  4. );
  5.  
  6. // Connect to 'localhost' and present 'server.name' server name
  7. client.connect(1234, "localhost", "server.name", res -> {
  8. if (res.succeeded()) {
  9. System.out.println("Connected!");
  10. NetSocket socket = res.result();
  11. } else {
  12. System.out.println("Failed to connect: " + res.cause().getMessage());
  13. }
  14. });

能用于多种目的:

表示一个不同于于服务器主机的服务器名字

表示一个服务器名字当连接IP时

强制表示一个服务器名字当使用缩减名时

应用层协议商谈

应用层协议商谈是一个应用层协议商谈的TLS扩展,用于HTTP/2:在TLS握手期间,客户端给出应用层协议列表,包含它支持的服务器响应协议。

如果你使用 Java9,这是很好的,你可以使用HTTP/2开箱即用,无需额外的步骤。

Java8 不支持ALPN开箱即用,所以ALPN应该开启通过其它方式:

OpenSSL 支持

Jetty-ALPN支持

引擎选项使用

getSslEngineOptions 选项当它设置时

JdkSSLEngineOptions 当ALPN可用时对于JDK.

OpenSSLEngineOptions 当ALPN对于OpenSSL可用时

否者,失败

OpenSSL ALPN支持

OpenSSL 提供本地ALPN支持。

OpenSSL 需要配置 setOpenSslEngineOptions 和使用 netty-tcnative jar 在类路径下。使用  tcnative 可能需要OpenSSL安装在你的操作系统中,依赖于 tcnative 实现。

Jetty-ALPN 支持

Jetty-ALPN 是一个小的jar,复写了java 8 的一些类 为了支持ALPN.

JVM必须开始以alpn-boot-${version}.jar  在 它的bootclasspath

  1. -Xbootclasspath/p:/path/to/alpn-boot${version}.jar
    ${version} 依赖JVM版本,例如。 OpenJDK 1.8.0u74 8.1.7.v20160121 全部可用的列表在  Jetty-ALPN page.
    版本主要的缺陷依赖于 JVM
    为了解决这个问题  Jetty ALPN agent 能够被替代。代理是一个JVM代理 将会关闭ALPN版本 JVM运行它。
  1. -javaagent:/path/to/alpn/agent
    使用代理对于客户端连接
    NetClient 支持HTTP/1.x CONNECT, SOCKS4a  SOCKS5 代理。
    代理能够被配置在NetClientOptions 中,通过设置ProxyOptions 对象,包含代理类型,端口和可选的用户名和密码
    这里有一个例子:
  1. NetClientOptions options = new NetClientOptions()
  2. .setProxyOptions(new ProxyOptions().setType(ProxyType.SOCKS5)
  3. .setHost("localhost").setPort(1080)
  4. .setUsername("username").setPassword("secret"));
  5. NetClient client = vertx.createNetClient(options);
  1.  

DNS方法总是在代理服务器上完成,为了实现SOCKS4 客户端的函数,必须解决DNS 本地地址

  1. HTTP服务器和客户端
  2.  
  3. Vert.x允许你很容易的写出非阻塞的HTTP客户端和服务器。Vert.x支持 HTTP/1.0, HTTP/1.1 HTTP/2协议。
    HTTP基础的APIHTTP/1.x HTTP/2相同,具体的API特性可用于处理HTTP/2协议。
    创建HTTP服务器
    创建HTTP服务器最简单的方式时使用默认选项,如下:
  1. HttpServer server = vertx.createHttpServer();
  1.  

配置HTTP服务

如果你不想使用默认选项,一个服务器可以被配置通过传递一个HttpServerOptions 实例当创建它时。

  1. HttpServerOptions options = new HttpServerOptions().setMaxWebsocketFrameSize(1000000);
  2.  
  3. HttpServer server = vertx.createHttpServer(options);

配置HTTP/2服务端。

Vert.x支持HTTP/2 TLS h2 和 TCP h2c 

h2 标识 HTTP/2协议当使用 TLS商谈 通过应用层协议商谈。

h2c标识HTTP/2协议, 当用在TCP清楚文本上时,如此的连接被建立在HTTP/1.1  更新请求。

为了处理h2请求,TLS必须随着setUseAlpn 开启:

  1. HttpServerOptions options = new HttpServerOptions()
  2. .setUseAlpn(true)
  3. .setSsl(true)
  4. .setKeyStoreOptions(new JksOptions().setPath("/path/to/my/keystore"));
  5.  
  6. HttpServer server = vertx.createHttpServer(options);

ALPN是一个商谈协议的TLS扩展,在客户端和服务器开始交换数据之前。

不支持ALPN的客户端将一直能够用经典的握手。

ALPN将会同意h2协议,尽管 http/1.1 能够使用,如果服务器或客户端决定使用。

为了处理h2c请求,TLS必须失效,服务将更新到HTTP/2 ,任何HTTP/1.1请求想更新到HTTP/2.它也将接受一个直接的h2c连接,以 PRI * HTTP/2.0\r\nSM\r\n 为前缀。

  1. 警告:大部分浏览器不支持h2c,所以对于服务端网站,你应该使用h2 ,不是h2c

当服务端接收一个HTTP/2连接,它将发送给客户端初始化设置,设置定义客户端怎么使用连接,默认的服务端初始化设置是:

  1. getMaxConcurrentStreams100被推荐作为HTTP/2 RFC
    对于其它的服务器,默认的HTTP/2设置值
  1. 注意:工作线程和HTTP/2不兼容

日志网络服务活动

为了调试,网络日志能够被日志记录

  1. HttpServerOptions options = new HttpServerOptions().setLogActivity(true);
  2.  
  3. HttpServer server = vertx.createHttpServer(options);

更多详细的信息看章节 logging network activity

开启服务监听

为了告诉服务器监听进入的请求,你可以使用一个可选监听

为了告诉服务器指定的监听端口,在选项中

  1. HttpServer server = vertx.createHttpServer();
  2. server.listen();

或者指定主机和端口号在调用listen时,忽视在选项中的配置。

  1. HttpServer server = vertx.createHttpServer();
  2. server.listen(8080, "myhost.com");

默认的主机是 0.0.0.0 ,意味着监听所有可用的地址,默认的端口是80.

实际的绑定是异步的,以至于服务器不能够实际监听直到调用listen返回之后。

如果你想被通知对于服务器实际监听,你可以提供一个handler对于listen调用。例如:

  1. HttpServer server = vertx.createHttpServer();
  2. server.listen(8080, "myhost.com", res -> {
  3. if (res.succeeded()) {
  4. System.out.println("Server is now listening!");
  5. } else {
  6. System.out.println("Failed to bind!");
  7. }
  8. });

对于进入的请求得到通知

当一个请求到达,想被通知,你可以设置一个requestHandler

  1. HttpServer server = vertx.createHttpServer();
  2. server.requestHandler(request -> {
  3. // Handle the request in here
  4. });

处理请求

当一个请求到达时,请求处理器被调用时,传递进  HttpServerRequest 实例。这个对象代表服务端的HTTP请i求。

handler 被调用,当请求头被完全读时。

如果请求包含请求体,请求体将会到达服务器,在请求处理器被调用之后。

服务端请求对象允许你得到 uripathparams 和 headers ,所有其它的事情。

 每个服务请求对象和服务响应对象相联系。你可以使用 response 得到一个 HttpServerResponse 对象引用。

这里有一个简单的 例子,服务器处理请求并且用 “hello world ”回应。

  1. vertx.createHttpServer().requestHandler(request -> {
  2. request.response().end("Hello world");
  3. }).listen(8080);

请求版本

在请求中指定的HTTP版本可以通过 version 获得

请求方法

使用 method 获得请求的HTTP 方法(例如。 GET, POST, PUT, DELETE, HEAD, OPTIONS 等)

请求URI

使用 uri 能够获得请求的 URI

记住,这是传递给HTTP请求实际URI,它总是一个相对的URI.

URI定义在  Section 5.1.2 of the HTTP specification - Request-URI

请求路径

使用 path 获得URI的路径部分

例如,如果请求URI是:

  1. a/b/c/page.html?param1=abc&param2=xyz

那么路径是

  1. /a/b/c/page.html
    请求查询
    使用 query 返回URI的查询部分
    例如:如果URI
  1. a/b/c/page.html?param1=abc&param2=xyz
    那么查询部分是:
  1. param1=abc&param2=xyz
    请求头
    使用 headers 返回HTTP请求的头。
    返回一个 MultiMap 实例,它像一个普通的 Map 或者Hash ,但是对于相同的键允许多重值,这是因为 HTTP允许 多重头值对于相同的键。
  2.  
  3. 它也是大小写不敏感的,意味着你能够做下面的事:
  1. MultiMap headers = request.headers();
  2.  
  3. // Get the User-Agent:
  4. System.out.println("User agent is " + headers.get("user-agent"));
  5.  
  6. // You can also do this and get the same result:
  7. System.out.println("User agent is " + headers.get("User-Agent"));

请求主机

使用 host 返回HTTP请求的主机。

对于 HTTP/1.x  请求,主机头返回,对于HTTP/1  请求,这个 :authority pseudo 头返回。

请求参数

使用 params 返回HTTP请求参数

就像  headers  将返回一个 MultiMap 实例,因为这里可能有超过一个参数用相同的名字。

请求参数被送通过请求URI,在路径之后,例如,如果一个URI是:

  1. /page.html?param1=abc&param2=xyz
    那么这个请求参数将包含下面的:
  1. param1: 'abc'
  2. param2: 'xyz

记住,请求参数能够从请求URL中获得。如果你有表单属性,它将会作为HTML表单属性的一部分提交,在 multi-part/form-data 格式的请求体中。

远程地址

发送的请求地址能够通过 remoteAddress 获得。

绝对URI

传递给HTTP请求的URI经常是相关的。如果你想获得与请求通信的绝对的URI,你可以得到它用absoluteURI

最终处理器

请求的 endHandler 被调用当完全请求时,包括任何请求体已经被读的。

从请求体中读数据

通常HTTP请求包含请求体我们想要读取的 。   如前面所述,请求处理器将会被调用当请求头到达时,在那个时刻,请求对象不包含请求体。

这是因为body可能非常大,例如文件上传,我们一般不想缓冲全部的请求体在内存中在处理它之前,如此将会造成服务器耗尽可用的内存。

为了获得body,你可以使用handler  在请求上,每当请求体的块到达时,他将会被调用,这里有一个例子

  1. request.handler(buffer -> {
  2. System.out.println("I have received a chunk of the body of length " + buffer.length());
  3. });

传递给 handler的对象是一个 Buffer,handler会被调用多次当数据从网络中到达时,依赖于请求体的大小。

在一些情况下,(例如:请求体是小的)你将想吞噬所有的请求体在内存中,所以你可以自己做吞噬,如下:

  1. Buffer totalBuffer = Buffer.buffer();
  2.  
  3. request.handler(buffer -> {
  4. System.out.println("I have received a chunk of the body of length " + buffer.length());
  5. totalBuffer.appendBuffer(buffer);
  6. });
  7.  
  8. request.endHandler(v -> {
  9. System.out.println("Full body received, length = " + totalBuffer.length());
  10. });

这里有一个公共的案例,Vert.x提供 bodyHandler 为你,body处理器被调用当所有的body被接收时:

  1. request.bodyHandler(totalBuffer -> {
  2. System.out.println("Full body received, length = " + totalBuffer.length());
  3. });

抽去请求

请求对象是一个读取流ReadStream,所以你能抽取请求体对于任意的 写入流WriteStream实例

更多的详情看这一章节  streams and pumps

处理HTML表单

HTML表单能够被提交以内容类型 application/x-www-form-urlencoded 和 multipart/form-data. 对于url编码的表单,表单属性能够被编码在url中,就像正常的请求参数。

对于多部分表单 ,它们能够被编码在请求体中,因为如此时不可用的,直到所有的请求体从数据报中读入。

多部分表单也能包含文件上传。

如果你想获得多部分表单,你应该告诉Vert.x你期望获得如此的表单在body的任何部分被读入之前,通过调用 setExpectMultipart 为true。然后,你应该获得实际的属性使用 formAttributes 一旦全部的body被读入:

  1. server.requestHandler(request -> {
  2. request.setExpectMultipart(true);
  3. request.endHandler(v -> {
  4. // The body has now been fully read, so retrieve the form attributes
  5. MultiMap formAttributes = request.formAttributes();
  6. });
  7. });

处理表单文件上传

Vert.x 也能处理文件上传,文件被编码为多部分请求体

为了接收文件上传,你告诉Vert.x期望多部分表单,在请求上 设置  uploadHandler

处理器将会被调用,一旦每个上传都到达服务器。

对象传递给处理器一个 HttpServerFileUpload 实例。

  1. server.requestHandler(request -> {
  2. request.setExpectMultipart(true);
  3. request.uploadHandler(upload -> {
  4. System.out.println("Got a file upload " + upload.name());
  5. });
  6. });

上传的文件可能非常大,我们不能够提供全部的上传在一次单一的缓冲流中,这可能导致内存耗尽,相反,上传数据被接受以块形式:

  1. request.uploadHandler(upload -> {
  2. upload.handler(chunk -> {
  3. System.out.println("Received a chunk of the upload of length " + chunk.length());
  4. });
  5. });

上传的对象是一个 读入流,所以你能提取请求体对于任意写入流实例。更多详细信息看章节streams and pumps

如果你仅仅想上传文件到硬盘,你可以使用streamToFileSystem

  1. request.uploadHandler(upload -> {
  2. upload.streamToFileSystem("myuploads_directory/" + upload.filename());
  3. });
  1. 警告:确保你检查文件名在生产系统中,去避免有害的客户端上传文件到任意位置在你的文件系统中,看 安全笔记  security notes得到更多的 信息

处理压缩体

Vert.x 能够处理压缩体,压缩体能够被编码通过客户端,用 deflate 或 gzip 算法。

为了使解压缩有效,你可以设置 setDecompressionSupported 在选项上,当创建服务时。

默认解压缩时无效的 。

接受定制的HTTP/2框架

HTTP/2是一个框架协议用各种各样的HTTP 请求/响应 模型。协议允许其它种类的框架被接收和发送。

为了接收定制框架,你可以使用 customFrameHandler  在请求上,这将会被调用每次定制框架到达时。这里有一个例子:

  1. request.customFrameHandler(frame -> {
  2.  
  3. System.out.println("Received a frame type=" + frame.type() +
  4. " payload" + frame.payload().toString());
  5. });

HTTP/2框架是无主题流框架--框架处理器将会立即被调用,当自定义框架

vertx 从Tcp服务端和客户端开始翻译的更多相关文章

  1. 【转】TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端

    [转]TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端).UDP客户端 目录 说明 TCP/UDP通信主要结构 管理多个Socket的解决方案 框架中TCP部分的使用 框架中UDP ...

  2. swoole创建TCP服务端和客户端

    服务端: server.php <?php //创建Server对象,监听 127.0.0.1:9501端口    $serv = new swoole_server("127.0.0 ...

  3. 基于Select模型的Windows TCP服务端和客户端程序示例

    最近跟着刘远东老师的<C++百万并发网络通信引擎架构与实现(服务端.客户端.跨平台)>,Bilibili视频地址为C++百万并发网络通信引擎架构与实现(服务端.客户端.跨平台),重新复习下 ...

  4. TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端

    目录 说明 TCP/UDP通信主要结构 管理多个Socket的解决方案 框架中TCP部分的使用 框架中UDP部分的使用 框架源码结构 补充说明 源码地址 说明 之前有好几篇博客在讲TCP/UDP通信方 ...

  5. Java TCP服务端向客户端发送图片

    /** * 1.创建TCP服务端,TCP客户端 * 2.服务端等待客户端连接,客户端连接后,服务端向客户端写入图片 * 3.客户端收到后进行文件保存 * @author Administrator * ...

  6. python创建tcp服务端和客户端

    1.tcp服务端server from socket import * from time import ctime HOST = '' PORT = 9999 BUFSIZ = 1024 ADDR ...

  7. C++封装的基于WinSock2的TCP服务端、客户端

    无聊研究Winsock套接字编程,用原生的C语言接口写出来的代码看着难受,于是自己简单用C++封装一下,把思路过程理清,方便自己后续翻看和新手学习. 只写好了TCP通信服务端,有空把客户端流程也封装一 ...

  8. Tcp服务端判断客户端是否断开连接

    今天搞tcp链接弄了一天,前面创建socket,绑定,监听等主要分清自己的参数,udp还是tcp的.好不容易调通了,然后就是一个需求,当客户端主动断开连接时,服务端也要断开连接,这样一下次客户端请求链 ...

  9. 编写一个简单的TCP服务端和客户端

    下面的实验环境是linux系统. 效果如下: 1.启动服务端程序,监听在6666端口上  2.启动客户端,与服务端建立TCP连接  3.建立完TCP连接,在客户端上向服务端发送消息 4.断开连接 实现 ...

随机推荐

  1. c++对象的生命周期

    C++ 的new 运算子和C 的malloc 函数都是为了配置内存,但前者比之后者的优点是,new 不但配置对象所需的内存空间时,同时会引发构造式的执行. 所谓构造式(constructor),就是对 ...

  2. QT之 Hello World

    下载……   我下载的Qt creater 版本为4.2.1,Qt版本为5.8.0 打开QT Creater 1. 新建项目 New Project -> Application -> Q ...

  3. 怎么修改wamp的本地时间

    最近配置了一台wamp环境的服务器,但发现时间与本地时间是地区别的,并且 利用time获取的时间再利用date显示有时差的,下面我们一起来导致原因与解决办法. 如果date时间不一致可以使用date_ ...

  4. C++中的异常安全性【转】

    原文写的非常好,来自这里 一个函数如果说是“异常安全”的,必须同时满足以下两个条件:1.不泄漏任何资源:2.不允许破坏数据. 我们先通过两个反面的例子开始. 第一个是造成资源泄漏的例子.一个类Type ...

  5. document.visibilityState 监听浏览器最小化

    document.hidden:表示页面是否隐藏的布尔值.页面隐藏包括 页面在后台标签页中 或者 浏览器最小化 (注意,页面被其他软件遮盖并不算隐藏,比如打开的 sublime 遮住了浏览器). do ...

  6. sencha touch Demo(示例)(2014-6-25)

    这是一个开源示例,是我对sencha touch的深层应用.已停止更新 sencha touch版本:2.2.1/2.3.1 源码地址: https://bitbucket.org/moLangZai ...

  7. Unity3D Mecanim :Body Mask的使用、 角色Retargeting原理分析、Apply RootMotion

    一.Body Mask的使用 1.1.配置好骨骼后通过Muscles来微调角色骨骼中的运动范围,以避免角色在动画中的不正确的叠加或失真等现象. 1.2.身体遮罩BodyMask更形象的描述就是身体的开 ...

  8. C++ 术语(C++ Primer)

    argument(实参):传递给被调用函数的值.block(块):花括号括起来的语句序列.buffer(缓冲区):一段用来存放数据的存储区域.IO 设备常存储输入(或输出)到缓冲区,并独立于程序动作对 ...

  9. yii---左查询使用

    看到一些做关联查询的示例,例如使用hasMany(一对多),还是有一个hasOne(一对一)的,没有细看,下面是我看到的一个比较好用的一个: $query = (new \yii\db\Query() ...

  10. python的十进制与任意进制的转换

    将任意进制转换成十进制 ", 8)) # 表示把8进制的54转换成十进制数并输出结果. # 8可以是2.8,10,16等进制数 将十进制转换成任意进制 def f(n,x): #n为待转换的 ...