Клиент Python не получает сообщения от сервера веб-сокетов Spring - PullRequest
2 голосов
/ 17 октября 2019

Цель проекта состоит в том, чтобы иметь сервер JVM (Kotlin) и клиент Python, взаимодействующие друг с другом через веб-сокет. Сервер должен отправлять обновления клиенту, и клиент будет их обрабатывать.

Сервер является приложением Spring Boot, на котором запущен сервер веб-сокетов Spring Boot. На данный момент клиент является всего лишь скриптом Python, на котором запущен websocket-клиент для подключения к серверу.

Что работает:

  • Элемент списка
  • клиент может подключиться к серверу
  • клиент может подключиться к теме на сервере (используя STOMP)
  • клиент может отправить сообщение на сервер, и сервер получает иобрабатывает это сообщение
  • сервер может отправить сообщение на веб-сокет

Что не работает:

  • сообщение отсервер не получен клиентом

Я попробовал принимающую часть клиента, подключившись к эхо-серверу websocket.org (ws: //echo.websocket.org) иклиент получает отраженные сообщения от сервера. Таким образом, мне кажется, что проблема не на стороне клиента.


Время кода.

Сервер Kotlin: Код для создания сервера веб-сокетов:

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

Консольный вывод

Теперь консольный вывод сервера выглядит следующим образом. Видно, что когда клиент не подключен, нет подписчиков для отправки запланированного сообщения. После успешного подключения клиента и отправки запланированного сообщения 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

Вывод клиента такой: он просто ждет до конца света, чтобы получить сообщение:

--- 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 секунды). Интервал этих обновлений может варьироваться от пары минут до полсекунды. Клиент и сервер - это две разные машины в одной сети, но эта сеть ограничена по пропускной способности.


Итак, кто может определить, что идет не так?

Я прочиталХорошая часть этого документа о веб-сокетах, но я не вижу, что происходит не так: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket

Этот ответ также дал мне понять, но сам пример не работаетдля начала (после исправления while not True в while True).

Обновление 18 октября 2019: Я искал клиент Python SocketJS, потому что я получил серверную часть Kotlin, работающую сSocketJS и клиент JavaScript. Но я не могу найти какие-либо реализации клиента Python SocketJS. Интересно, остается ли единственное решение - запустить сервер веб-сокетов в приложении Python (на стороне клиента) и позволить клиенту отправлять данные своего сервера веб-сокетов на сервер Kotlin, который затемподключится к серверу веб-сокета клиента. Это не симпатичное решение, но мне интересно, будет ли оно работать. Я буду держать вас в курсе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...