簡體   English   中英

Python 客戶端沒有收到來自 Spring websocket 服務器的消息

[英]Python client doesn't receive messages from Spring websocket server

該項目的目標是讓 JVM (Kotlin) 服務器和 Python 客戶端通過 websocket 相互通信。 服務器必須向客戶端發送更新,客戶端將處理這些更新。

服務器是 Spring 引導應用程序,運行 Spring 引導 websocket 服務器。 客戶端現在只是一個 Python 腳本,運行 websocket-client 來連接服務器。

什么工作:

  • 項目清單
  • 客戶端可以連接到服務器
  • 客戶端可以連接到服務器上的主題(使用 STOMP)
  • 客戶端可以向服務器發送消息,服務器接收並處理此消息
  • 服務器可以在 websocket 上發送消息

什么不起作用:

  • 客戶端未收到來自服務器的消息

我已經通過連接到websocket.org(ws://echo.websocket.org)的回顯服務器嘗試了客戶端的接收部分,並且客戶端從服務器接收回顯消息。 所以在我看來,問題不在於客戶端。


代碼時間。

Kotlin 服務器:用於創建 websocket 服務器的代碼:

package nl.sajansen.screenchangenotifierserver

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.handler.annotation.MessageMapping
import org.springframework.messaging.simp.SimpMessagingTemplate
import org.springframework.messaging.simp.config.MessageBrokerRegistry
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Controller
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
import org.springframework.web.socket.config.annotation.StompEndpointRegistry
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer
import java.time.Instant
import java.time.format.DateTimeFormatter


@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig : WebSocketMessageBrokerConfigurer {
    override fun registerStompEndpoints(registry: StompEndpointRegistry) {
        registry.addEndpoint("/ws").setAllowedOrigins("*")
    }

    override fun configureMessageBroker(config: MessageBrokerRegistry) {
        config.enableSimpleBroker("/topic", "/queue")
        config.setApplicationDestinationPrefixes("/app")
    }
}

@Controller
class WebsocketController @Autowired constructor(val template: SimpMessagingTemplate) {
    @Scheduled(fixedDelayString = "1000")
    fun blastToClientsHostReport() {
        println("Sending something on the websocket")
        template.convertAndSend("/topic/greeting", "Hello World");
    }

    @MessageMapping("/greeting")
    fun handle(message: String): String {
        println("Received message: $message")
        template.convertAndSend("/topic/greeting", message)
        return "[" + getTimestamp() + ": " + message
    }
}

fun getTimestamp(): String = DateTimeFormatter.ISO_INSTANT.format(Instant.now())

Gradle 依賴等:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "2.1.9.RELEASE"
    id("io.spring.dependency-management") version "1.0.8.RELEASE"
    kotlin("jvm") version "1.2.71"
    kotlin("plugin.spring") version "1.2.71"
    kotlin("plugin.jpa") version "1.2.71"
    kotlin("plugin.allopen") version "1.2.71"       // For JPA lazy fetching
}

allOpen {
    annotation("javax.persistence.Entity")
    annotation("javax.persistence.Embeddable")
    annotation("javax.persistence.MappedSuperclass")
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    runtimeOnly("com.h2database:h2:1.4.197") // Fixed version as a workaround for https://github.com/h2database/h2database/issues/1841
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    testImplementation("org.springframework.boot:spring-boot-starter-test") {
        exclude(module = "junit")
        exclude(module = "mockito-core")
    }
    testImplementation("org.junit.jupiter:junit-jupiter-api")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
    testImplementation("com.ninja-squad:springmockk:1.1.2")
    compile("org.springframework.boot:spring-boot-starter-websocket")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "1.8"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

Python 客戶端:

import random

import stomper as stomper
import websocket


def main():
  websocket.enableTrace(True)

  # Connecting to websocket
  ws = websocket.create_connection("ws://localhost:8080/ws")

  # Subscribing to topic
  client_id = str(random.randint(0, 1000))
  sub = stomper.subscribe("/topic/greeting", client_id, ack='auto')
  ws.send(sub)

  # Sending some message
  ws.send(stomper.send("/app/greeting", "Hello there"))

  while True:
    print("Receiving data: ")
    d = ws.recv()
    print(d)


if __name__ == '__main__':
  main()

Pip 依賴關系:

opencv-python==4.1.1.26
websocket-client==0.56.0
stomper==0.4.3

控制台 output

現在,服務器的控制台 output 就是這個。 可以看到,當客戶端未連接時,沒有訂閱者發送預定消息。 然后客戶端成功連接並將預定消息廣播給 1 個訂閱者。

Sending something on the websocket
2019-10-17 12:45:09.425 DEBUG 32285 --- [MessageBroker-3] org.springframework.web.SimpLogging      : Processing MESSAGE destination=/topic/greeting session=null payload=Hello World
Sending something on the websocket
2019-10-17 12:45:10.426 DEBUG 32285 --- [MessageBroker-3] org.springframework.web.SimpLogging      : Processing MESSAGE destination=/topic/greeting session=null payload=Hello World
2019-10-17 12:45:10.849  INFO 32285 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-10-17 12:45:10.850  INFO 32285 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2019-10-17 12:45:10.850 DEBUG 32285 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Detected StandardServletMultipartResolver
2019-10-17 12:45:10.855 DEBUG 32285 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : enableLoggingRequestDetails='true': request parameters and headers will be shown which may lead to unsafe logging of potentially sensitive data
2019-10-17 12:45:10.855  INFO 32285 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 5 ms
2019-10-17 12:45:10.861 DEBUG 32285 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : GET "/ws", parameters={}
2019-10-17 12:45:10.865 DEBUG 32285 --- [nio-8080-exec-1] o.s.w.s.s.s.WebSocketHandlerMapping      : Mapped to org.springframework.web.socket.server.support.WebSocketHttpRequestHandler@27a9f025
2019-10-17 12:45:10.872 DEBUG 32285 --- [nio-8080-exec-1] o.s.w.s.s.s.WebSocketHttpRequestHandler  : GET /ws
2019-10-17 12:45:10.885 DEBUG 32285 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed 101 SWITCHING_PROTOCOLS
2019-10-17 12:45:10.901 DEBUG 32285 --- [nio-8080-exec-1] s.w.s.h.LoggingWebSocketHandlerDecorator : New StandardWebSocketSession[id=393fc3cd-9ca3-1749-1ea8-541def6592e0, uri=ws://localhost:8080/ws]
2019-10-17 12:45:10.912 DEBUG 32285 --- [nboundChannel-2] org.springframework.web.SimpLogging      : Processing SUBSCRIBE /topic/greeting id=216 session=393fc3cd-9ca3-1749-1ea8-541def6592e0
2019-10-17 12:45:10.914 DEBUG 32285 --- [nboundChannel-7] .WebSocketAnnotationMethodMessageHandler : Searching methods to handle SEND /app/greeting session=393fc3cd-9ca3-1749-1ea8-541def6592e0 text/plain payload=Hello there, lookupDestination='/greeting'
2019-10-17 12:45:10.915 DEBUG 32285 --- [nboundChannel-7] .WebSocketAnnotationMethodMessageHandler : Invoking nl.sajansen.screenchangenotifierserver.WebsocketController#handle[1 args]
Received message: Hello there
2019-10-17 12:45:10.916 DEBUG 32285 --- [nboundChannel-7] org.springframework.web.SimpLogging      : Processing MESSAGE destination=/topic/greeting session=null payload=Hello there
2019-10-17 12:45:10.916 DEBUG 32285 --- [nboundChannel-7] org.springframework.web.SimpLogging      : Broadcasting to 1 sessions.
2019-10-17 12:45:10.919 DEBUG 32285 --- [nboundChannel-7] org.springframework.web.SimpLogging      : Processing MESSAGE destination=/topic/greeting session=393fc3cd-9ca3-1749-1ea8-541def6592e0 payload=[2019-10-17T10:45:10.917Z: Hello there
2019-10-17 12:45:10.919 DEBUG 32285 --- [nboundChannel-7] org.springframework.web.SimpLogging      : Broadcasting to 1 sessions.
Sending something on the websocket
2019-10-17 12:45:11.427 DEBUG 32285 --- [MessageBroker-3] org.springframework.web.SimpLogging      : Processing MESSAGE destination=/topic/greeting session=null payload=Hello World

客戶端的output是這樣的,只是等到世界末日才收到一條消息:

--- request header ---
GET /ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: localhost:8080
Origin: http://localhost:8080
Sec-WebSocket-Key: 8ihHk0C5C0nji4s7u4atZQ==
Sec-WebSocket-Version: 13


-----------------------
--- response header ---
HTTP/1.1 101
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: Drq8+/vJkXkvToB3/AuCGMdYwuo=
Date: Thu, 17 Oct 2019 10:45:10 GMT
-----------------------
send: b'\x81\xb9\x88k\xa6j\xdb>\xe49\xcb9\xef(\xcda\xcf\x0e\xb2Y\x97\\\x82\x0f\xc3\x19\xfc\x02\xc8\x0b\xfc\x02\xc9\x04\xb2D\xd2\x05\xf8\x02\xc5E\xef\x19\xc3\x0f\xfc\x02\xc8\r\x82\n\xc5\x01\xb2\n\xd3\x1e\xe7a\xacj\x82'
send: b'\x81\xc5\xd0\x8dE6\x83\xc8\x0br\xda\xe9 E\xa4\xe4+W\xa4\xe4*X\xea\xa2$F\xa0\xa2"D\xb5\xe81_\xbe\xeaOU\xbf\xe31S\xbe\xf9hB\xa9\xfd \x0c\xa4\xe8=B\xff\xfd)W\xb9\xe3O<\x98\xe8)Z\xbf\xad1^\xb5\xff 6\xda'
Receiving data:

如果您有更好的想法,請提供更多背景信息:更新將包含一個圖像文件(可能編碼為 base64)。 更新必須接近實時發送(允許延遲不超過 1 秒)。 這些更新的間隔可以從幾分鍾到半秒不等。 客戶端和服務器是兩台不同的機器,在同一個網絡中,但該網絡的吞吐量有限。


那么,誰能發現哪里出了問題?

我已經閱讀了本文檔中有關 websockets 的大部分內容,但我看不出出了什么問題: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#網絡套接字

這個答案也讓我走得很遠,但這個例子本身並沒有開始工作(在將while not True更正為while True之后)。

2019 年 10 月 18 日更新:我尋找了 Python SocketJS 客戶端,因為我得到了 Kotlin 服務器部分與 SocketJS 和 Z686155AF75A60A0F6E9D80C1F7EDD3E 一起使用 But I can't find any Python SocketJS client implementations.. I wonder if the only solution left is to start the websocket server in the Python applicaion (client side) and let the client send it's websocket server details to the Kotlin server, which then將連接到客戶端的 websocket 服務器。 這不是一個很好的解決方案,但我想知道它是否會起作用。 我會及時通知你的。

2021 年 2 月 1 日更新:我沒有再花時間解決這個問題。 但是我會讓這篇文章,以防有人解決這個問題並可以幫助其他人解決問題。

上個月我也面臨同樣的問題。 我假設您在 Python 的 Web 套接字上使用 Stomp。 參考Websocket Client not received any messages ,我想你忘記啟動連接了。

因此,您應該從此更改您的 python 客戶端代碼

...
 # Connecting to websocket
  ws = websocket.create_connection("ws://localhost:8080/ws")

  # Subscribing to topic
  client_id = str(random.randint(0, 1000))
  sub = stomper.subscribe("/topic/greeting", client_id, ack='auto')
  ws.send(sub)
...

進入這個

 # Connecting to websocket
 ws = websocket.create_connection("ws://localhost:8080/ws")
  
 # Initate Stomp connection!
 ws.send("CONNECT\naccept-version:1.0,1.1,2.0\n\n\x00\n")

  # Subscribing to topic
  client_id = str(random.randint(0, 1000))
  sub = stomper.subscribe("/topic/greeting", client_id, ack='auto')
  ws.send(sub)

我希望它的工作。

雖然,我通過從import websocket庫中實現WebSocketApp class 來解決此問題,而不是像您的代碼那樣執行程序步驟。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM