springboot2.0 + websocket + android client practice

Keywords: Session Java Android Maven

brief introduction

WebSocket is a protocol in HTML5. It supports persistent connection and can effectively solve the problem of polling when data synchronization between client and server.

Design sketch

Server side

  • Create a web project (omitted here)

  • Introduce websocket maven dependency (supported only when springboot2.0 or above)

    Locate the pom.xml folder of the project and add the following dependencies.

<!--websocket springboot2.0 Above is supported-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
  • Configure Websocket
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * Configure websocket and turn it on
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
  • Write WebSocket message processing class
package com.mhwang.miniprogram;

import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/websocket/{vmcNo}") // Path to client URI access
@Component
public class WebSocketServer {
/** Save all connected webSocket entities
* CopyOnWriteArrayList A method called copy on write is used,
* When a new element is added to CopyOnWriteArrayList,
* First copy from the original array, and then write to the new array,
* After writing, point the original array reference to the new array.
* Thread safe and suitable for high concurrency scenarios
*/
private static CopyOnWriteArrayList<WebSocketServer> sWebSocketServers = new CopyOnWriteArrayList<>();
private Session mSession; // A session connected to a client for sending data
private long mVmcNo; // Identification of the client (machine number here)
private Log mLog = LogFactory.getLog(WebSocketServer.class);

@OnOpen
public void onOpen(Session session, @PathParam("vmcNo") long vmcNo){
mSession = session;
sWebSocketServers.add(this); // Save reply
mLog.info("-->onOpen new connect vmcNo is "+vmcNo);
mVmcNo = vmcNo;
}

@OnClose
public void onClose(){
sWebSocketServers.remove(this);
mLog.info("-->onClose a connect");
}

@OnMessage
public void onMessage(String message, Session session){
mLog.info("-->onMessage "+message);
// What we choose here is to let other clients know the message, similar to the forwarded chat room, which can be used according to the usage scenario
for (WebSocketServer socketServer : sWebSocketServers){
socketServer.sendMessage("i have rcv you message");
}
}

/** Send message out
* @param message
*/
public boolean sendMessage(String message){
try {
mSession.getBasicRemote().sendText(message);
} catch (IOException e) {
mLog.info(e.toString());
return false;
}
return true;
}

/** Send a message to a machine
 * @param message
 * @param vmcNo Machine number
 * @return true,Return sent message, false, return failed string
*/
public static String sendMessage(String message, long vmcNo){
boolean success = false;
for (WebSocketServer server : sWebSocketServers){
if (server.mVmcNo == vmcNo){
success = server.sendMessage(message);
break;
}
}
return success ? message : "failed";
}
}
  • Add external access interface (if not required, it is not necessary to write, here is mainly to send commands to the machine through the interface)
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WebSocketController {

@RequestMapping(value = "/operation/{vmc}/{cmd}")
public String remote(@PathVariable("vmc") long vmc, @PathVariable("cmd") String cmd){
System.out.print("remote");
RemoteOperation operation = new RemoteOperation();
operation.setVmc_no(vmc);
operation.setOperation(cmd);
String message = new Gson().toJson(operation);
System.out.println("message in json is :"+message);
return WebSocketServer.sendMessage(message,vmc);
}

@RequestMapping(value = "/test")
public String test(){
System.out.print("test");
return "hello world";
}
}

It should be noted that there may be such a bug in springboot+websocket. When the package of IntelliJ IDE is used to package the functions of jar package, everything is normal at local runtime, but the package reports the error javax.websocket.server.ServerContainer not available. This is because the package fails due to the failure of test.
The solution is to use maven's command line packaging. Open the system cmd command, enter the project root directory, and enter mvn package -DskipTests

Android terminal

The process of Android is similar to that of server.

  • Create Android project (nonsense)

  • Join gradle dependency

    Add the following dependencies to the project build.gradle(Module:app) file:

compile "org.java-websocket:Java-WebSocket:1.3.8"
  • Write WebSocket message connection and processing class
package com.mhwang.adbcommunicatetest;

import android.content.Context;
import android.content.Intent;
import android.util.Log;


import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;
import java.net.URISyntaxException;

public class WebClient extends WebSocketClient{
    public static final String ACTION_RECEIVE_MESSAGE = "com.jinuo.mhwang.servermanager";
    public static final String KEY_RECEIVED_DATA = "data";
    private static WebClient mWebClient;
    private Context mContext;
    /**
     *  The path is ws + server address + sub path + parameter set by the server (the corresponding server machine number here is the parameter)
     *  If the server side is https, the ws of the prefix becomes wss
     */
    private static final String mAddress = "ws://Server address: port / mhwang7758/websocket/“;
    private void showLog(String msg){
        Log.d("WebClient---->", msg);
    }
    private WebClient(URI serverUri, Context context){
        super(serverUri, new Draft_6455());
        mContext = context;
        showLog("WebClient");
    }

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        showLog("open->"+handshakedata.toString());
    }

    @Override
    public void onMessage(String message) {
        showLog("onMessage->"+message);
        sendMessageBroadcast(message);
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        showLog("onClose->"+reason);
    }

    @Override
    public void onError(Exception ex) {
        showLog("onError->"+ex.toString());
    }

    /** Initialization
     * @param vmc_no
     */
    public static void initWebSocket(final Context context, final long vmc_no){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    mWebClient = new WebClient(new URI(mAddress+vmc_no), context);
                    try {
                        mWebClient.connectBlocking();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } catch (URISyntaxException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /** Send message broadcast
     * @param message
     */
    private void sendMessageBroadcast(String message){
        if (!message.isEmpty()){
            Intent intent = new Intent();
            intent.setAction(ACTION_RECEIVE_MESSAGE);
            intent.putExtra(KEY_RECEIVED_DATA,message);
            showLog("Send received message");
            mContext.sendBroadcast(intent);
        }
    }

}

Called at application initialization:

WebClient.initWebSocket(this,10086);

Remember to add network permissions.

Posted by philspliff on Sat, 15 Feb 2020 12:16:17 -0800