WebSocket before we introduce the integrated SpringBoot carried out to achieve, here we take a look at Netty WebSocket is how, in fact, is relatively simple, according to first before we can achieve Http server and client using Netty , we A simple page is used to make a request to the client, and its page content is as follows:
Very simple, it is a div area for displaying information below the input box and a button for sending a message, then we are also related to this page WebSocket way, here we realize chat native WebSocket API also introduces However, here is mainly in the onmessage method, when the message is received, it is displayed on the page, as follows:
Then we are our message sending event, as follows:
The above is the entire content of chat.html, we put it in the project, we can make a request on the page
Then the server is the standard Netty startup process, and some SSL related support is added during startup. This is also introduced when we implement the Http server, as follows:
public final class WebSocketServer {
private static boolean SSL = false;
public static final int PORT = SSL ? 8443 : 8080;
private void start() throws Exception {
final SslContext sslContext;
if (SSL) {
SelfSignedCertificate signedCertificate = new SelfSignedCertificate();
sslContext = SslContextBuilder.forServer(signedCertificate.certificate(),
signedCertificate.privateKey()).build();
} else {
sslContext = null;
}
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
try {
serverBootstrap.group(boss, work)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 512)
.childHandler(new WebSocketServerInitializer(sslContext));
ChannelFuture channelFuture = serverBootstrap.bind(PORT).sync();
channelFuture.channel().closeFuture().sync();
} finally {
boss.shutdownGracefully();
work.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
WebSocketServer server = new WebSocketServer();
server.start();
}
}
Then we need to take a look at the related Handler we added, which needs to add related SSL support and Http support. We also said before that WebSocket is built based on Http, and then we also set the path of WebSocket The path is the same as the path we requested in JS, and then add related WebSocket support, as follows:
Then what we need appropriate treatment and WebSocket request Http request, where the request for Http main thing is to chat.html page back to the browser for display, here and use Netty achieve Http server and client is almost Consistent
public class WebPageHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
// 状态为1xx的话,继续请求
if(HttpUtil.is100ContinueExpected(request)){
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
ctx.writeAndFlush(response);
}
//处理错误或者无法解析的http请求
if (!request.decoderResult().isSuccess()) {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST,
Unpooled.copiedBuffer("请求失败", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
//只允许GET请求
if (request.method() != HttpMethod.GET) {
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN);
ByteBuf byteBuf = Unpooled.copiedBuffer("只允许GET请求", CharsetUtil.UTF_8);
response.content().writeBytes(byteBuf);
byteBuf.release();
HttpUtil.setContentLength(response, response.content().readableBytes());
ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
if ("/chat".equals(request.uri())) {
String path = this.getClass().getClassLoader().getResource("static/chat.html").getPath();
RandomAccessFile file = new RandomAccessFile(path, "r");
HttpResponse response = new DefaultHttpResponse(request.protocolVersion(), HttpResponseStatus.OK);
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");
if (HttpUtil.isKeepAlive(request)) {
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, file.length());
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
}
ctx.write(response);
if (ctx.pipeline().get(SslHandler.class) == null) {
ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length()));
} else {
ctx.write(new ChunkedNioFile(file.getChannel()));
}
ChannelFuture channelFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
if (!HttpUtil.isKeepAlive(request)) {
channelFuture.addListener(ChannelFutureListener.CLOSE);
}
} else {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND,
Unpooled.copiedBuffer("访问路径错误", CharsetUtil.UTF_8));
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
}
Finally, we mainly need to look at the processing of related WebSocket requests, as follows:
public class WebSocketHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception {
if (msg instanceof TextWebSocketFrame) {
//判断是否为文本帧,若是则进行处理
String data = ((TextWebSocketFrame) msg).text();
ctx.channel().writeAndFlush(new TextWebSocketFrame(data));
}
}
}
Here we briefly introduce TextWebSocketFrame , the WebSocket RFC issued by IETF, which defines six frames, Netty provides a POJO implementation for each of them. Let ’s take a look at six different types of WebSocketFrame, the first three are data frames , The last three are control frames, the last two frames are heartbeat packets, ping initiates heartbeat, and pong responds to heartbeat.
Frame type | description |
---|---|
BinaryWebSocketFrame | Contains binary data |
TextWebSocketFrame | Contains text data |
ContinuationWebSocketFrame | Contains text data or binary data belonging to the previous BinaryWebSocketFrame or TextWebSocketFrame |
CloseWebSocketFrame | Indicates a CLOSE request, including a closed status code and reason for closing |
PingWebSocketFrame | Request to transmit a PongWebSocketFrame |
PongWebSocketFrame | Sent as a response to PingWebSocketFrame |
The following is the result of its operation. Enter the relevant information in the input box and click Send. It will be sent to the server through WebSocket, and then the server will return the message, as shown below:
Although the above implemented functions are the same as our previous ones, the implementation is different. We can find the displayed message through WebSoket through the front-end JS code.
Below we make a simple modification, and can also achieve the function of mass sending. We also define an event to remind other clients that a new client logs in to proceed as follows: