Vert.x 3 核心手册之TCP服务器与客户端

编写TCP服务器和客户端

Vert.x 允许您很轻松的编写一个非阻塞的TCP客户端和服务器。

创建TCP服务器

使用下面的默认选项可以用最简单的方式的创建一个TCP服务器:

NetServer server = vertx.createNetServer();

配置TCP服务器

如果您不想使用默认配置,你可以在创建服务器的时候通过 NetServerOptions 实例来配置它。

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

监听服务器

让服务器监听接入的请求你需要调用 listen 方法。
告诉服务器监听指定的主机和端口:

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

或者调用listen方法的时候指定主机和端口号,忽略默认配置的选项:

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

默认的主机是0.0.0.0,这意味着“监听所有可用的地址”,默认端口是 0,这是一个特殊的值,表示服务器查找一个随机未使用的本地端口作为监听的端口。

服务器实际的绑定是异步的,因此服务器可能不会真正地侦听,直到在调用侦听之后的某个时间返回。

如果您希望在服务器实际监听时得到通知,您可以为侦听器提供一个处理程序。例如:

NetServer server = vertx.createNetServer();
server.listen(1234, "localhost", res -> {
   if (res.succeeded()) {
       System.out.println("Server is now listening!")
   }  else {
       System.out.println("Faield to bind!");
   }
});

监听随机端口

如果 0 作为监听的端口,服务器将会找一个随机未使用的端口监听它。
获取服务器真实的监听端口你可以使用 actualPort 方法.

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

获取传入连接的通知

要在连接完成时得到通知,您需要设置 connectHandler :

NetServer server = vertx.createNetServer();
server.connectHandler(socket -> {
   // 在这里处理连接 
});

在建立连接时,处理程序将被调用一个 NetSocket 实例。这是一个与实际连接类似的套接字接口,允许您读写数据以及执行其他各种操作,如关闭套接字。

从套接字上读取数据

要从套接字读取数据,你需要在套接字上设置 handler 处理机制。每当在套接字上接收到数据时,将使用 Buffer 的实例调用此处理程序。

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

写数据到套接字

你可以使用socket的 write 方法来写数据。

Buffer buffer = Buffer.buffer().appendFloat(12.34f).appendInt(123);
socket.write(buffer);

// 写UTF-8编码的字符串
socket.write("some data");

// 写一个指定编码的字符串
socket.write("some data", "UTF-16");

写操作是异步的,可能在调用写之后的某个时间之后才会发生。

关闭Handler

如果你想要在socket被关闭时被通知,你可以设置 closeHandler

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

处理异常

你可以设置一个 exceptionHandler 处理器来接收任何在这个套接字上面的任何异常。

你可以设置一个 exceptionHandler 处理器来接收在连接到connectHandler之前发生的任何异常,例如:在TLS握手过程中。

事件总线写处理程序

每个套接字都会自动在事件总线上注册一个处理程序,当在这个处理程序中接收到缓冲区时,它将会把数据写入到这个缓冲区。

这使您可以通过将缓冲区发送到该处理程序的地址,将数据写入可能位于完全不同Verticle或甚至不同Vert.x实例中的套接字。

处理程序的地址是由 writeHandlerID 提供的

本地和远程地址

NetSocket 的本地地址可以通过 localAddress 来检索。

NetSocket 的远程地址可以通过 romoteAddress 来检索(即连接的另一端的地址)。

从类路径中发送文件或资源

文件和类路径的资源可以直接使用 sendFile 写入到套接字中去。这是一种行之有效的发送文件方式,因为它可以直接在操作系统支持的OS内核中处理。

请参阅关于 从类路径中提供文件
的章节,以了解类路径解析的限制或禁用它。

socket.sendFile("myfile.dat");

流式套接字

NetSocket 的实例也是 ReadStreamWriteStream 的实例,所以它们可以用来向其他读写流传输数据。

获取更多信息请参见 streams and pumps 章节。

升级至SSL/TLS连接

一个非SSL/TLS连接可以使用 upgradeToSsl 升级至SSL/TLS。

服务端和客户端必须配置SSL/TLS才能正常工作。详情请参考 SSL/TLS 章节。

关闭TCP服务器

调用 close 方法可以关闭服务器,关闭服务器意味着关闭任何打开的连接和释放所有的服务器资源。

关闭实际上是异步处理的,并且可能在调用之后一段时间才能完成。如果你想要在真正关闭连接的时候接收到通知,你可以传递一个处理器。

这个处理器将会在完全关闭完成的时候被调用。

server.close(res -> {
   if (res.succeeded()) {
       System.out.println("Server is now closed");
   } else {
       System.out.println("close failed");
   }
});

自动清理verticles

如果您从内部的verticles创建TCP服务器和客户端,当verticles被卸载的时候会自动将这些服务器和客户端进行关闭。

扩展-共享TCP服务器

任何TCP服务器的处理程序总是在相同的事件循环线程上执行的。

这意味着您在多核心的处理器只部署了一个服务器实例,那么您的服务器上最多只能使用一个内核。

为了合理使用多内核处理器,您将会需要在服务器上部署更多的实例。

您可以在代码中以编程的方式实例化更多的实例:

for (int i = 0; i < 10; i++) {
    NetServer server = vertx.createNetServer();
    server.connectHandler(socket -> {
       socket.handler(buffer -> {
          // Just echo back the data
          socket.write(buffer);
       });
    });
}

或者,如果您正在使用verticles,则可以通过命令行的方式上使用 instances 选项来简单地部署多个服务器Verticles实例。

vertx run com.mycompany.MyVerticle -instances 10

或者以编程的方式部署你的实例:

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

一旦你这样做了你会发现打印服务器功能和以前差不多,但是你的服务器内核可以被充分利用并且能够处理更多的工作。

在这一点上,你可能会问自己了‘俺们咋整才可以在一台服务器上面同时监听相同的主机和端口?当您尝试并部署多个实例时,您一定会遇到端口冲突?

Vert.x 在这里做了一个小魔法(biubiubiu)。*

当你部署另外一个服务器在相同的主机和端口上作为一个存在的服务器,它实际上不会尝试创建一个新的服务器监听在相同的主机和端口上。

取而代之的是它的内部只维护了一台服务器,并且,当连接到达时,它将以循环的方式将它们分配给任何连接处理程序。

所以Vert.x TCP服务器可以在每个实例保持单线程的情况下仍然能够扩展可用内核。

创建TCP客户端

使用下面的默认配置可以用最简单的方式创建TCP客户端:

NetClient client = vertx.createNetClient();

配置TCP客户端

如果您不想使用默认的配置选项,你可以在创建实例的时候传入 NetClientOptions 实例来使用指定配置:

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

建立连接

请指定服务器的主机和端口并使用 connect 方法连接到服务器,当连接成功时将调用包含 NetSocket 结果的处理程序,如果连接失败,则返回失败。

NetClientOptions options = new NetClientOptions().setConnectTimeout(10000);
NetClient client = vertx.createNetClient(options);
client.connect(4321, "localhost", res -> {
   if (res.succeeded()) {
       System.out.println("Connected!");
       NetSocket socket = res.result();
   } else {
       System.out.println("Failed to connect: " + res.cause().getMessage());
   }
});

配置尝试连接

客户端能够配置在连接失败时自动重试连接服务器。这里使用 setReconnectIntervalsetReconnectAttempts 来进行配置。

NOTE : 当前的Vert.x连接失败时不会试图尝试重连,尝试重连和间隔时间需要在创建实例连接的时候设置

NetClientOptions options = new NetClientOptions().
    setReconnectAttempts(10).
    setReconnectInterval(500);
    
NetClient client = vertx.createNetClient(options);

默认情况下,多连接尝试是禁用的。

记录网络活动

为了调试的目的,可以记录网络活动:

服务器:

NetServerOptions options = new NetServerOptions().setLogActivity(true);

NetServer server = vertx.createNetServer(options);

客户端:

NetClientOptions options = new NetClientOptions().setLogActivity(true);

NetClient client = vertx.createNetClient(options);

Netty使用 DEBUG 等级和 io.netty.handler.logging.LoggingHandler 名称来记录网络活动。但使用网络活动记录的时候这里有几点事项需要注意:

  • 网络记录活动是由Netty记录的,不是由Vert.x记录。
  • 非线上环境特性。

具体你可以参考 Netty logging章节。

配置服务端和客户端使用SSL/TLS

TCP客户端和服务端能够配置使用 Transport Layer Security - 较早版本的TLS叫SSL。

不论是否使用SSL/TLS,服务器和端口的API都是相同的,并且可以通过服务器或客户端的 NetClientOptionsNetServerOptions 实例来启用配置。

在服务器上启用SSL/TLS

SSL/TLS使用 ssl 启用。
默认它是关闭的。

为服务器指定 键/证书

SSL/TLS服务器通常提供证书给客户端,以便验证客户端的身份。

为服务器配置 密钥/证书 有下面几种方式:

第一种方法是指定包含证书和私钥的Java密钥库的位置。

Java密钥存储可以使用JDK附带的 keytool 实用程序进行管理。

还需提供密钥存储的密码:

NetServerOptions options = new NetServerOptions().setSsl(true).
    setKeyStoreOptions(new JksOptions().
    setPath("/path/to/your/server-keystore.jks").
    setPassowrd("wibble"));

NetServer server = vertx.createNetServer(options);

同时您还可以自己将密钥存储区作为缓冲区读取,并直接提供:

Buffer myKeyStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/server-keystore.jks");
JksOptions jksOptions = new JksOptions().
    setVAlue(myKeyStoreAsABuffer).
    setPassword("wibble");

NetServerOptions options = new NetServerOptions().
    setSsl(true).
    setKeyStoreOptions(jksOptions);

NetServer server = vertx.createNetServer(options);

PKCS#12格式(http://en.wikipedia.org/wiki/PKCS_12) 中的密钥/证书(通常为.pfx或.p12扩展名)也可以以与JKS密钥库类似的方式加载:

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

PKCS#12 Buffer配置同样也支持:

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

另一种使用.pem文件分别提供服务器私钥和证书的方法。

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

pem文件Buffer配置同样也支持

Buffer myKeyAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/server-key.pem");
Buffer myCertAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/server-cert.pem");
PemKeyCertOptions pemOptions = new PemKeyCertOptions().
  setKeyValue(myKeyAsABuffer).
  setCertValue(myCertAsABuffer);
NetServerOptions options = new NetServerOptions().
  setSsl(true).
  setPemKeyCertOptions(pemOptions);
NetServer server = vertx.createNetServer(options);

支持以PEM块格式封装的PKCS8,PKCS1和X.509证书。

WARNING: | pem配置需要注意的是私钥没有加密

指定信任服务器

SSL/TLS服务器可以使用证书授权来验证客户端的身份。

证书授权能够以好几种方式配置:

可以使用JDK附带的 keytool 实用程序来管理Java信任库。

还需要提供信任商店的密码:

NetServerOptions options = new NetServerOptions().
    setSsl(true).
    setClientAuth(ClientAuth.REQUIRED).
    setTrustStoreOptions(
        new JksOptions().
            setPath("/path/to/your/truststore.jks").
            setPassword("password-of-your-truststore")
      );
NetServer server = vertx.createNetServer(options);

另外你还可以自己读取信任存储作为缓冲区,并直接提供:

Buffer myTrustStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/truststore.jks");
NetServerOptions options = new NetServerOptions().
  setSsl(true).
  setClientAuth(ClientAuth.REQUIRED).
  setTrustStoreOptions(
    new JksOptions().
      setValue(myTrustStoreAsABuffer).
      setPassword("password-of-your-truststore")
  );
NetServer server = vertx.createNetServer(options);

PKCS#12格式(http://en.wikipedia.org/wiki/PKCS_12) 中的证书颁发机构(通常为.pfx或.p12扩展名)也可以以与JKS信任库类似的方式加载:

NetServerOptions options = new NetServerOptions().
  setSsl(true).
  setClientAuth(ClientAuth.REQUIRED).
  setPfxTrustOptions(
    new PfxOptions().
      setPath("/path/to/your/truststore.pfx").
      setPassword("password-of-your-truststore")
  );
NetServer server = vertx.createNetServer(options);

Buffer配置也提供了PKCS#12格式的支持:

Buffer myTrustStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/truststore.pfx");
NetServerOptions options = new NetServerOptions().
  setSsl(true).
  setClientAuth(ClientAuth.REQUIRED).
  setPfxTrustOptions(
    new PfxOptions().
      setValue(myTrustStoreAsABuffer).
      setPassword("password-of-your-truststore")
  );
NetServer server = vertx.createNetServer(options);

使用列表.pem文件提供服务器证书颁发机构的另一种方法。

NetServerOptions options = new NetServerOptions().
  setSsl(true).
  setClientAuth(ClientAuth.REQUIRED).
  setPemTrustOptions(
    new PemTrustOptions().
      addCertPath("/path/to/your/server-ca.pem")
  );
NetServer server = vertx.createNetServer(options);

Buffer同样也提供了.pem文件的配置支持:

Buffer myCaAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/server-ca.pfx");
NetServerOptions options = new NetServerOptions().
  setSsl(true).
  setClientAuth(ClientAuth.REQUIRED).
  setPemTrustOptions(
    new PemTrustOptions().
      addCertValue(myCaAsABuffer)
  );
NetServer server = vertx.createNetServer(options);

客户端使用SSL/TLS

客户端也可以轻松配置使用SSL。 当使用SSL和使用标准套接字时,它们的API基本上完全相同。

客户端要启用SSL只要调用 setSSL(true) 方法即可。

客户端信任配置

如果客户端设置 trustAll 为true,客户端将会信任所有服务器的证书。连接仍然会被加密,但这种模式容易受到“中间人”的攻击。即你不能确定你连接到谁。 请谨慎使用。 默认值是false。

NetClientOptions options = new NetClientOptions().
    setSsl(true).
    setTrustAll(true);
NetClient client = vertx.createNetClient(options);

如果客户端未设置 trustAll,则必须配置客户端信任库,并且应该包含客户端信任的服务器的证书。

默认情况下,在客户端上禁用主机验证。 要启用主机验证,请设置要在客户端上使用的算法(当前仅支持HTTPS和LDAPS):

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

同服务器配置差不多,客户端信任也可以用几种方式配置:

第一种方式通过指定Java信任库和包含证书授权的位置。

它只是一个标准的Java密钥存储区,与服务器端的密钥存储区相同。 客户端信任存储位置是使用jks选项上的函数路径设置的。 如果服务器在连接期间提供证书而不在客户端信任库中,则连接尝试将不会成功。

NetClientOptions options = new NetClientOptions().
  setSsl(true).
  setTrustStoreOptions(
    new JksOptions().
      setPath("/path/to/your/truststore.jks").
      setPassword("password-of-your-truststore")
  );
NetClient client = vertx.createNetClient(options);

Buffer配置同样也支持Java信任库和包含证书授权:

Buffer myTrustStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/truststore.jks");
NetClientOptions options = new NetClientOptions().
  setSsl(true).
  setTrustStoreOptions(
    new JksOptions().
      setValue(myTrustStoreAsABuffer).
      setPassword("password-of-your-truststore")
  );
NetClient client = vertx.createNetClient(options);

PKCS#12格式 (http://en.wikipedia.org/wiki/PKCS_12) 中的证书颁发机构(通常为.pfx或.p12扩展名)也可以以与JKS信任库类似的方式加载:

NetClientOptions options = new NetClientOptions().
  setSsl(true).
  setPfxTrustOptions(
    new PfxOptions().
      setPath("/path/to/your/truststore.pfx").
      setPassword("password-of-your-truststore")
  );
NetClient client = vertx.createNetClient(options);

同样Buffer支持PKCS#12格式的配置:

Buffer myTrustStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/truststore.pfx");
NetClientOptions options = new NetClientOptions().
  setSsl(true).
  setPfxTrustOptions(
    new PfxOptions().
      setValue(myTrustStoreAsABuffer).
      setPassword("password-of-your-truststore")
  );
NetClient client = vertx.createNetClient(options);

另外一种方式使用列表.pem文件提供服务器证书颁发机构的另一种方法:

NetClientOptions options = new NetClientOptions().
  setSsl(true).
  setPemTrustOptions(
    new PemTrustOptions().
      addCertPath("/path/to/your/ca-cert.pem")
  );
NetClient client = vertx.createNetClient(options);

同样Buffer也提供了支持.pem文件:

Buffer myTrustStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/ca-cert.pem");
NetClientOptions options = new NetClientOptions().
  setSsl(true).
  setPemTrustOptions(
    new PemTrustOptions().
      addCertValue(myTrustStoreAsABuffer)
  );
NetClient client = vertx.createNetClient(options);

为客户端指定 密钥/证书

如果服务器要求客户端认证,客户端在连接的时候必须提供自己的证书给服务器。客户端可以通过几种方式进行配置:

第一种方法是指定包含密钥和证书的Java密钥库的位置。 再次,它只是一个普通的Java密钥存储。 客户端密钥库位置通过使用 jks options 上的 path 来设置。

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

Buffer同样也支持配置:

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

PKCS#12格式 (http://en.wikipedia.org/wiki/PKCS_12) 中的密钥/证书(通常为.pfx或.p12扩展名)也可以以与JKS密钥库类似的方式加载:

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

Buffer同样也支持PKCS#12格式支持

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

另一种使用.pem文件分别提供服务器私钥和证书的方法。

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

Buffer也同样提供了.pem文件配置的支持:

Buffer myKeyAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/client-key.pem");
Buffer myCertAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/client-cert.pem");
PemKeyCertOptions pemOptions = new PemKeyCertOptions().
  setKeyValue(myKeyAsABuffer).
  setCertValue(myCertAsABuffer);
NetClientOptions options = new NetClientOptions().
  setSsl(true).
  setPemKeyCertOptions(pemOptions);
NetClient client = vertx.createNetClient(options);

需要注意的是pem的配置,私钥是不会加密的。

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

CAUTION:不要使用在生产环境中,并注意生成的密钥是非常不安全的。

通常情况下,需要自签名证书,无论是单元/集成测试还是运行开发版本的应用程序。

SelfSignedCertificate 可用于提供自签名的PEM证书助手并给予 KeyCertOptionsTrustOptions 配置:

SelfSignedCertificate certificate = SelfSignedCertificate.create();

NetServerOptions serverOptions = new NetServerOptions()
  .setSsl(true)
  .setKeyCertOptions(certificate.keyCertOptions())
  .setTrustOptions(certificate.trustOptions());

NetServer server = vertx.createNetServer(serverOptions)
  .connectHandler(socket -> socket.write("Hello!").end())
  .listen(1234, "localhost");

NetClientOptions clientOptions = new NetClientOptions()
  .setSsl(true)
  .setKeyCertOptions(certificate.keyCertOptions())
  .setTrustOptions(certificate.trustOptions());

NetClient client = vertx.createNetClient(clientOptions);
client.connect(1234, "localhost", ar -> {
  if (ar.succeeded()) {
    ar.result().handler(buffer -> System.out.println(buffer));
  } else {
    System.err.println("Woops: " + ar.cause().getMessage());
  }
});

客户端能够配置信任所有证书:

NetClientOptions clientOptions = new NetClientOptions()
  .setSsl(true)
  .setTrustAll(true);

注意:自签名证书也适用于其他TCP协议,例如HTTPS:

SelfSignedCertificate certificate = SelfSignedCertificate.create();

vertx.createHttpServer(new HttpServerOptions()
  .setSsl(true)
  .setKeyCertOptions(certificate.keyCertOptions())
  .setTrustOptions(certificate.trustOptions()))
  .requestHandler(req -> req.response().end("Hello!"))
  .listen(8080);

撤销证书授权

可以将信任配置为使用证书吊销列表(CRL)来吊销不应再受信任的证书。 crlPath 配置crl列表使用:

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

Buffer的方式同样也支持:

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

配置密码套件

默认情况下,TLS配置将使用运行Vert.x的JVM的Cipher套件。 这个Cipher套件可以配置一套启用的密码:

NetServerOptions options = new NetServerOptions().
  setSsl(true).
  setKeyStoreOptions(keyStoreOptions).
  addEnabledCipherSuite("ECDHE-RSA-AES128-GCM-SHA256").
  addEnabledCipherSuite("ECDHE-ECDSA-AES128-GCM-SHA256").
  addEnabledCipherSuite("ECDHE-RSA-AES256-GCM-SHA384").
  addEnabledCipherSuite("CDHE-ECDSA-AES256-GCM-SHA384");
NetServer server = vertx.createNetServer(options);

密码套可以使用 NetServerOptions 或者 NetClientOptions 来配置。

配置TLS协议版本

默认情况下,TLS配置将会使用下面协议版本:SSLv2Hello, TLSv1, TLSv1.1 和 TLSv1.2。协议版本可以通过明确添加启用的协议进行配置:

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

协议版本可以通过 NetServerOptions 或者 NetClientOptions 来配置。

SSL引擎

这个引擎使用OpenSSL的实现配置从而取代JDK的实现。OpenSSL提供比JDK引擎更好的性能和CPU使用率以及JDK版本独立性。

这个引擎可供使用的选项如下:

NetServerOptions options = new NetServerOptions().
  setSsl(true).
  setKeyStoreOptions(keyStoreOptions);

// Use JDK SSL engine explicitly
options = new NetServerOptions().
  setSsl(true).
  setKeyStoreOptions(keyStoreOptions).
  setJdkSslEngineOptions(new JdkSSLEngineOptions());

// Use OpenSSL engine
options = new NetServerOptions().
  setSsl(true).
  setKeyStoreOptions(keyStoreOptions).
  setOpenSslEngineOptions(new OpenSSLEngineOptions());

服务器名称指示(SNI)

服务器名称指示(SNI)是一个TLS扩展,客户端通过该扩展指定一个尝试连接的主机名:在TLS握手过程中,客户端提供一个服务器名称,服务器可以使用它来响应此服务器名称的特定证书,而不是 默认部署的证书。

当服务器使用SNI时,

  • 证书CN或SAN DNS(使用DNS的主题备用名称)进行完全匹配,例如 www.example.com
  • 证书CN或SAN DNS证书匹配通配符名称,例如 *.example.com
  • 否则当客户端不提供服务器名称或主讲者服务器名称时,第一个证书不能匹配

您可以通过设置并使用多个密钥/证书对 setSnitrue 配置服务器来启用服务器上的SNI 。

Java KeyStore文件或PKCS12文件可以存储多个密钥/证书对。

JksOptions keyCertOptions = new JksOptions().setPath("keystore.jks").setPassword("wibble");

NetServer netServer = vertx.createNetServer(new NetServerOptions()
    .setKeyStoreOptions(keyCertOptions)
    .setSsl(true)
    .setSni(true)
);

PemKeyCertOptions 可以配置为保存多个条目:

PemKeyCertOptions keyCertOptions = new PemKeyCertOptions()
    .setKeyPaths(Arrays.asList("default-key.pem", "host1-key.pem", "etc..."))
    .setCertPaths(Arrays.asList("default-cert.pem", "host2-key.pem", "etc...")
    );

NetServer netServer = vertx.createNetServer(new NetServerOptions()
    .setPemKeyCertOptions(keyCertOptions)
    .setSsl(true)
    .setSni(true)
);

客户端隐式发送连接主机作为完全合格域名(FQDN)的SNI服务器名称。

连接套接字时,可以提供一个明确的服务器名称

NetClient client = vertx.createNetClient(new NetClientOptions()
    .setTrustStoreOptions(trustOptions)
    .setSsl(true)
);

// Connect to 'localhost' and present 'server.name' server name
client.connect(1234, "localhost", "server.name", res -> {
  if (res.succeeded()) {
    System.out.println("Connected!");
    NetSocket socket = res.result();
  } else {
    System.out.println("Failed to connect: " + res.cause().getMessage());
  }
});

它可以用于不同的目的:

  • 提供与服务器主机不同的服务器名称
  • 在连接到IP时显示服务器名称
  • 强制在使用短名称时显示服务器名称

应用层协议协商(ALPN)

应用层协议协商(ALPN)是应用层协议协商的TLS扩展。它被HTTP / 2使用:在TLS握手过程中,客户端提供它接受的应用程序协议列表,服务器以它支持的协议进行响应。

如果您使用的是Java 9,那么您就可以使用HTTP / 2,而无需额外的步骤。

Java 8不支持开箱即用的ALPN,所以ALPN应该通过其他方式启用:

  • OpenSSL支持
  • Jetty-ALPN支持

要使用的引擎选项是:

OpenSSL ALPN支持

OpenSSL提供本地ALPN支持。

OpenSSL需要在类路径上配置 setOpenSslEngineOptions 和使用 netty-ticative jar。使用tcnative可能需要在操作系统上安装OpenSSL,具体取决于具体的实现。

etty-ALPN支持

Jetty-ALPN是一个小的jar,它覆盖了几个Java 8发行版以支持ALPN。

在JVM必须与启动alpn启动- ${version}.jar在 bootclasspath

-Xbootclasspath / p:/路径/到/ alpn启动${version}.jar

其中${version}依赖于JVM版本,如8.1.7.v20160121为OpenJDK的1.8.0u74。完整列表在 Jetty-ALPN 页面上提供。

主要缺点是版本取决于JVM。

为了解决这个问题,可以使用 Jetty ALPN 代理。代理是一个JVM代理,它将为运行它的JVM选择正确的ALPN版本:

-javaagent:/路径/到/ alpn /剂

为客户端连接使用代理

NetClient 支持HTTP/1.x 连接,SOCKET4a 或者 SOCKET5 代理。

代理可以在 NetClientOptions 中配置一个 ProxyOptions 对象包含的代理类型,主机,端口和可选的用户名和密码。

举个栗子:

NetClientOptions options = new NetClientOptions()
    .setProxyOptions(new ProxyOptions().setType(ProxyType.SOCKET5)
        .setHost("localhost").setPort(9090)
        .setUsername("username").setPassword("secret"));
NetClient client = vertx.createNetClient(options);

DDNS解析总是在代理服务器上完成的,为了实现SOCKS4客户端的功能,需要在本地解析DNS地址。

The End
2017-11-16 20:00

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容