TCP Server

Creating a TCP server

我们使用默认的选项(HttpServerOptions)来创建一个最简单的TCP服务器.

  1. NetServer server = vertx.createNetServer();

Configuring a TCP server

如果想要对创建的服务器进行特殊配置,可以使用HttpServerOptions来创建服务器。

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

Start the Server Listening

我们提供了众多的listen方法,下面我们选择一个不带参数的listen方法(端口和主机地址已经在刚才的HttpServerOptions中指定了)

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

下面我们在listen方法中显式地指定监听的端口和网卡地址(这时候会忽略掉HttpServerOptions中设置的端口号)

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

listen方法默认监听的地址是0.0.0.0(所有可用地址),默认的端口是0(这种情况下会随机选择一个可用的端口). 需要注意的是绑定操作(listen)是异步进行的,所以当listen方法返回之后并不保证绑定操作已经成功. 在示例中我们添加了一个handler用于接受绑定成功之后的通知.

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

Listening on a random port

当我们监听端口号为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. });

Getting notified of incoming connections

下例中我们设置了一个connectHandler,用于处理服务器接受到的网络连接

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

当网络连接建立成功之后,handler就会自动被调用(同时会带有一个NetSocket对象作为参数)

NetSocket是对实际网络连接的一个类Socket接口抽象(socket-like interface),你可以在这个接口进行读写数据,或者直接关闭Socket等操作.

Reading data from the socket

To read data from the socket you set the handler on the socket.

想要读取Socket中的数据,那你就需要调用NetSockethandler方法,设置一个handler,用于处理数据.

当Socket流中有数据到达时,服务器就会将接受到的数据封装成一个Buffer对象,然后刚刚在NetSocket上设置的那个handler就会被调用.

考虑半包处理

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

Writing data to a socket

你可以直接调用NetSocketwrite方法进行写回数据

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

注意,write方法一样是异步进行的,当write方法返回后,并不保证数据已经完全写入到Socket流中,也不保证数据能够写入成功

Closed handler

下例中我们设置了一个closeHandler用于当Socket关闭时,获得一些通知

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

Handling exceptions

如果你想当socket操作发生异常时获得通知,你可以设置一个exceptionHandler

Event bus write handler

每一个Socket都会在event bus上自动注册一个handler,一旦该handler接受到Buffer, handler会将Buffer写到Socket上.

利用这种特性你可以在不同的verticle甚至不同的Vertx实例里对同一个socket写数据. 这种功能的实现方式是handler身上有一个writeHandlerID,这个ID是handlerevent bus上的注册地址,不同的verticle甚至不同的Vertx实例就可以通过该地址向Socket写入数据。

Local and remote addresses

我们可以通过localAddress获得NetSocket的本地地址. 通过remoteAddress获得网络对等端地址.

Sending files

我们可以通过sendFile直接向Socket中写入一个文件. 这是一种非常高效发送文件的方式,如果操作操作系统支持的话,这还可以被OS内核支持

  1. 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.

See the chapter on streams and pumps for more information.

NetSocket实例还是ReadStreamWriteStream的实例,因此

Upgrading connections to SSL/TLS

我们可以使用upgradeToSsl方法将一个不支持SSL/TLS的连接改为支持SSL/TLS的连接,具体参考相关章节

Closing a TCP Server

我们可以调用close方法关闭服务器,close方法会关闭所有打开的连接和所有的服务器资源.

一样一样的,关闭操作同样是异步的,你懂得,想要关闭完成时进行某些操作,设置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. });

Automatic clean-up in verticles

如果你是在verticle中创建的TCP服务器和客户端,那么当宿主verticleundeployed时,宿主身上的服务器和客户端也会被自动的关闭掉

Scaling - sharing TCP servers

TCP服务器上的所有handler都只会被相同的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. }

或者如果你的服务器是在verticle内实现的,那么你也可以在命令行中通过-instances部署多个服务器实例.

  1. vertx run com.mycompany.MyVerticle -instances 10

以及通过编程的方式部署多个服务器verticle实例

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

Once you do this you will find the echo server works functionally identically to before, but all your cores on your server can be utilised and more work can be handled.

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?’

Vert.x does a little magic here.*

When you deploy another server on the same host and port as an existing server it doesn’t actually try and create a new server listening on the same host/port.

Instead it internally maintains just a single server, and, as incoming connections arrive it distributes them in a round-robin fashion to any of the connect handlers.

Consequently Vert.x TCP servers can scale over available cores while each instance remains single threaded.