Vert.x 3学习笔记---07

时间:2022-03-22 15:16:08

Writing HTTP servers and clients
上一章介绍了 tcp服务端和客户端,本章介绍怎么使用vertx创建无阻塞的http服务端和客户端。

Creating an HTTP Server

使用vertx可以很简单的创建一个http的服务端:

HttpServer server = vertx.createHttpServer();

Configuring an HTTP server

可以使用HttpServerOptions对象类配置http 服务端。

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

Start the Server Listening

可以使用无参数的listen方法:

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

也可以使用有参数的listen方法:

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

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

监听完成后的动作,可以指定handler来响应。

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

Getting notified of incoming requests

获取一个进来的请求通知。

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

Handling requests

当一个请求到达时,request handler会被调用,并传入一个httprequest参数。
The handler is called when the headers of the request have been fully read.
If the request contains a body, that body will arrive at the server some time after the request handler has been called.
当request的header全部读取完毕后,就会调用request handler。
如果一个request包含一个比较大的body时,这个body有可能会在调用request handler后才完整的读入到server中。
通过request对象,你可以获取到uri, path, params and headers等。
每一次request都有一个绑定的response对象,可以通过response()方法获取到。

vertx.createHttpServer().requestHandler(request -> {
request.response().end("Hello world");//获取response对象,并返回hello world字符串
}).listen(8080);

Request version

通过version方法获取。

Request method

通过method方法获取(GET, POST, PUT, DELETE, HEAD, OPTIONS, etc)。

Request URI

Use uri to retrieve the URI of the request.
Note that this is the actual URI as passed in the HTTP request, and it’s almost always a relative URI.
获取相对的URI地址。

Request path

Use path to return the path part of the URI

For example, if the request URI was:
a/b/c/page.html?param1=abc&param2=xyz

Then the path would be
/a/b/c/page.html

Request query

Use query to return the query part of the URI
使用query方法获取请求的uri参数部分
For example, if the request URI was:
a/b/c/page.html?param1=abc&param2=xyz

Then the query would be
param1=abc&param2=xyz

Request headers

使用headers方法获取请求的头部。其将会返回MultiMap的实例。

MultiMap headers = request.headers();

// Get the User-Agent:
System.out.println("User agent is " + headers.get("user-agent"));

// You can also do this and get the same result:
System.out.println("User agent is " + headers.get("User-Agent"));

Request parameters

使用params来获取请求的参数。和headers类似,返回的是MultiMap的实例。
For example if the URI was:
/page.html?param1=abc&param2=xyz

Then the parameters would contain the following:
param1: ‘abc’
param2: ‘xyz

注意:当提交一个multi-part/form-data的form表单时,这种方法是不能获取到参数的。

Remote address

请求的地址可以使用remoteAddress方法获取。

Absolute URI

使用absoluteURI获取绝对地址。

End handler

当整个请求内容(包含body)读取完毕后,会调用endHandler方法。

Reading Data from the Request Body

一般情况下,一个请求都会带有一个body。如前文说讲,request handler可能在body还没有被读取完成之前就被调用。这是因为一个body可能会很大,因此我们在将body交给你之前不会将整个body放到缓冲区中,这样做的话有可能导致内存用尽。
为了接收body,我们可以使用handler方法。body的一个块(a chunk)到达时就会调用这个方法。

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

此方法会被调用几次,这取决于你的body的大小。
如果你的body不是很大,那么你可以使用下面的代码:

Buffer totalBuffer = Buffer.buffer();

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

request.endHandler(v -> {
System.out.println("Full body received, length = " + totalBuffer.length());
});

这是一个很普遍的现象。因此,vertx提供了bodyHandler方法,用来达到上面代码的功能。当body读取完成后,就会调用bodyHandler。

Pumping(泵) requests

The request object is a ReadStream so you can pump the request body to any WriteStream instance。
request对象是一个ReadStream (读流),因此可以将request body泵入到任何WriteStream (写流)中。

Handling HTML forms

提交的form的type可以是 application/x-www-form-urlencoded 或者multipart/form-data中的一个。

For url encoded forms, the form attributes are encoded in the url, just like normal query parameters.
对于application/x-www-form-urlencoded这种类型的form表单,form数据将会编码到url中,就像一个普通的查询参数一样。

For multi-part forms they are encoded in the request body, and as such are not available until the entire body has been read from the wire.
Multi-part forms can also contain file uploads.
对于multipart/form-data类型的form表单,form数据会被编码到request body中,并且在整个body没有读完之前是不可用的。
这种类型的表单也包含文件的上传。

If you want to retrieve the attributes of a multi-part form you should tell Vert.x that you expect to receive such a form before any of the body is read by calling setExpectMultipart with true, and then you should retrieve the actual attributes using formAttributes once the entire body has been read:
为了获取 multi-part类型的form数据,你需要,在接收body的任何数据之前通过调用setExpectMultipart 方法(参数为true),告诉vertx,你期望接收这样一个form。当整个body读取完成后,可以使用formAttributes 方法来回去actual attributes。如下代码:

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

Handling form file uploads

为了接收上传的文件,需要设置form type为multi-part 和 设置一个 uploadHandler (request 上)。每次上传到达时,都会调用uploadHandler (参数为HttpServerFileUpload的实例)。

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

vertx没有提供完整的buffer包含整个上传内容。相反的,上传的内容只是分段的接收:

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

request对象是一个ReadStream (读流),因此可以将request body泵入到任何WriteStream (写流)中。

如果你想将上传文件写入到磁盘上,你可以调用streamToFileSystem方法:

request.uploadHandler(upload -> {
upload.streamToFileSystem("myuploads_directory/" + upload.filename());
});

Sending back responses

返回响应

response object 是 HttpServerResponse的实例。可以通过request.response()获取。可以通过response对象返回响应的数据。

Setting status code and message

设置状态码和消息

响应的状态码默认为200,表现形势为OK。
可以使用setStatusCode方法改变响应的状态码。
也可以使用自定义的状态消息,可以通过setStatusMessage方法来定义。
如果没有指定一个状态消息,一个默认合适的状态码将会被使用。

Writing HTTP responses

使用response对象的write方法可以给响应返回数据。
在response结束之前,write方法可以被调用多次。

HttpServerResponse response = request.response();
response.write(buffer);//写入一个buffer
.write("hello world!");//写入一个字符串
.write("hello world!", "UTF-16");//按特定的编码写入一个字符串

向response中写入数据之后,会立即的返回。然后向前端传送数据是需要排队的。将一个response结束,需要调用end方法。

响应头首先会被写入响应中,因此如果我们们有按响应块(using HTTP chunking)来传输的话,就需要在响应头中设置Content-Length信息。否则的话,就有可能在响应还没有传输完成就关闭了连接。

Ending HTTP responses

使用end方法来结束响应。

HttpServerResponse response = request.response();
response.write("hello world!");
response.end();

当然也可以这样使用:

HttpServerResponse response = request.response();
response.end("hello world!");

Closing the underlying connection

调用close方法来关闭底层连接。
Keep-alive connections不会自动的去关闭底层连接,如果希望在超过空闲的时间后(idle time),自动的去关闭连接,可以调用方法setIdleTimeout去配置。

Setting response headers

设置响应头部的两种方式:

HttpServerResponse response = request.response();
MultiMap headers = response.headers();
headers.set("content-type", "text/html");
headers.set("other-header", "wibble");

或者

HttpServerResponse response = request.response();
response.putHeader("content-type", "text/html").putHeader("other-header", "wibble");

Chunked HTTP responses and trailers

分块的响应和追踪

这将允许响应进行分块的写入。这一般用在有大的响应body中,或者提前不能知道的响应流中。

HttpServerResponse response = request.response();
response.setChunked(true);//设置响应进行分块传输

默认是不分块。在分块模式,每次调用一个写方法会产生一个新的HTTP块被写出来。
在分块模式,你也可以写HTTP响应追踪到响应中。这些实际上是在响应的最后一块中(in the final chunk of the response)。
To add trailers to the response, add them directly to the trailers.

HttpServerResponse response = request.response();
response.setChunked(true);
MultiMap trailers = response.trailers();
trailers.set("X-wibble", "woobble").set("X-quux", "flooble");

or

HttpServerResponse response = request.response();
response.setChunked(true);
response.putTrailer("X-wibble", "woobble").putTrailer("X-quux", "flooble");

Serving files directly from disk or the classpath

你也可以将本地磁盘或者classpath下的文件作为响应的内容返回,在vertx中这是异步的,调用AsyncFile方法,并将流泵入到响应中。
你也可以使用readFile方法来加载文件,然后将其写入到response中。
当然也可以通过sendFile来达到相同的目的,尤其是在大文件的情况下,效率会很好,小文件的情况下,效率可能会有点慢。

vertx.createHttpServer().requestHandler(request -> {
String file = "";
if (request.path().equals("/")) {
file = "index.html";
} else if (!request.path().contains("..")) {
file = request.path();
}
request.response().sendFile("web/" + file);//发送文件
}).listen(8080);

sendFile是异步的,如果你需要监听,也可以在sendFile后面增加一个handler的参数。
如下的例子,说明的是sendFile发送文件的一部分数据:

vertx.createHttpServer().requestHandler(request -> {
long offset = 0;
try {
offset = Long.parseLong(request.getParam("start"));//起始位置
} catch (NumberFormatException e) {
// error handling...
}

long end = Long.MAX_VALUE;//结束位置
try {
end = Long.parseLong(request.getParam("end"));
} catch (NumberFormatException e) {
// error handling...
}

request.response().sendFile("web/mybigfile.txt", offset, end);//发送从起始位置到结束位置中间部分的内容。
}).listen(8080);

当然end是可以省略的,表明到文件的结尾

Pumping responses

The server response is a WriteStream instance so you can pump to it from any ReadStream, e.g. AsyncFile, NetSocket, WebSocket or HttpServerRequest.
服务端的response是一个WriteStream(写入流),因此你可以泵入任何的ReadStream(读入流)。

vertx.createHttpServer().requestHandler(request -> {
HttpServerResponse response = request.response();
if (request.method() == HttpMethod.PUT) {
response.setChunked(true);
Pump.pump(request, response).start();//将request泵入到response
request.endHandler(v -> response.end());
} else {
response.setStatusCode(400).end();
}
}).listen(8080);

这是一个例子,是将所有的put方法请求的内容在返回回去。

HTTP Compression(压缩)

使用setCompressionSupported来开启压缩。(不知道压缩有什么作用的请自行度娘)。默认情况不开启压缩。通常使用的格式为deflate and gzip。

Creating an HTTP client

使用默认的参数创建:

HttpClient client = vertx.createHttpClient();

也可以使用HttpClientOptions 配置参数:

HttpClientOptions options = new HttpClientOptions().setKeepAlive(false);
HttpClient client = vertx.createHttpClient(options);

Making requests

向服务端发起请求。
可以复用httpclient。
向同一个host和port发起请求:

HttpClientOptions options = new HttpClientOptions().setDefaultHost("wibble.com");
// Can also set default port if you want...
HttpClient client = vertx.createHttpClient(options);
client.getNow("/some-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
});

向不同的host或者port发起请求:

HttpClient client = vertx.createHttpClient();

// Specify both port and host name
client.getNow(8080, "myserver.mycompany.com", "/some-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
});

// This time use the default port 80 but specify the host name
client.getNow("foo.othercompany.com", "/other-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
});

Simple requests with no request body

HTTP三种请求的方式 GET, OPTIONS and HEAD requests—可以使用xxxNow方法请求(getNow、optionNow、headNow)。

HttpClient client = vertx.createHttpClient();

// Send a GET request
client.getNow("/some-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
});

// Send a GET request
client.headNow("/other-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
});

Writing general requests

有的时候我们不会到我们需要使用何种的请求方式。因此我们提供了request的方法来完成:

HttpClient client = vertx.createHttpClient();

client.request(HttpMethod.GET, "some-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
}).end();

client.request(HttpMethod.POST, "foo-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
}).end("some-data");

Writing request bodies

post方式的请求是有body的。使用下面的方式来添加body。

HttpClient client = vertx.createHttpClient();

HttpClientRequest request = client.post("some-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
});

// Now do stuff with the request
request.putHeader("content-length", "1000");
request.putHeader("content-type", "text/plain");
request.write(body);

// Make sure the request is ended when you're done with it
request.end();

Or fluently:

client.post("some-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
}).putHeader("content-length", "1000").putHeader("content-type", "text/plain").write(body).end();

Or event more simply:

client.post("some-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
}).putHeader("content-type", "text/plain").end(body);

存在的方法使用的是utf-8的编码,如果需要指定其他格式的编码,可以使用:

request.write("some data");

// Write string encoded in specific encoding
request.write("some other data", "UTF-16");//使用UTF-16编码

// Write a buffer
Buffer buffer = Buffer.buffer();
buffer.appendInt(123).appendLong(245l);
request.write(buffer);

如果只是需要写入一次数据,可以调用有参数的end方法:

request.end("some simple data");

// Write buffer and end the request (send it) in a single call
Buffer buffer = Buffer.buffer().appendDouble(12.34d).appendLong(432l);
request.end(buffer);

还有一点需要注意的是,跟服务端的响应一样,如果客户端的请求有较大的请求体,如果没有做chunks操作,那么就需要在head中指定Content-Length参数。否则的可以会出现异常。

Writing request headers

参考如下代码:

MultiMap headers = request.headers();
headers.set("content-type", "application/json").set("other-header", "foo");

或者:

request.putHeader("content-type", "application/json").putHeader("other-header", "foo");

Ending HTTP requests

调用end方法结束http request。

request.end();

或者

request.end("some-data");

// End it with a buffer
Buffer buffer = Buffer.buffer().appendFloat(12.3f).appendInt(321);
request.end(buffer);

Chunked HTTP requests

setChunked(true);
默认是不做chunk的。
如果不做chunk,最好在头部中加入Content-Length参数。

request.setChunked(true);

// Write some chunks
for (int i = 0; i < 10; i++) {
request.write("this-is-chunk-" + i);
}

request.end();

Request timeouts

setTimeout method

Handling exceptions

注册一个exception handler来监听,参数为HttpClientRequest的实例。

HttpClientRequest request = client.post("some-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
});
request.exceptionHandler(e -> {
System.out.println("Received exception: " + e.getMessage());
e.printStackTrace();
});

上面的例子exceptionHandler不接收响应状态码为2xx的。
如果需要可以如下处理:

HttpClientRequest request = client.post("some-uri", response -> {
if (response.statusCode() == 200) {
System.out.println("Everything fine");
return;
}
if (response.statusCode() == 500) {
System.out.println("Unexpected behavior on the server side");
return;
}
});
request.end();

note:XXXNow methods cannot receive an exception handler.

Specifying a handler on the client request

在客户端请求上指定一个handler。

当请求被创建时,你可以使用handler方法来注册一个方法。

HttpClientRequest request = client.post("some-uri");
request.handler(response -> {
System.out.println("Received response with status code " + response.statusCode());
});

Using the request as a stream

将request作为一个stream

client request请求是一个WriteStream。因此你可以将任何ReadStream泵入到client request中。

request.setChunked(true);
Pump pump = Pump.pump(file, request);
file.endHandler(v -> request.end());
pump.start();

Handling http responses 处理响应

在handler中,会接收到一个HttpClientResponse的实例。从这个实例中,你可以查询到status code 和 status message 使用方法 statusCode 和statusMessage.

client.getNow("some-uri", response -> {
// the status code - e.g. 200 or 404
System.out.println("Status code is " + response.statusCode());

// the status message e.g. "OK" or "Not Found".
System.out.println("Status message is " + response.statusMessage());
});

Using the response as a stream

response是一个ReadStream,因此你可以将其泵入到任何WriteStream中。

Response headers and trailers

使用headers方法可以获取response的头部信息。

String contentType = response.headers().get("content-type");
String contentLength = response.headers().get("content-lengh");

使用trailers方法可以获取response的trailers信息。Trailers are also a MultiMap。

Reading the request body

读取响应内容

client.getNow("some-uri", response -> {
//获取响应内容
response.handler(buffer -> {
System.out.println("Received a part of the response body: " + buffer);
});
});

如果响应体很大,可以如下操作:

client.getNow("some-uri", response -> {

// Create an empty buffer
Buffer totalBuffer = Buffer.buffer();

response.handler(buffer -> {
System.out.println("Received a part of the response body: " + buffer.length());

totalBuffer.appendBuffer(buffer);
});

response.endHandler(v -> {
// Now all the body has been read
System.out.println("Total response body length is " + totalBuffer.length());
});
});

或者,你可以直接这样:

client.getNow("some-uri", response -> {

response.bodyHandler(totalBuffer -> {
// Now all the body has been read
System.out.println("Total response body length is " + totalBuffer.length());
});
});

Response end handler

使用endHandler注册

Reading cookies from the response

使用cookies方法。
或者
you can just parse the Set-Cookie headers yourself in the response.

100-Continue handling

何为100-Continue???
来自网上的解释:
在使用curl做POST的时候, 当要POST的数据大于1024字节的时候, curl并不会直接就发起POST请求, 而是会分为俩步,
1. 发送一个请求, 包含一个Expect:100-continue, 询问Server使用愿意接受数据
2. 接收到Server返回的100-continue应答以后, 才把数据POST给Server
这是libcurl的行为.

于是,这样就有了一个问题, 并不是所有的Server都会正确应答100-continue, 比如lighttpd, 就会返回417 “Expectation Failed”, 则会造成逻辑出错。

不过多解释,直接上代码:

1 客户端代码:

HttpClientRequest request = client.put("some-uri", response -> {
System.out.println("Received response with status code " + response.statusCode());
});
//设置Expect:100-Continue头部
request.putHeader("Expect", "100-Continue");
//使用continueHandler注册handler,用来完成后续动作
request.continueHandler(v -> {
// OK to send rest of body
request.write("Some data");
request.write("Some more data");
request.end();
});

2 服务端代码:

httpServer.requestHandler(request -> {
//判断是否为Expect:100-Continue头部
if (request.getHeader("Expect").equalsIgnoreCase("100-Continue")) {

// Send a 100 continue response
request.response().writeContinue();

// The client should send the body when it receives the 100 response
request.bodyHandler(body -> {
// Do something with body
});

request.endHandler(v -> {
request.response().end();
});
}
});

当然,服务端可以拒接接收后面的body内容:

httpServer.requestHandler(request -> {
if (request.getHeader("Expect").equalsIgnoreCase("100-Continue")) {
//
boolean rejectAndClose = true;
if (rejectAndClose) {

// Reject with a failure code and close the connection
// this is probably best with persistent connection
request.response()
.setStatusCode(405)
.putHeader("Connection", "close")
.end();
} else {

// Reject with a failure code and ignore the body
// this may be appropriate if the body is small
request.response()
.setStatusCode(405)
.end();
}
}
});

Enabling compression on the client

客户端数据压缩。默认是不压缩的。
需要压缩可以使用setTryUseCompression方法。

Pooling and keep alive

setKeepAlive—-可以让客户端和服务端保持连接。
setIdleTimeout—-可以设置客户端空闲多长时间后断开连接。
setMaxPoolSize—-可以设置最大开启多少个连接池。

Pipe-lining

The client also supports pipe-lining of requests on a connection.

Pipe-lining means another request is sent on the same connection before the response from the preceding one has returned. Pipe-lining is not appropriate for all requests.

To enable pipe-lining, it must be enabled using setPipelining. By default pipe-lining is disabled.

When pipe-lining is enabled requests will be written to connections without waiting for previous responses to return.

HttpClient usage

The HttpClient can be used in a Verticle or embedded.

When used in a Verticle, the Verticle should use its own client instance.

More generally a client should not be shared between different Vert.x contexts as it can lead to unexpected behavior.

For example a keep-alive connection will call the client handlers on the context of the request that opened the connection, subsequent requests will use the same context.

When this happen Vert.x detects it and log a warn:

Reusing a connection with a different context: an HttpClient is probably shared between different Verticles
The HttpClient can be embedded in a non Vert.x thread like a unit test or a plain java main: the client handlers will be called by different Vert.x threads and contexts, such contexts are created as needed. For production this usage is not recommended.

Server sharing

When several HTTP servers listen on the same port, vert.x orchestrates the request handling using a round-robin strategy.

Let’s take a verticle creating a HTTP server such as:

io.vertx.examples.http.sharing.HttpServerVerticle:

vertx.createHttpServer().requestHandler(request -> {
request.response().end("Hello from server " + this);
}).listen(8080);

This service is listening on the port 8080. So, when this verticle is instantiated multiple times as with: vertx run io.vertx.examples.http.sharing.HttpServerVerticle -instances 2, what’s happening ? If both verticles would bind to the same port, you would receive a socket exception. Fortunately, vert.x is handling this case for you. 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. It binds only once to the socket. When receiving a request it calls the server handlers following a round robin strategy.
Let’s now imagine a client such as:

vertx.setPeriodic(100, (l) -> {
vertx.createHttpClient().getNow(8080, "localhost", "/", resp -> {
resp.bodyHandler(body -> {
System.out.println(body.toString("ISO-8859-1"));
});
});
})
;

Vert.x delegates the requests to one of the server sequentially:

Hello from i.v.e.h.s.HttpServerVerticle@1
Hello from i.v.e.h.s.HttpServerVerticle@2
Hello from i.v.e.h.s.HttpServerVerticle@1
Hello from i.v.e.h.s.HttpServerVerticle@2

Consequently the servers can scale over available cores while each Vert.x verticle instance remains strictly single threaded, and you don’t have to do any special tricks like writing load-balancers in order to scale your server on your multi-core machine.

Using HTTPS with Vert.x

参照TCP ssl的配置。

WebSockets

在服务端有两种方式配置websocket:

WebSocket handler

第一种方式:
使用websocketHandler来注册websocket 的 handler。 handler的参数为ServerWebSocket的实例。

server.websocketHandler(websocket -> {
System.out.println("Connected!");
});

也可以拒接接收websocket :

server.websocketHandler(websocket -> {
if (websocket.path().equals("/myapi")) {
websocket.reject();
} else {
// Do something
}
});

第二种方式: Upgrading to WebSocket
upgrade方法。

server.requestHandler(request -> {
if (request.path().equals("/myapi")) {

ServerWebSocket websocket = request.upgrade();//将请求升级
// Do something

} else {
// Reject
request.response().setStatusCode(400).end();
}
});

The server WebSocket
The ServerWebSocket instance enables you to retrieve the headers, path, query and URI of the HTTP request of the WebSocket handshake.

WebSockets on the client

客户端websocket配置:

client.websocket("/some-uri", websocket -> {
System.out.println("Connected!");
});

Writing messages to WebSockets

使用websocket发送消息:

Buffer buffer = Buffer.buffer().appendInt(123).appendFloat(1.23f);

websocket.writeBinaryMessage(buffer);

如果websocket发送的数据过大,比setMaxWebsocketFrameSize方法设置的还要大,vertx将会将其拆分多个frames发送。

Writing frames to WebSockets

A WebSocket message can be composed of multiple frames. In this case the first frame is either a binary or text frame followed by zero or more continuation frames.

The last frame in the message is marked as final.

To send a message consisting of multiple frames you create frames using WebSocketFrame.binaryFrame , WebSocketFrame.textFrame or WebSocketFrame.continuationFrame and write them to the WebSocket using writeFrame.

Here’s an example for binary frames:

WebSocketFrame frame1 = WebSocketFrame.binaryFrame(buffer1, false);
websocket.writeFrame(frame1);

WebSocketFrame frame2 = WebSocketFrame.continuationFrame(buffer2, false);
websocket.writeFrame(frame2);

// Write the final frame
WebSocketFrame frame3 = WebSocketFrame.continuationFrame(buffer2, true);
websocket.writeFrame(frame3);

In many cases you just want to send a websocket message that consists of a single final frame, so we provide a couple of shortcut methods to do that with writeFinalBinaryFrame and writeFinalTextFrame.

Here’s an example:

websocket.writeFinalTextFrame("Geronimo!");

// Send a websocket messages consisting of a single final binary frame:

Buffer buff = Buffer.buffer().appendInt(12).appendString("foo");

websocket.writeFinalBinaryFrame(buff);

Reading frames from WebSockets

websocket.frameHandler(frame -> {
System.out.println("Received a frame of size!");
});

Closing WebSockets

close方法

Streaming WebSockets

The WebSocket instance is also a ReadStream and a WriteStream so it can be used with pumps.

When using a WebSocket as a write stream or a read stream it can only be used with WebSockets connections that are used with binary frames that are no split over multiple frames.

Automatic clean-up in verticles

If you’re creating http servers and clients from inside verticles, those servers and clients will be automatically closed when the verticle is undeployed.