springboot2.0 + websocket + android客户端实战

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013564742/article/details/81041740

简介

WebSocket是HTML5中的协议,支持持久连接,可以有效解决客户端和服务端之间数据数据同步时需要轮询的问题。

效果图

这里写图片描述

服务端

  • 创建web工程(此处省略)

  • 引入websocket maven依赖(springboot2.0以上才支持)

    找到工程的pom.xml文件夹,添加以下依赖。

<!--websocket springboot2.0以上才支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
  • 配置Websocket
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * 配置websocket并开启
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
  • 编写WebSocket消息处理类
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}") // 客户端URI访问的路径
@Component
public class WebSocketServer {
/** 保存所有连接的webSocket实体
* CopyOnWriteArrayList使用了一种叫写时复制的方法,
* 当有新元素添加到CopyOnWriteArrayList时,
* 先从原有的数组中拷贝一份出来,然后在新的数组做写操作,
* 写完之后,再将原来的数组引用指向到新数组。
* 具备线程安全,并且适用于高并发场景
*/
private static CopyOnWriteArrayList<WebSocketServer> sWebSocketServers = new CopyOnWriteArrayList<>();
private Session mSession; // 与客户端连接的会话,用于发送数据
private long mVmcNo; // 客户端的标识(这里以机器编号)
private Log mLog = LogFactory.getLog(WebSocketServer.class);

@OnOpen
public void onOpen(Session session, @PathParam("vmcNo") long vmcNo){
mSession = session;
sWebSocketServers.add(this); // 将回话保存
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);
// 这里选择的是让其他客户端都知道消息,类似于转发的聊天室,可根据使用场景使用
for (WebSocketServer socketServer : sWebSocketServers){
socketServer.sendMessage("i have rcv you message");
}
}

/** 对外发送消息
* @param message
*/
public boolean sendMessage(String message){
try {
mSession.getBasicRemote().sendText(message);
} catch (IOException e) {
mLog.info(e.toString());
return false;
}
return true;
}

/** 对某个机器发送消息
 * @param message
 * @param vmcNo 机器编号
 * @return true,返回发送的消息,false,返回failed字符串
*/
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";
}
}
  • 添加外部访问接口(如不需要,可不写,这里主要是想通过该接口给机器发送命令)
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";
}
}

这里需要注意的是,springboot+websocket可能存在这样的一个bug,使用IntelliJ IDE的package打包成jar包的功能时,本地运行时一切正常,但是打包却报javax.websocket.server.ServerContainer not available错误,这是因为test不通过,导致打包失败。
解决方法是使用maven的命令行打包。打开系统cmd命令,进入工程根目录下,输入mvn package -DskipTests
这里写图片描述

Android端

Android端的流程跟服务器端的差不多。

  • 创建Android工程(废话)

  • 加入gradle依赖

    在工程build.gradle(Module:app)文件中加入以下依赖:

compile "org.java-websocket:Java-WebSocket:1.3.8"
  • 编写WebSocket消息连接及处理类
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;
    /**
     *  路径为ws+服务器地址+服务器端设置的子路径+参数(这里对应服务器端机器编号为参数)
     *  如果服务器端为https的,则前缀的ws则变为wss
     */
    private static final String mAddress = "ws://服务器地址:端口/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());
    }

    /** 初始化
     * @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();
    }

    /** 发送消息广播
     * @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("发送收到的消息");
            mContext.sendBroadcast(intent);
        }
    }

}

在应用初始化时调用:

WebClient.initWebSocket(this,10086);

记得添加网络权限。

猜你喜欢

转载自blog.csdn.net/u013564742/article/details/81041740