springboot下的websocket服務端和客戶端編寫

  • 一:服務端

            引入maven依賴
    
        <!-- websocket-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
		</dependency>
        <!-- 引入外部SDK-->
        <dependency>
			<groupId>com.suning.api.sdk</groupId>
			<artifactId>api-push-sdk</artifactId>
			<version>suning-sdk-java-standard-20200610</version>
			<scope>system</scope>
			<!--1、某模塊根目錄下的,src建立一個lib,把jar放入(選)-->
			<!--2、把jar放入maven庫建立版本,通過maven私庫拉取-->
			<systemPath>${project.basedir}/lib/suning-sdk-java-standard-20200610.jar</systemPath>
		</dependency>
       服務端寫法,有詳細註釋
package com.xxx.xx.dispatch.module.platform.websocket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.suning.api.message.Message;
import com.suning.api.util.EncryptMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@ServerEndpoint("/websocket")
@Component
public class WebSocketServer implements InitializingBean{

    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;

    @Value("${mmcs.kafka.suning.subscription.topic:suning-subscription-topic}")
    private String subscriptionTopic;

    private Session session;
    private String TOPIC = "suning_virtualcomm_order_create";
    private String CLIENT_TYPE = "java";
    private String version = "1.0";
    private static  String SUNING_SUBSCRIPTION_TOPIC;
    /**
    WebSocket是多對象,在連接時才實例化;無法在啓動時被spring(單例模式)bean對象注入
    引文1:https://blog.csdn.net/Programmer__Wang/article/details/88538993
    引文2:https://blog.csdn.net/CoderYin/article/details/90173118
     */
    private static  KafkaTemplate<String, String> kafkaTemplateSu;

    @Override
    public void afterPropertiesSet() throws Exception {
        SUNING_SUBSCRIPTION_TOPIC=subscriptionTopic;
        kafkaTemplateSu=kafkaTemplate;
    }

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        log.info("start 一個 webSocket連接:{}", session.getId());
    }

    /**
     * 收到客戶端消息後調用的方法
     * @param message  客戶端發送過來的消息
     * @param session
     * @return
     */
    @OnMessage
    public String onMessage(String message, Session session) {
        log.info("收到客戶端發送的信息:{}", message);
        //解析xx的消息體
        Message messageSu = JSON.parseObject(message,new TypeReference<Message>(){});
        //校驗topic
        if(!TOPIC.equals(messageSu.getTopic())){
            //xxxx訂單推送主題不合符
            return "";
        }
        String uriStr = session.getRequestURI().getPath();
        log.info("客戶端回拼的帶參路徑URI:{}", uriStr);
        //從uri中獲取
        String signSu =getParamByUrl(uriStr,"sign");
        String timestampStr=getParamByUrl(uriStr,"timestamp");
        long timestamp=Long.parseLong(timestampStr==null?"0":timestampStr);
        //校驗sign
        String sign = this.getSignStr(messageSu.getAppKey(), "messageSu.appSecret", version, CLIENT_TYPE,timestamp);
        //存入Kafka的內容
        /**
         * {
         *     "orderId": "110022132",     --
         *     "supplierCode": "100101000" --
         * }
         */
        String msg =messageSu.getMsg();
        kafkaTemplateSu.send(SUNING_SUBSCRIPTION_TOPIC,msg);
        log.info("success to send kafka msg:{}", msg);

        log.info("當前的sessionId:{}", session.getId());
        return "SUCCESS";
    }

    @OnClose
    public void onClose(Session session, CloseReason reason) {
        log.info("webSocket連接關閉:sessionId:"+session.getId() + "關閉原因是:"+reason.getReasonPhrase() + "code:"+reason.getCloseCode());
    }

    @OnError
    public void onError(Throwable t) {
        log.info("webSocket連接發生錯誤!");
        t.printStackTrace();
    }

    private String getSignStr(String appKey, String appSecret, String version, String clientType, long timestamp) {
        StringBuilder signSource = (new StringBuilder()).append(appKey).append(appSecret).append(version).append(clientType).append(timestamp);
        return EncryptMessage.encryptMessage("MD5", signSource.toString());
    }

    /**
     * 獲取指定url中的某個參數
     * @param url
     * @param name
     * @return
     */
    private String getParamByUrl(String url, String name) {
        url += "&";
        String pattern = "(\\?|&){1}#{0,1}" + name + "=[a-zA-Z0-9]*(&{1})";
        Pattern r = Pattern.compile(pattern);
        Matcher m = r.matcher(url);
        if (m.find( )) {
            return m.group(0).split("=")[1].replace("&", "");
        } else {
            return null;
        }
    }
}

測試服務端的結果
1、在線測試 http://coolaf.com/tool/chattest

ws://127.0.0.1:8002/xxxx/websocket 配送的地址 ws://ip:端口/項目名/具體類。 這個項目路徑參數這行代碼: server.port=8002 server.context-path=/項目名

  • 二:客戶端

  •   引入maven依賴
    
        <!--websocket作爲客戶端-->
		<dependency>
			<groupId>org.java-websocket</groupId>
			<artifactId>Java-WebSocket</artifactId>
			<version>1.4.0</version>
		</dependency>
  • 第一種寫法
package com.xxxx.xxx.dispatch.module.platform.websocket;

import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.websocket.Session;
import java.net.URI;

@Slf4j
@Component
public class MyWebSocketClient extends WebSocketClient {
    //這中寫法報錯,Java.net.URI無法找到,沒有解決
    @Value("${mmcs.kafka.suning.subscription.topic:suning-subscription-topic}")
    private String subscriptionTopic;

    private Session session;
    private String TOPIC = "suning_virtualcomm_order_create";
    private String CLIENT_TYPE = "java";
    private String version = "1.0";

    public MyWebSocketClient(URI serverUri) {
        super(serverUri);
    }

    @Override
    public void onOpen(ServerHandshake serverHandshake) {
        log.info("=====MyWebSocket onOpen======");
    }

    //@Override
    public void onMessage(String s) {
        log.info("-------- 接收到服務端數據: " + s + "--------");
    }

    //@Override
    public void onClose(int i, String s, boolean b) {
        log.info("=====MyWebSocket onClose======");
    }

    //@Override
    public void onError(Exception e) {
        log.info("=====MyWebSocket onError======");
    }
}

第二種寫法

package com.xxxx.xx.dispatch.module.platform.websocket;

import com.alibaba.fastjson.JSON;
import com.suning.api.message.Message;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.enums.ReadyState;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.NotYetConnectedException;

@Slf4j
public class MyWebSocketTest{

    private String TOPIC = "suning_virtualcomm_order_create";

    /*public static void main(String[] arg0){
        log.info("=====MyWebSocket onOpen======");
        //ws://ip:端口/項目名/websocket/用戶id
        URI serverUri =URI.create("ws://127.0.0.1:8002/webSocketServer/su");
        MyWebSocketClient myClient = new MyWebSocketClient(serverUri);
        Message message = new Message();
        message.setTopic("suning_virtualcomm_order_create");
        message.setMsg("{\n" +
                "    \"orderId\": \"110022132\",\n" +
                "    \"supplierCode\": \"100101000\"\n" +
                "}");
        myClient.send(JSON.toJSON(message).toString());

        try {
            myClient.connect();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }*/

    public static WebSocketClient client;

    public static void main(String[] args) throws URISyntaxException, NotYetConnectedException, UnsupportedEncodingException {
        // ws://localhost:8085/websocket/   10.1.5.245:8002  10.1.123.19
        //IP:10.12.12.77,Port:8050],success:true
            // websocket = new WebSocket("ws://192.168.2.107:8085/websocket");
        client = new WebSocketClient(new URI("ws://localhost:8002/xxxx/websocket")) {

            @Override
            public void onOpen(ServerHandshake serverHandshake) {
                System.out.println("onOpen");
                log.info("=====MyWebSocket onOpen======");
            }

            @Override
            public void onMessage(String message) {
                System.out.println("接收到服務端數據");
                log.info("-------- 接收到服務端數據: " + message + "--------");
                try {
                    // 主題名(當監聽多個主題時,注意根據主題判斷進行業務邏輯處理)
                    System.err.println("topic:" + message.getTopic());
                    // 消息內容
                    System.err.println("message:" + message.getMsg());
                } catch (Exception e) {
                    e.printStackTrace();

                    // 當需要消息重傳時,拋出該異常
                    // 注意:不是所有的異常都需要系統重試。
                    // 對於字段不全、主鍵衝突問題,導致寫DB異常,
                    //不可重傳,否則消息會一直重傳
                    // 對於,由於網絡問題,權限問題導致的失敗,可重傳。
                    // 不要濫用,否則會引起系統不穩定
                    throw new RetransmissionException();
                }
            }

            @Override
            public void onClose(int i, String s, boolean b) {
                System.out.println("onClose");
                log.info("=====MyWebSocket onClose======");
            }

            @Override
            public void onError(Exception e) {
                System.out.println("onError");
                log.info("=====MyWebSocket onError======");
            }
        };

        client.connect();

        while(!client.getReadyState().equals(ReadyState.OPEN)){
            System.out.println("還沒有打開");
            System.out.println("連接中···請稍後");
        }
        System.out.println("打開了");
        //send("hello world");

        Message message = new Message();
        message.setTopic("suning_virtualcomm_order_create");
        message.setMsg("{\n" +
                "    \"orderId\": \"110022132\",\n" +
                "    \"supplierCode\": \"100101000\"\n" +
                "}");
        client.send(JSON.toJSON(message).toString());
    }

    public static void send(String str){
        client.send(str);
    }
}

啓動服務端(eureka上服務註冊上),直接運行test的main方法,全部用debug運行進行調試。

其它資料

Kafka在bin下命令添加主題,在bin下運行如下命令
./kafka-topics.sh --create --zookeeper 10.1.5.244:2181,10.1.5.244:12181,10.1.5.245:2181/kafka --replication-factor 2 --partitions 10 --topic su-subscription-topic

查看kafka topic列表,使用--list參數
./kafka-topics.sh -list -zookeeper 10.1.5.244:2181,10.1.5.244:12181,10.1.5.245:2181/kafka
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章