Websocket is so simple

Time:2020-1-16

Preface

Today, I saw a new course of Java on mooc.com (the first experience of netsocket for getting started with netty): https://www.imooc.com/learn/941

Websocket is a technology I’ve heard a lot about that I haven’t really used. I’ve known what websocket is before, but I haven’t practiced it.

When I write a listener blog, I use the listener to do the online population function. In the comments, it is said that the way to use websocket will be better.

So, let’s explore what websocket is. By the way, get to know netty!

Websocket introduction

What is websocket

Websocket is a protocol that belongs to IETF.

  • HTTP is an application protocol running on the transport layer of TCP protocol, while websocket is an application protocol running on the transport layer of TCP protocol independently after negotiating how to connect through HTTP protocol.
  • Websocket is a persistent protocol, compared with the non persistent protocol such as http
  • Websocket specifies a communication specification,By means of a handshake mechanism, a TCP like connection can be established between the client and the server to facilitate their communication

Why websocket is needed

The websocket feature is added for better, more flexible and lightweight communication with the server. Because websocket provides a simple message specification,Can adapt to long connected environment fasterIn fact, the HTTP protocol can be done by itself, but it’s not very portable.

The biggest feature of websocket is its implementationFull duplex communication: the client can push messages to the server in real time, and the server can also push messages to the client in real time.

Websocket can be used as chat room, real-time stock price display and other applications

Correct websocket mistakes

Websocket is an application protocol, and we often see that HTML5 websocket is an API, do not confuse it.

In a broad sense, HTML5 contains the websocket API, not websocket.In short, websocket can be used as HTTP and websocket API as Ajax.

Netty introduction

What is netty

Zhihu’s @ Guo didn’t mean to summarize it very well. I’ll take an excerpt below (the link is below):

What is netty?

  • 1) Essence: a jar package made by JBoss
  • 2) Objective: to develop high performance and high reliability web server and client programs quickly
  • 3) Advantages: provides asynchronous, event driven network application framework and tools

Generally speaking:A good way to deal with socket

Without netty?

Ancient times: java.net + java.io

Modern: java.nio

Others: Mina, grizzly

In short:

  • You want to write a Tomcat like server. You can use netty.
  • If you want to write an instant messaging application, you can use netty.
  • If you want to implement a high-performance RPC framework, you can use netty.

Netty advantage

Netty advantages: simple API, high performance, low entry threshold, mature and robust, and fixed many bugs of native NiO

Come back to the course

The course is based on the implementation of websocket by netty, which is also mentioned above:Using netty to realize the application of instant communication

Source download address: https://img.mukewang.com/down/5a6e804c0001970d000000000.zip

First of all, a global configuration class is created. Websocket is full duplex communication throughpassagewayTo communicate, soThe system channel group is configured to manage all channels

/**
 *Store the global configuration of the entire project
 * @author liuyazhuang
 *
 */
public class NettyConfig {
    
    /**
     *Store the channel object when each client accesses
     */
    public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}

Configure some channel information (it can be understood that configure charset of request object and cache of response object in servlet)

/**
 *Initialize the components when connecting
 * @author liuyazhuang
 *
 */
public class MyWebSocketChannelHandler extends ChannelInitializer<SocketChannel> {



    //Configure some encoding formats, data sizes, and processors of the channel (to whom)
    @Override
    protected void initChannel(SocketChannel e) throws Exception {
        e.pipeline().addLast("http-codec", new HttpServerCodec());
        e.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
        e.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
        e.pipeline().addLast("handler", new MyWebSocketHandler());
    }
}

Netty receives requests, and processes HTTP requests and websocket requests respectively. This part of the video is just code writing, not too much. Let me sort it out:

  • This class is used for processingCore business class requested
  • The most important method is the messagereceived() method, which mainly determines whether the request is HTTP or websocket

    • When it is an HTTP request, handle it with handhttprequest(),This method judges whether there is a tendency to shake hands

      • If it is not a websocket handshake request message, thenDirectly return the HTTP 400 bad request response to the client, answer the message, and close the link.
      • If it’s a handshake request, handshake it and associate websocket withDynamic addition of encoding and decoding classes to channelpipeline
    • It’s websocketGroup sending, the server sends a group of messages to each connected client
package com.imooc.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;

import java.util.Date;

/**
 *Core business processing class for receiving / processing / responding to client websocket requests
 *
 * @author liuyazhuang
 */
public class MyWebSocketHandler extends SimpleChannelInboundHandler<Object> {

    private WebSocketServerHandshaker handshaker;
    private static final String WEB_SOCKET_URL = "ws://localhost:8888/websocket";


    //Called when the client and the server create a connection
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig.group.add(ctx.channel());
        System. Out. Println ("connection between client and server is enabled...");
    }

    //Called when the client is disconnected from the server
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        NettyConfig.group.remove(ctx.channel());
        System. Out. Println ("connection between client and server is closed...");
    }

    // the server receives the call after the client sends the data.
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    //Called when there is an exception in the project
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    //The core method of server processing client websocket request
    @Override
    protected void messageReceived(ChannelHandlerContext context, Object msg) throws Exception {

        //Traditional HTTP access
        //The first handshake request message is hosted by the HTTP protocol, so it is an HTTP message. Execute the handlehttprequest method to handle the websocket handshake request.
        if (msg instanceof FullHttpRequest) {
            handHttpRequest(context, (FullHttpRequest) msg);
        }

        //Websocket access
        //The client submits the request message to the server through the text box, and the websocketserverhandler receives the decoded websocketframe message.
        else if (msg instanceof WebSocketFrame) {

            handWebsocketFrame(context, (WebSocketFrame) msg);
        }
    }


    /**
     *Handle the service of HTTP handshake request from client to server
     *
     * @param ctx
     * @param req
     */
    private void handHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {

        //If it is not a websocket handshake request message, the HTTP 400 bad request response is returned to the client.
        if (!req.getDecoderResult().isSuccess()
                || !("websocket".equals(req.headers().get("Upgrade")))) {
            sendHttpResponse(ctx, req,
                    new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }

        //If it's a handshake request, shake hands
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                WEB_SOCKET_URL, null, false);
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        } else {

            //It constructs a handshake response message to return to the client,
            //At the same time, the encoding and decoding classes related to websocket are dynamically added to the channelpipeline for the encoding and decoding of websocket messages,
            //After adding websocketencoder and websocketecoder, the server can automatically encode and decode websocket messages
            handshaker.handshake(ctx.channel(), req);
        }
    }

    /**
     *Handle websocket business before client and server
     *
     * @param ctx
     * @param frame
     */
    private void handWebsocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
        //Determine whether it is the instruction to close websocket
        if (frame instanceof CloseWebSocketFrame) {
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
        }
        //Determine whether it is a ping message
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }

        //Determine whether it is a binary message. If it is a binary message, throw an exception
        if (!(frame instanceof TextWebSocketFrame)) {
            System. Out. Println ("we don't support binary messages at this time");
            Throw new runtimeException ("[" + this. Getclass(). Getname() + "] does not support messages");
        }


        //Return reply message
        //Get the message sent by the client to the server
        String request = ((TextWebSocketFrame) frame).text();
        System. Out. Println ("the server receives the message from the client = ===== > >" + request);
        TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString()
                + ctx.channel().id()
                + " ===>>> "
                + request);
        //Group sending: the server sends a group of messages to each connected client
        NettyConfig.group.writeAndFlush(tws);
    }



    /**
     *Server response message to client
     *
     * @param ctx
     * @param req
     * @param res
     */
    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req,
                                  DefaultFullHttpResponse res) {

        //Return reply to client
        if (res.getStatus().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }

        //If it is not keep alive, close the connection
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (res.getStatus().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }
}

Finally, write the entry program: start websocket service

package com.imooc.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 *The entry of the program, responsible for starting the application
 * @author liuyazhuang
 *
 */
public class Main {
    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workGroup);
            b.channel(NioServerSocketChannel.class);
            b.childHandler(new MyWebSocketChannelHandler());
            System. Out. Println ("server opens waiting for client connection...");
            Channel ch = b.bind(8888).sync().channel();
            ch.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            //Elegant exit procedure
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

Client code:

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset = utf-8"/>
        < title > websocket client < / Title >
    <script type="text/javascript">
        var socket;
        if(!window.WebSocket){
            window.WebSocket = window.MozWebSocket;
        }

        if(window.WebSocket){
            socket = new WebSocket("ws://localhost:8888/websocket");
            socket.onmessage = function(event){
                var ta = document.getElementById('responseContent');
                ta.value += event.data + "\r\n";
            };

            socket.onopen = function(event){
                var ta = document.getElementById('responseContent');
                Ta.value = "your current browser supports websocket, please carry out subsequent operations \ R \ n";
            };

            socket.onclose = function(event){
                var ta = document.getElementById('responseContent');
                ta.value = "";
                TA. Value = "websocket connection is closed \ R \ n";
            };
        }else{
            Alert ("websocket is not supported in your browser");
        }


        function send(message){
            if(!window.WebSocket){
                return;
            }
            if(socket.readyState == WebSocket.OPEN){
                socket.send(message);
            }else{
                Alert ("websocket connection was not established successfully!! "";
            }
        }
    </script>
    </head>
    <body>
        <form onSubmit="return false;">
            <input type = "text" name = "message" value = ""/>
            <br/><br/>
            < input type = "button" value = "send websocket request message" onclick = "send (this. Form. Message. Value)" / >
            <hr color="red"/>
            <h2>The client receives the reply message returned by the server</h2>
            <textarea id = "responseContent" style = "width:1024px; height:300px"></textarea>
        </form>
    </body>
</html>

Websocket is so simple

Actually using websocket

The above example explains the implementation of websocket by netty. Generally, we don’t use websocket to implement it by ourselves, but we use off the shelf toolkits to implement it.

There are two common ways I can find out:

  • Tomcat implements websocket
  • Integrating spring to realize websocket

I won’t go into this part again. I’ll supplement the tutorial when I use itFirst mark the related blogs:

  • Tomcat implementation
  • Spring integration and Tomcat implementation
  • Java based implementation

summary

The biggest feature of websocket is long connection, which can push data in real time.

Reference link:

  • Websocket Zhihu problem
  • Websocket principle
  • Websocket principle
  • Netty knows what’s wrong
  • What does chunk code do

If there is something wrong in the article, you are welcome to correct it and communicate with each other. Students who are used to reading technical articles on wechat and want to get more Java resources canPay attention to WeChat public address: Java3y