美文网首页
Vert.x3 Core手册 [Part 2. TCP 服务器与

Vert.x3 Core手册 [Part 2. TCP 服务器与

作者: 半枚荔枝 | 来源:发表于2016-05-15 14:14 被阅读0次

本文依照 知识共享许可协议(署名-非商业性使用-禁止演绎) 发布。


编写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();

或者在方法调用时指定端口和主机,这将忽略配置项:

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

缺省主机地址是0.0.0.0,其意义是在所有可用的地址上进行监听;缺省端口是0,这是一个特殊的值,它会指示服务器随机寻找一个可用的本地端口来使用。

实际的绑定是异步发生的。所以有可能在listen方法调用返回之后,服务器才开始监听。

如果想得到监听开始的通知,可以在调用listen方法时提供一个handler供回调执行:

NetServer server = vertx.createNetServer();
server.listen(1234, "localhost", res -> {
  if (res.succeeded()) {
    System.out.println("Server is now listening!");
  } else {
    System.out.println("Failed 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 -> {
  // Handle the connection in here
});

链接产生时,这个handler将被调用,参数是NetSocket的实例。

NetSocket是对实际链接的一个类socket的接口(socket-like interface),你可以读写数据,也可以关闭socket。

从socket读数据

要从socket读数据,只需在socket上设置handler

这样每次socket收到数据时,将会给传入一个buffer并调用handler。

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);

// Write a string in UTF-8 encoding
socket.write("some data");

// Write a string using the specified encoding
socket.write("some data", "UTF-16");

写入是异步的,有可能write方法已经返回而数据写入还未发生。

结束的handler(Closed handler)

要在socket关闭时得到通知,可以设置一个closeHandler

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

处理异常

可以设置一个exceptionHandler来捕获socket上发生的异常。

Event bus write handler

socket会在event bus上自动注册一个handler,这个handler会在收到buffer时将其写入socket。

这样你可以在不同的verticle里、甚至是不同的Vert.x实例里发送buffer到这个地址,而这些数据将被写入socket。

这个handler的地址可以用writeHandlerID方法获得。

本地和远程地址

NetSocket的本地地址用localAddress方法获取。

远程地址(即,链接另一端的地址),可以用remoteAddress方法获取。

发送文件或类路径里的资源

sendFile方法可以将文件或类路径里的资源直接写入socket。因为其可以借助操作系统内核支持的操作来完成,所以这是一种很有效的发送文件的方式。

请参阅本章,可以了解关于serving files from the classpath 的限制或者关闭这个特性。
socket.sendFile("myfile.dat");

Streaming sockets

NetSocket的实例也是ReadStreamWriteStream的实例,所以它们可以用于pump data(指将数据转发出去)或从其他流读写数据。

更多细节请参阅 streams and pumps

升级链接到 SSL/TLS

可以使用upgradeToSsl方法将一个非SSL/TLS的链接升级。

要使这个特性正常工作,服务器/客户端需要配置安全选项。请参阅SSL/TLS这一节获取更多细节。

关闭TCP 服务器

调用close方法可以关闭服务器。关闭服务器时,将会关闭所有打开的链接,并释放所有的服务器资源。

关闭也是异步的,所以close方法返回时关闭可能还没结束。若要在关闭完成时得到通知,需传入一个handler。

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

verticles的自动清理

如果你是在verticle内部创建的TCP 服务器或客户端,那当verticle被卸载时,它们将被自动关闭。

扩展-共享(Scaling - sharing) TCP 服务器

TCP 服务器的handlers将一直在同一个event loop线程上执行。

这意味着如果在一个多核机器上运行时,只有一个实例被部署,你无法从多核中获得任何多余的好处。

为了利用到机器上的更多cpu核心,你需要部署你的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);
    });
  });
  server.listen(1234, "localhost");
}

或者,如果你使用了verticle,只需要部署你的服务器verticle的多个实例即可。可以在命令行里加上-instance选项:
vertx run com.mycompany.MyVerticle -instances 10

再或者,以编程方式部署你的verticle:

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

一旦你这么做了,你会发现服务器功能如以前一样没变,但它利用上了多核资源,可以做更多事。

这时候,你也许会问:“一台主机的确定端口,怎么能有多个服务器监听呢?部署多个实例难道不会造成端口冲突吗?”

Vert.x在这里耍了点小花招。*

当你在同一个端口部署另一个服务器时,其实并没有真的创建一个新服务器监听这个端口。

相反,内部其实只维护了一个服务器,但是链接传入时,会用轮询的方式将链接分发给任意的connect handler。

因此,Vert.x的TCP 服务器可以在单线程的情况下,扩展到多个可用的cpu核心。

创建TCP 客户端

创建TCP客户端也和服务器类似:
NetClient client = vertx.createNetClient();

配置TCP 客户端

类似于服务器:

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

创建链接

指定了服务器的port和host后,可以使用connect方法创建到服务器的链接。之后handler将被调用,当链接成功创建,传入的参数会包含一个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

注意:当前Vert.x不会尝试重连,这两个特性仅仅在链接创建时可用。

NetClientOptions options = new NetClientOptions().
    setReconnectAttempts(10).
    setReconnectInterval(500);

NetClient client = vertx.createNetClient(options);

缺省情况下,创建多个链接是被禁止的。

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

TCP客户端/服务器通过配置可以使用Transport Layer Security(前身是大名鼎鼎的SSL)。

是否使用SSL/TLS 对API没有影响,在NetClientOptionsNetServerOptions实例上配置。

在服务端启用 SSL/TLS

通过设置ssl可以启用 SSL/TLS。

为服务端指定 key/certificate

为了客户端能校验SSL/TLS 服务的合法性,服务端通常会提供证书给客户端。

有好几种方式可以配置服务端的Certificates/keys 。

第一种方式是指定一个Java key-store的地址,这其中应该包含证书(the certificate)和私钥(private key)。

JDK中有一个实用的程序keytool可以管理Java key stores。

key store的密码也是必要的:

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

其二,你可以读取key store并将之存入buffer直接提供:

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

PKCS#12 格式的Key/certificate,通常文件后缀名是.pfx.p12,它们的载入方式类同于Java key store:

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);

支持以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);

同样支持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 配置时,私钥是未加密的。

Specifying trust for the server

为了验证客户端的身份,SSL/TLS 服务器可以使用证书授权(a certificate authority)。

有多种途径可为服务器配置证书授权。

Java trust store同样可以用keytool管理。

同样需要提供密码:

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再提供:

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格式的证书授权(Certificate authority )同样可用:

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格式的:

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格式的:

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,配置也很容易。这里的API与使用标准socket时极其相似。

调用setSSL(true)方法即可。

Client trust configuration

如果在客户端将trustAll 设置为true,客户端将会信任所有的服务器证书。这种情况下,链接仍然会被加密,不过容易受到‘中间人攻击’。换言之,其实你没法确定连上的是谁,这点需要加以注意。缺省值是false。

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

如果未曾设置trustAll ,那么必须配置客户端的trust store,其中应该包含客户端信任的服务器证书。

与服务端的配置类似,客户端的trust也有下面几种途径:

一是指定包含证书授权的Java trust-store的位置。

这是一个标准的Java key store,与服务端的一样。客户端trust store位置的设置通过JksOptions对象的path方法完成。客户端发起连接时,如果服务器的证书不在客户端的 trust store 里,则连接请求不会成功。

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支持:

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也类似:

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支持:

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支持:

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);

为客户端指定key/certificate

如果服务端要验证客户端的身份,那么在发起连接时,客户端需要向服务端提交自己的证书。下面几种方式可以配置客户端:

其一是指定包含密钥和证书的Java key-store的位置。同样这也是一个常规的Java key store。仍然通过JksOptions对象的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格式的密钥/证书:

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支持:

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支持:

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 配置中,私钥是未加密的。

撤销证书授权(Revoking certificate authorities)

被撤销的证书不应再被信任,这可以使用证书撤销列表( a certificate revocation list (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);

配置加密算法套件( the Cipher suite )

缺省情况下,TLS 配置使用的是运行Vert.x的JVM自带的加密算法套件。它也可以用一组已启用的加密算法来配置:

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);

NetServerOptionsNetClientOptions对象都可以指定加密算法套件。


相关文章

网友评论

      本文标题:Vert.x3 Core手册 [Part 2. TCP 服务器与

      本文链接:https://www.haomeiwen.com/subject/hrpvrttx.html