Writing TCP servers and clients 编写TCP服务端和客户端
使用vertx可以很简单的编写出无阻塞的tcp服务端和客户端。下面逐一来介绍。
Creating a TCP server
NetServer server = vertx.createNetServer();
Configuring a TCP server
可以使用NetServerOptions 来配置tcp服务端:
NetServerOptions options = new NetServerOptions().setPort(4321);
NetServer server = vertx.createNetServer(options);
Start the Server Listening
使用listen方法,让服务端开始监听进入的请求。
NetServer server = vertx.createNetServer();
server.listen();
或者使用指定端口和地址:
NetServer server = vertx.createNetServer();
server.listen(1234, "localhost");
不指定的情况下,地址默认使用0.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("Failed to bind!");
}
});
在listen方法后面设置一个handler参数,可以在监听成功后,执行handler。
Getting notified of incoming connections
当有客户端连接进来时,如果希望得到通知的话,可以如下:
NetServer server = vertx.createNetServer();
//设置客户端连接的handler
server.connectHandler(socket -> {
// Handle the connection in here
});
Reading data from the socket
NetServer server = vertx.createNetServer();
//设置客户端连接的handler
server.connectHandler(socket -> {
//设置读取数据的handler
socket.handler(buffer -> {
System.out.println("I received some bytes: " + buffer.length());
});
});
Writing data to a socket
Buffer 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");
Closed handler
当socket连接关闭时,可以注册一个handler来监听这个事件。
socket.closeHandler(v -> {
System.out.println("The socket has been closed");
});
Handling exceptions
当socket出现异常时,可以使用exceptionHandler方法,注册一个handler来监听这个事件。
Event bus write handler
如果想通过event bus给socket发送消息,首先我们先通过writeHandlerID()方法获取socket注册在event bus上的handler,然后再使用此handler id给其发送消息。
Local and remote addresses
The local address of a NetSocket can be retrieved using localAddress.
The remote address, (i.e. the address of the other end of the connection) of a NetSocket can be retrieved using remoteAddress.
Sending files or resources from the classpath
如果需要发送文件,或者classpath下的资源,我们直接调用sendFile()方法。
socket.sendFile("myfile.dat");
Streaming sockets
Instances of NetSocket are also ReadStream and WriteStream instances so they can be used to pump data to or from other read and write streams.
Upgrading connections to SSL/TLS
A non SSL/TLS connection can be upgraded to SSL/TLS using upgradeToSsl.
The server or client must be configured for SSL/TLS for this to work correctly.
Closing a TCP Server
调用close方法来关闭一个tcp服务端,也可以给它注册一个handler来监听关闭的事件。
server.close(res -> {
if (res.succeeded()) {
System.out.println("Server is now closed");
} else {
System.out.println("close failed");
}
});
Automatic clean-up in verticles
If you’re creating TCP servers and clients from inside verticles, those servers and clients will be automatically closed when the verticle is undeployed.
Scaling - sharing TCP servers
tcp服务端注册的所有handler总是在相同的event loop thread中执行。这也就意味着一个多核心的服务器上你只能运行一个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);
});
});
server.listen(1234, "localhost");
}
或者,如果你使用verticle来部署你的tcp服务,你可以使用以下命令来部署:
vertx run com.mycompany.MyVerticle -instances 10
或者在程序中这样编程:
DeploymentOptions options = new DeploymentOptions().setInstances(10);
vertx.deployVerticle("com.mycompany.MyVerticle", options);
At this point you might be asking yourself ‘How can you have more than one server listening on the same host and port? Surely you will get port conflicts as soon as you try and deploy more than one instance?’
在同一个主机和端口上,你怎么能有多台服务器?一旦你尝试和部署多个实例,你会得到端口冲突吗?
在这一点上,vertx的实现有一点不同的地方。不管我们部署多少个tcp服务的实例,只要是相同的端口和地址,vertx都只是生成了一个tcp服务实例,而部署的handler都会去监听这一个tcp服务实例。只不过对于所有的客户端连接,vertx会轮询的分配到部署的handler上。
Creating a TCP client
使用以下代码创建tcp客户端:
NetClient client = vertx.createNetClient();
Configuring a TCP client
使用NetClientOptions来配置tcp client连接:
NetClientOptions options = new NetClientOptions().setConnectTimeout(10000);
NetClient client = vertx.createNetClient(options);
Making connections
使用connect方法,连接到服务器。connect方法需要三个参数,端口,地址,连接的handler:
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());
}
});
Configuring connection attempts
配置连接尝试
可以配置客户端连接,使其在中断后,可以尝试去重新连接。
NetClientOptions options = new NetClientOptions().
setReconnectAttempts(10).//尝试次数
setReconnectInterval(500);//每一次尝试间隔
NetClient client = vertx.createNetClient(options);
By default, multiple connection attempts are disabled.
Configuring servers and clients to work with SSL/TLS
Enabling SSL/TLS on the server
SSL/TLS is enabled with setSsl method.
By default it is disabled.
通过ssl方法来开启ssl连接。
Specifying key/certificate for the server
配置ssl服务端的证书。
ssl server通常会为每一个ssl client提供一个证书,以便能识别client的身份。
ssl server配置证书有几种方式:
第一种情况:需要指定密钥存储区的地址:密钥存储区的密码也应提供:
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);
或者,你可以将密钥存储区读取为一个buffer,然后提供给vertx:
Buffer myKeyStoreAsABuffer = vertx.fileSystem().readFileBlocking("/path/to/your/server-keystore.jks");//将密钥存取区读取为一个buffer
JksOptions jksOptions = new JksOptions().
setValue(myKeyStoreAsABuffer).//设置密钥存取区buffer
setPassword("password-of-your-keystore");//设置密钥存取区buffer的密码
NetServerOptions options = new NetServerOptions().
setSsl(true).//开启ssl服务
setKeyStoreOptions(jksOptions);
NetServer server = vertx.createNetServer(options);
第二种情况:
如果使用PKCS#12格式的密钥(通常以.pfx 或 .p12 为后缀),可以使用以下代码:
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);
第三种情况:
Another way of providing server private key and certificate separately using .pem files.
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 configuration is also supported:
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);
Keep in mind that pem configuration, the private key is not crypted.
记住,PEM格式配置的私钥是不加密的。所以不需要密码。
Specifying trust for the server
ssl服务端可以通过设置证书来验证ssl client。
ssl服务端配置证书授权有以下几种方式:
第一种方法:
使用java内置的api的方式,代码如下:
需要提供密码
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
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);
Enabling SSL/TLS on the client
Client trust configuration
配置客户端信任证书
NetClientOptions options = new NetClientOptions().
setSsl(true).//开启ssl
setTrustAll(true);//如果为true,表示信任所有的服务器端的证书
NetClient client = vertx.createNetClient(options);
setTrustAll(true);//如果为true,表示信任所有的服务器端的证书。这样的连接仍然是加密的,但是容易受到’man in the middle’ attacks(中间人攻击)。
如果不进行设置的话,默认为false。如果为false的话,就需要配置证书。相对应的也会有几种方式:
第一种方法: jdk
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);
Specifying key/certificate for the client
如果服务器端需要证书,就需要为客户端指定证书。
相对应的几种配置的方法:
第一种方法: jdk
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);
Revoking certificate authorities
撤销证书授权
对于已撤销的不再可信的证书,可以配置一个证书撤销列表(CRL)。
NetClientOptions options = new NetClientOptions().
setSsl(true).
setTrustStoreOptions(trustOptions).
addCrlPath("/path/to/your/crl.pem");//设置CRL列表
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);//设置CRL列表
NetClient client = vertx.createNetClient(options);
Configuring the Cipher suite
对于jvm上运行的vertx来说,tsl配置可能需要Cipher suite。
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);