在服务器端使用Spring框架在android中设置Stomp客户端

雌铁蛋白95

我正在开发一个与Spring中配置的码头服务器交换数据的android应用程序。为了获得更动态的android应用程序,我正在尝试将WebSocket协议与Stomp消息一起使用。

为了实现这些功能,我在春季配置了一个Web套接字消息代理:

@Configuration
//@EnableScheduling
@ComponentScan(
        basePackages="project.web",
        excludeFilters = @ComponentScan.Filter(type= FilterType.ANNOTATION, value = Configuration.class)
)
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/message");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/client");
    }
}

还有一个SimpMessageSendingOperationsin Spring控制器,用于将消息从服​​务器发送到客户端:

@Controller
public class MessageAddController {
    private final Log log = LogFactory.getLog(MessageAddController.class);

    private SimpMessageSendingOperations messagingTemplate;

    private UserManager userManager;

    private MessageManager messageManager;

    @Autowired
    public MessageAddController(SimpMessageSendingOperations messagingTemplate, 
            UserManager userManager, MessageManager messageManager){
        this.messagingTemplate = messagingTemplate;
        this.userManager = userManager;
        this.messageManager = messageManager;
    }

    @RequestMapping("/Message/Add")
    @ResponseBody
    public SimpleMessage addFriendship(
            @RequestParam String content,
            @RequestParam Long otherUser_id
    ){
        if(log.isInfoEnabled())
            log.info("Execute MessageAdd action");
        SimpleMessage simpleMessage;

        try{
            User curentUser = userManager.getCurrentUser();
            User otherUser = userManager.findUser(otherUser_id);

            Message message = new Message();
            message.setContent(content);
            message.setUserSender(curentUser);
            message.setUserReceiver(otherUser);

            messageManager.createMessage(message);          
            Message newMessage = messageManager.findLastMessageCreated();

            messagingTemplate.convertAndSend( 
                    "/message/add", newMessage);//send message through websocket

            simpleMessage = new SimpleMessage(null, newMessage);
        } catch (Exception e) {
            if(log.isErrorEnabled())
                log.error("A problem of type : " + e.getClass() 
                        + " has occured, with message : " + e.getMessage());
            simpleMessage = new SimpleMessage(
                            new SimpleException(e.getClass(), e.getMessage()), null);
        }
        return simpleMessage;
    }
}

当我使用stomp.js在Web浏览器中测试此配置时,我没有任何问题:在Web浏览器和Jetty服务器之间可以完美地交换消息。用于网络浏览器的JavaScript代码测试:

    var stompClient = null;

    function setConnected(connected) {
        document.getElementById('connect').disabled = connected;
        document.getElementById('disconnect').disabled = !connected;
        document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
        document.getElementById('response').innerHTML = '';
    }

    function connect() {
        stompClient = Stomp.client("ws://YOUR_IP/client");         
        stompClient.connect({}, function(frame) {
            setConnected(true);
            stompClient.subscribe('/message/add', function(message){
                showMessage(JSON.parse(message.body).content);
            });
        });
    }

    function disconnect() {
        stompClient.disconnect();
        setConnected(false);
        console.log("Disconnected");
    }


    function showMessage(message) {
        var response = document.getElementById('response');
        var p = document.createElement('p');
        p.style.wordWrap = 'break-word';
        p.appendChild(document.createTextNode(message));
        response.appendChild(p);
    }

当我尝试在Android上使用带有gozirra,activemq-stomp之类的库或其他库的stomp时,会发生问题:大多数时候,与服务器的连接不起作用。我的应用程序停止运行,几分钟后,我在logcat中收到以下消息:java.net.UnknownHostException: Unable to resolve host "ws://192.168.1.39/client": No address associated with hostname,我不明白为什么。使用Gozzira库的代码来管理我的android活动中的踩踏上诉:

private void stomp_test() {
    String ip = "ws://192.172.6.39/client";
    int port = 8080;

    String channel = "/message/add";
    Client c;

    try {
        c = new Client( ip, port, "", "" );
        Log.i("Stomp", "Connection established");
        c.subscribe( channel, new Listener() {
            public void message( Map header, String message ) {
                Log.i("Stomp", "Message received!!!");
              }
        });

    } catch (IOException ex) {
        Log.e("Stomp", ex.getMessage());
        ex.printStackTrace();

    } catch (LoginException ex) {
        Log.e("Stomp", ex.getMessage());
        ex.printStackTrace();
    } catch (Exception ex) {
        Log.e("Stomp", ex.getMessage());
        ex.printStackTrace();
    }

}

经过研究,我发现大多数想通过Java Client在websocket上使用stomp的人都使用ActiveMQ服务器,就像在本站点中一样但是spring工具使用起来非常简单,如果我现在可以保留我的服务器层,那将会很酷。有人会知道如何在服务器端使用Spring配置在客户端使用stomp java(Android)吗?

雌铁蛋白95

我实现了在Android和Spring Server的Web套接字上使用stomp。

为此,我使用了一个Web套接字库:werbench(单击此链接下载)。要安装,我使用了maven命令mvn install,然后将jar恢复到本地存储库中。然后,我需要在基本的Web套接字上添加一个stomp层,但是我在Java中找不到任何可以在Web套接字上管理stomp的stomp库(我不得不放弃gozzira)。因此,我创建了自己的模型(使用stomp.js这样的模型)。不要犹豫,问我是否要看看它,但是我很快就意识到了这一点,因此它无法像stomp.js一样多地进行管理。然后,我需要通过spring服务器实现身份验证。为此,我遵循了该网站的指示当我取回JSESSIONID cookie时,我只需要在自己的“库”的werbench Web套接字的实例化中使用此cookie声明一个标头。

编辑:这是该库中的主要类,该类通过Web套接字连接管理脚踏:

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import android.util.Log;
import de.roderick.weberknecht.WebSocket;
import de.roderick.weberknecht.WebSocketEventHandler;
import de.roderick.weberknecht.WebSocketMessage;

public class Stomp {

    private static final String TAG = Stomp.class.getSimpleName();

    public static final int CONNECTED = 1;//Connection completely established
    public static final int NOT_AGAIN_CONNECTED = 2;//Connection process is ongoing
    public static final int DECONNECTED_FROM_OTHER = 3;//Error, no more internet connection, etc.
    public static final int DECONNECTED_FROM_APP = 4;//application explicitely ask for shut down the connection 

    private static final String PREFIX_ID_SUBSCIPTION = "sub-";
    private static final String ACCEPT_VERSION_NAME = "accept-version";
    private static final String ACCEPT_VERSION = "1.1,1.0";
    private static final String COMMAND_CONNECT = "CONNECT";
    private static final String COMMAND_CONNECTED = "CONNECTED";
    private static final String COMMAND_MESSAGE = "MESSAGE";
    private static final String COMMAND_RECEIPT = "RECEIPT";
    private static final String COMMAND_ERROR = "ERROR";
    private static final String COMMAND_DISCONNECT = "DISCONNECT";
    private static final String COMMAND_SEND = "SEND";
    private static final String COMMAND_SUBSCRIBE = "SUBSCRIBE";
    private static final String COMMAND_UNSUBSCRIBE = "UNSUBSCRIBE";
    private static final String SUBSCRIPTION_ID = "id";
    private static final String SUBSCRIPTION_DESTINATION = "destination";
    private static final String SUBSCRIPTION_SUBSCRIPTION = "subscription";


    private static final Set<String> VERSIONS = new HashSet<String>();
    static {
        VERSIONS.add("V1.0");
        VERSIONS.add("V1.1");
        VERSIONS.add("V1.2");
    }

    private WebSocket websocket;

    private int counter;

    private int connection;

    private Map<String, String> headers;

    private int maxWebSocketFrameSize;

    private Map<String, Subscription> subscriptions;

    private ListenerWSNetwork networkListener;

    /**
     * Constructor of a stomp object. Only url used to set up a connection with a server can be instantiate
     * 
     * @param url
     *      the url of the server to connect with
     */
    public Stomp(String url, Map<String,String> headersSetup, ListenerWSNetwork stompStates){       
        try {
            this.websocket = new WebSocket(new URI(url), null, headersSetup);
            this.counter = 0;

            this.headers = new HashMap<String, String>();
            this.maxWebSocketFrameSize = 16 * 1024;
            this.connection = NOT_AGAIN_CONNECTED;
            this.networkListener = stompStates;
            this.networkListener.onState(NOT_AGAIN_CONNECTED);
            this.subscriptions = new HashMap<String, Subscription>();

            this.websocket.setEventHandler(new WebSocketEventHandler() {        
                @Override
                public void onOpen(){
                    if(Stomp.this.headers != null){                                         
                        Stomp.this.headers.put(ACCEPT_VERSION_NAME, ACCEPT_VERSION);

                        transmit(COMMAND_CONNECT, Stomp.this.headers, null);

                        Log.d(TAG, "...Web Socket Openned");
                    }
                }

                @Override
                public void onMessage(WebSocketMessage message) {
                    Log.d(TAG, "<<< " + message.getText());
                    Frame frame = Frame.fromString(message.getText());
                    boolean isMessageConnected = false;

                    if(frame.getCommand().equals(COMMAND_CONNECTED)){
                        Stomp.this.connection = CONNECTED;
                        Stomp.this.networkListener.onState(CONNECTED);

                        Log.d(TAG, "connected to server : " + frame.getHeaders().get("server"));
                        isMessageConnected = true;

                    } else if(frame.getCommand().equals(COMMAND_MESSAGE)){
                        String subscription = frame.getHeaders().get(SUBSCRIPTION_SUBSCRIPTION);
                        ListenerSubscription onReceive = Stomp.this.subscriptions.get(subscription).getCallback();

                        if(onReceive != null){
                            onReceive.onMessage(frame.getHeaders(), frame.getBody());
                        } else{
                            Log.e(TAG, "Error : Subscription with id = " + subscription + " had not been subscribed");
                            //ACTION TO DETERMINE TO MANAGE SUBCRIPTION ERROR
                        }

                    } else if(frame.getCommand().equals(COMMAND_RECEIPT)){
                        //I DON'T KNOW WHAT A RECEIPT STOMP MESSAGE IS

                    } else if(frame.getCommand().equals(COMMAND_ERROR)){
                        Log.e(TAG, "Error : Headers = " + frame.getHeaders() + ", Body = " + frame.getBody());
                        //ACTION TO DETERMINE TO MANAGE ERROR MESSAGE

                    } else {

                    }

                    if(isMessageConnected)
                        Stomp.this.subscribe();
                }

                @Override
                public void onClose(){
                    if(connection == DECONNECTED_FROM_APP){
                        Log.d(TAG, "Web Socket disconnected");
                        disconnectFromApp();
                    } else{
                        Log.w(TAG, "Problem : Web Socket disconnected whereas Stomp disconnect method has never "
                                + "been called.");
                        disconnectFromServer();
                    }
                }

                @Override
                public void onPing() {

                }

                @Override
                public void onPong() {

                }

                @Override
                public void onError(IOException e) {
                    Log.e(TAG, "Error : " + e.getMessage());                
                }
            });
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    /**
     * Send a message to server thanks to websocket
     * 
     * @param command
     *      one of a frame property, see {@link Frame} for more details
     * @param headers
     *      one of a frame property, see {@link Frame} for more details
     * @param body
     *      one of a frame property, see {@link Frame} for more details
     */
    private void transmit(String command, Map<String, String> headers, String body){
        String out = Frame.marshall(command, headers, body);
        Log.d(TAG, ">>> " + out);
        while (true) {
            if (out.length() > this.maxWebSocketFrameSize) {
                this.websocket.send(out.substring(0, this.maxWebSocketFrameSize));
                out = out.substring(this.maxWebSocketFrameSize);
            } else {
                this.websocket.send(out);
                break;
            }
        }
    }

    /**
     * Set up a web socket connection with a server
     */
    public void connect(){
        if(this.connection != CONNECTED){
            Log.d(TAG, "Opening Web Socket...");
            try{
                this.websocket.connect();
            } catch (Exception e){
                Log.w(TAG, "Impossible to establish a connection : " + e.getClass() + ":" + e.getMessage());
            }
        }
    }

    /**
     * disconnection come from the server, without any intervention of client side. Operations order is very important
     */
    private void disconnectFromServer(){
        if(this.connection == CONNECTED){
            this.connection = DECONNECTED_FROM_OTHER;
            this.websocket.close();
            this.networkListener.onState(this.connection);
        }
    }

    /**
     * disconnection come from the app, because the public method disconnect was called
     */
    private void disconnectFromApp(){
        if(this.connection == DECONNECTED_FROM_APP){
            this.websocket.close();
            this.networkListener.onState(this.connection);
        }
    }

    /**
     * Close the web socket connection with the server. Operations order is very important
     */
    public void disconnect(){
        if(this.connection == CONNECTED){
            this.connection = DECONNECTED_FROM_APP;
            transmit(COMMAND_DISCONNECT, null, null);
        }
    }

    /**
     * Send a simple message to the server thanks to the body parameter
     * 
     * 
     * @param destination
     *      The destination through a Stomp message will be send to the server
     * @param headers
     *      headers of the message
     * @param body
     *      body of a message
     */
    public void send(String destination, Map<String,String> headers, String body){
        if(this.connection == CONNECTED){
            if(headers == null)
                headers = new HashMap<String, String>();

            if(body == null)
                body = "";

            headers.put(SUBSCRIPTION_DESTINATION, destination);

            transmit(COMMAND_SEND, headers, body);
        }
    }

    /**
     * Allow a client to send a subscription message to the server independently of the initialization of the web socket.
     * If connection have not been already done, just save the subscription
     * 
     * @param subscription
     *      a subscription object
     */
    public void subscribe(Subscription subscription){
        subscription.setId(PREFIX_ID_SUBSCIPTION + this.counter++);
        this.subscriptions.put(subscription.getId(), subscription);

        if(this.connection == CONNECTED){   
            Map<String, String> headers = new HashMap<String, String>();            
            headers.put(SUBSCRIPTION_ID, subscription.getId());
            headers.put(SUBSCRIPTION_DESTINATION, subscription.getDestination());

            subscribe(headers);
        }
    }

    /**
     * Subscribe to a Stomp channel, through messages will be send and received. A message send from a determine channel
     * can not be receive in an another.
     *
     */
    private void subscribe(){
        if(this.connection == CONNECTED){
            for(Subscription subscription : this.subscriptions.values()){
                Map<String, String> headers = new HashMap<String, String>();            
                headers.put(SUBSCRIPTION_ID, subscription.getId());
                headers.put(SUBSCRIPTION_DESTINATION, subscription.getDestination());

                subscribe(headers);
            }
        }
    }

    /**
     * Send the subscribe to the server with an header
     * @param headers
     *      header of a subscribe STOMP message
     */
    private void subscribe(Map<String, String> headers){
        transmit(COMMAND_SUBSCRIBE, headers, null);
    }

    /**
     * Destroy a subscription with its id
     * 
     * @param id
     *      the id of the subscription. This id is automatically setting up in the subscribe method
     */
    public void unsubscribe(String id){
        if(this.connection == CONNECTED){
            Map<String, String> headers = new HashMap<String, String>();
            headers.put(SUBSCRIPTION_ID, id);

            this.subscriptions.remove(id);
            this.transmit(COMMAND_UNSUBSCRIBE, headers, null);
        }
    }
}

这是a脚消息的框架:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Frame {
//  private final static String CONTENT_LENGTH = "content-length";

    private String command;
    private Map<String, String> headers;
    private String body;

    /**
     * Constructor of a Frame object. All parameters of a frame can be instantiate
     * 
     * @param command
     * @param headers
     * @param body
     */
    public Frame(String command, Map<String, String> headers, String body){
        this.command = command;     
        this.headers = headers != null ? headers : new HashMap<String, String>();
        this.body = body != null ? body : "";
    }

    public String getCommand(){
        return command;
    }

    public Map<String, String> getHeaders(){
        return headers;
    }

    public String getBody(){
        return body;
    }

    /**
     * Transform a frame object into a String. This method is copied on the objective C one, in the MMPReactiveStompClient
     * library
     * @return a frame object convert in a String
     */
    private String toStringg(){
        String strLines = this.command;
        strLines += Byte.LF;
        for(String key : this.headers.keySet()){
            strLines += key + ":" + this.headers.get(key);
            strLines += Byte.LF;
        }
        strLines += Byte.LF;
        strLines += this.body;
        strLines += Byte.NULL;

        return strLines;
    }

    /**
     * Create a frame from a received message. This method is copied on the objective C one, in the MMPReactiveStompClient
     * library
     * 
     * @param data
     *  a part of the message received from network, which represented a frame
     * @return
     *  An object frame
     */
    public static Frame fromString(String data){
        List<String> contents = new ArrayList<String>(Arrays.asList(data.split(Byte.LF)));

        while(contents.size() > 0 && contents.get(0).equals("")){
            contents.remove(0);
        }

        String command = contents.get(0);
        Map<String, String> headers = new HashMap<String, String>();
        String body = "";

        contents.remove(0);
        boolean hasHeaders = false;
        for(String line : contents){
            if(hasHeaders){
                for(int i=0; i < line.length(); i++){
                    Character c = line.charAt(i);
                    if(!c.equals('\0'))
                        body += c;
                }
            } else{
                if(line.equals("")){
                    hasHeaders = true;
                } else {
                    String[] header = line.split(":");
                    headers.put(header[0], header[1]);
                }
            }
        }
        return new Frame(command, headers, body);   
    }

//    No need this method, a single frame will be always be send because body of the message will never be excessive
//    /**
//     * Transform a message received from server in a Set of objects, named frame, manageable by java
//     * 
//     * @param datas
//     *        message received from network
//     * @return
//     *        a Set of Frame
//     */
//    public static Set<Frame> unmarshall(String datas){
//      String data;
//      String[] ref = datas.split(Byte.NULL + Byte.LF + "*");//NEED TO VERIFY THIS PARAMETER
//      Set<Frame> results = new HashSet<Frame>();
//      
//      for (int i = 0, len = ref.length; i < len; i++) {
//            data = ref[i];
//            
//            if ((data != null ? data.length() : 0) > 0){
//              results.add(unmarshallSingle(data));//"unmarshallSingle" is the old name method for "fromString"
//            }
//        }         
//      return results;
//    }

    /**
     * Create a frame with based fame component and convert them into a string
     * 
     * @param command
     * @param headers
     * @param body
     * @return  a frame object convert in a String, thanks to <code>toStringg()</code> method
     */
    public static String marshall(String command, Map<String, String> headers, String body){
        Frame frame = new Frame(command, headers, body);
        return frame.toStringg();
    }

    private class Byte {
        public static final String LF = "\n";
        public static final String NULL = "\0";
    }
}

这个对象是用于通过stomp协议建立订阅的对象:

public class Subscription {

    private String id;

    private String destination;

    private ListenerSubscription callback;

    public Subscription(String destination, ListenerSubscription callback){
        this.destination = destination;
        this.callback = callback;
    }

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }

    public String getDestination() {
        return destination;
    }

    public ListenerSubscription getCallback() {
        return callback;
    }
}

至少有两个接口用作“运行” java类,以侦听Web套接字网络和给定的订阅渠道

public interface ListenerWSNetwork {
    public void onState(int state);
}

import java.util.Map;
public interface ListenerSubscription {
    public void onMessage(Map<String, String> headers, String body);
}

欲了解更多信息,请随时问我。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

在服务器端使用Spring框架在android中设置Stomp客户端

来自分类Dev

如何使用videoview在android中播放流?无法在客户端打开文件,尝试在服务器端

来自分类Dev

客户端与服务器端模板(哪个?)

来自分类Dev

Mongodb服务器端与客户端处理

来自分类Dev

客户端服务器端模板nodejs

来自分类Dev

在服务器端访问客户端变量

来自分类Dev

服务器端或客户端渲染

来自分类Dev

客户端服务器端模板nodejs

来自分类Dev

在客户端委派服务器端验证

来自分类Dev

客户端数据到服务器端

来自分类Dev

使用Echo框架在Go Lang中安装SSL证书时出现问题(客户端向HTTPS服务器发送了HTTP请求。)

来自分类Dev

这是从客户端设置服务器端Cookie的好方法吗?

来自分类Dev

在客户端与服务器端设置跟踪 cookie?

来自分类Dev

在Java中从客户端查找使用的服务器端口

来自分类Dev

如何在Dart中创建客户端和服务器端都使用的通用类

来自分类Dev

如何从流星中的客户端调用服务器端功能

来自分类Dev

在Meteor中,如何在服务器端完成操作时在客户端知道?

来自分类Dev

动态路由在Next JS中呈现在服务器端还是客户端?

来自分类Dev

Rxvt中的Xft字体规范:是客户端字体还是服务器端字体?

来自分类Dev

Facebook API中的服务器端访问令牌与客户端访问令牌

来自分类Dev

如何从流星中的客户端调用服务器端功能

来自分类Dev

客户端和服务器端javascript中“ this”的值之间的差异

来自分类Dev

在JavaScript中的客户端调用服务器端变量

来自分类Dev

如何在OpenVPN中与服务器端共享客户端Internet连接?

来自分类Dev

在node.js中从服务器端调用客户端函数

来自分类Dev

在MVC中,model.isvalid是检查客户端还是服务器端?

来自分类Dev

在 NodeJs 中从客户端调用服务器端函数

来自分类常见问题

客户端路由(使用react-router)和服务器端路由

来自分类Dev

使用Play Framework进行客户端/服务器端验证

Related 相关文章

  1. 1

    在服务器端使用Spring框架在android中设置Stomp客户端

  2. 2

    如何使用videoview在android中播放流?无法在客户端打开文件,尝试在服务器端

  3. 3

    客户端与服务器端模板(哪个?)

  4. 4

    Mongodb服务器端与客户端处理

  5. 5

    客户端服务器端模板nodejs

  6. 6

    在服务器端访问客户端变量

  7. 7

    服务器端或客户端渲染

  8. 8

    客户端服务器端模板nodejs

  9. 9

    在客户端委派服务器端验证

  10. 10

    客户端数据到服务器端

  11. 11

    使用Echo框架在Go Lang中安装SSL证书时出现问题(客户端向HTTPS服务器发送了HTTP请求。)

  12. 12

    这是从客户端设置服务器端Cookie的好方法吗?

  13. 13

    在客户端与服务器端设置跟踪 cookie?

  14. 14

    在Java中从客户端查找使用的服务器端口

  15. 15

    如何在Dart中创建客户端和服务器端都使用的通用类

  16. 16

    如何从流星中的客户端调用服务器端功能

  17. 17

    在Meteor中,如何在服务器端完成操作时在客户端知道?

  18. 18

    动态路由在Next JS中呈现在服务器端还是客户端?

  19. 19

    Rxvt中的Xft字体规范:是客户端字体还是服务器端字体?

  20. 20

    Facebook API中的服务器端访问令牌与客户端访问令牌

  21. 21

    如何从流星中的客户端调用服务器端功能

  22. 22

    客户端和服务器端javascript中“ this”的值之间的差异

  23. 23

    在JavaScript中的客户端调用服务器端变量

  24. 24

    如何在OpenVPN中与服务器端共享客户端Internet连接?

  25. 25

    在node.js中从服务器端调用客户端函数

  26. 26

    在MVC中,model.isvalid是检查客户端还是服务器端?

  27. 27

    在 NodeJs 中从客户端调用服务器端函数

  28. 28

    客户端路由(使用react-router)和服务器端路由

  29. 29

    使用Play Framework进行客户端/服务器端验证

热门标签

归档