Android端WebSocket对接SpringBoot聊天服务问题求助
Let's tackle your two problems one by one, starting with the connection issue since that's blocking you first.
1. Fixing the Heroku Connection Failure
The error you're seeing (OpeningHandshakeException: The status code of the opening handshake response is not '101 Switching Protocols') happens for two key reasons:
- Your Spring Boot service uses a SockJS endpoint, but your Android code is using a raw WebSocket client. SockJS has a specific handshake flow (it starts with an HTTP 200 check before upgrading to WebSocket) that raw clients don't handle.
- Heroku enforces HTTPS by default, so you need to use
wss://(WebSocket Secure) instead ofws://for your endpoint URL—plain HTTP connections may be blocked or redirected.
Fix Steps:
First, use a STOMP + SockJS compatible Android client. A popular choice is the stomp-websocket-android library.
Add Dependency
Add this to your app-level build.gradle (Module level):
implementation 'com.github.gdiazs:stomp-websocket-android:1.6.0'
Update Connection Code
Replace your ServerConnection class with code that uses the STOMP client, handles SockJS, and uses the correct wss:// URL:
import android.os.AsyncTask; import android.util.Log; import com.github.gdiazs.stompandroid.StompClient; import com.github.gdiazs.stompandroid.dto.LifecycleEvent; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; class ServerConnection extends AsyncTask<Void, Void, Void> { // Use wss:// for Heroku's HTTPS environment private static final String CHAT_SERVER = "wss://chat-vu.herokuapp.com/chat-messaging"; private StompClient stompClient; @Override protected Void doInBackground(Void... voids) { // Initialize STOMP client with SockJS support stompClient = new StompClient(CHAT_SERVER, true); // true = enable SockJS // Connect and handle lifecycle events stompClient.connect() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(lifecycleEvent -> { switch (lifecycleEvent.getType()) { case OPENED: Log.d("myLog", "Connected to server successfully!"); // Subscribe to your destinations once connected subscribeToMessages(); // Send a test echo message sendEchoMessage("Hi from Android!"); break; case ERROR: Log.e("myLog", "Connection error: ", lifecycleEvent.getException()); break; case CLOSED: Log.d("myLog", "Connection closed"); break; } }); return null; } // Subscribe to different message destinations private void subscribeToMessages() { // Subscribe to private messages (/user/queue/reply) stompClient.topic("/user/queue/reply") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(topicMessage -> { String messageBody = topicMessage.getPayload(); Log.d("myLog", "Received private message: " + messageBody); // Parse JSON and handle the message here }); // Subscribe to echo test replies (/queue/echo-reply) stompClient.topic("/queue/echo-reply") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(topicMessage -> { String echoMessage = topicMessage.getPayload(); Log.d("myLog", "Received echo reply: " + echoMessage); }); } // Send message to /app/echo test endpoint private void sendEchoMessage(String text) { stompClient.send("/app/echo", text) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(() -> Log.d("myLog", "Echo message sent: " + text), error -> Log.e("myLog", "Failed to send echo message: ", error)); } // Send message to /app/message-flow (for real chat) private void sendChatMessage(String messageJson) { stompClient.send("/app/message-flow", messageJson) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(() -> Log.d("myLog", "Chat message sent"), error -> Log.e("myLog", "Failed to send chat message: ", error)); } // Remember to disconnect when done (e.g., in onDestroy) public void disconnect() { if (stompClient != null) { stompClient.disconnect(); } } }
Additional Setup
- Add internet permission to your
AndroidManifest.xml:<uses-permission android:name="android.permission.INTERNET" /> - For Android 9+, since we're using
wss://(encrypted), you don't need extra network security config. If you ever use plain HTTP for testing, you'd need to add a network security policy to allow cleartext traffic.
2. Using Different URLs (STOMP Destinations) on Android
This works exactly like your web client—you just need to use the same destination paths with the STOMP client:
- Sending messages: Use
stompClient.send(destination, payload)where the destination matches your Spring Boot@MessageMappingpaths, prefixed with/app(e.g.,/app/echo,/app/message-flow). - Receiving messages: Use
stompClient.topic(subscriptionPath)to subscribe to the queues your server sends to (e.g.,/user/queue/replyfor private messages,/queue/echo-replyfor test echoes).
The key thing to remember is that STOMP uses destinations (logical paths) instead of raw WebSocket URLs—your client connects once to the SockJS endpoint, then sends/receives messages over that single connection using different destinations.
Final Notes
- Make sure your Heroku service is running and the
/chat-messagingSockJS endpoint is accessible. - When sending the
Messageobject to/app/message-flow, serialize it to JSON just like your web client does (usinguserJson). - Always handle connection lifecycle events (error, closed) to retry connections if needed.
内容的提问来源于stack exchange,提问作者Victor

