在这篇文章中,简单介绍了WebSocket的使用,客户端可以使用了JavaScript向服务发送请求,服务端也可以通过SimpMessagingTemplate 主动向客户端推送消息,实现服务器端与客户端的双向交互。
下面介绍客户端如何连接远程WebSocket Server并推送消息,参照gs-messaging-stomp-websocket中的TestCase,我们简单修改下代码
@Slf4j
public class StompClientTest {
public static void main(String[] args) throws Exception {
int port = 8898;
SockJsClient sockJsClient;
WebSocketStompClient stompClient;
final WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
StompSession stompSession = null;
{
List transports = new ArrayList<>();
transports.add(new WebSocketTransport(new StandardWebSocketClient()));
sockJsClient = new SockJsClient(transports);
stompClient = new WebSocketStompClient(sockJsClient);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
}
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference failure = new AtomicReference<>();
TestSessionHandler handler = new TestSessionHandler(failure) {
private StompSession stompSession;
@Override
public String getSomething() {
return "something in main";
}
@Override
public void afterConnected(final StompSession session, StompHeaders connectedHeaders) {
session.subscribe("/topic/greetings", new StompFrameHandler() {
@Override
public Type getPayloadType(StompHeaders headers) {
return Greeting.class;
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {
Greeting greeting = (Greeting) payload;
try {
log.info("{}", greeting.getContent());
} catch (Throwable t) {
failure.set(t);
} finally {
session.disconnect();
latch.countDown();
}
}
});
try {
session.send("/app/hello", new HelloMessage("message forwarded from '/app/hello'"));
session.send("/topic/greetings", new Greeting("message directly from '/topic/greetings'"));
this.stompSession = session;
} catch (Throwable t) {
failure.set(t);
latch.countDown();
}
}
public StompSession getStompSession() {
return stompSession;
}
};
stompClient.connect("ws://localhost:{port}/gs-guide-websocket", headers, handler, port);
log.info("something is {}", handler.getSomething());
while (!stompClient.isRunning()) {
if ((stompSession = handler.getStompSession()) != null) {
break;
}
}
stompSession.send("/app/hello", new HelloMessage("message2 forwarded from '/app/hello'"));
stompSession.send("/topic/greetings", new Greeting("message2 directly from '/topic/greetings'"));
stompSession.send("/app/hello", new HelloMessage("message3 forwarded from '/app/hello'"));
stompSession.send("/topic/greetings", new Greeting("message3 directly from '/topic/greetings'"));
if (latch.await(30, TimeUnit.SECONDS)) {
if (failure.get() != null) {
throw new AssertionError("", failure.get());
}
} else {
fail("Greeting not received!");
}
}
}
代码修改部分
this.stompSession = session;
获取StompSession
while (!stompClient.isRunning()) {
if ((stompSession = handler.getStompSession()) != null) {
break;
}
}
WebSocket连接之后,我们将StompSession返回,之后便可以通过send方法向远程WebSocket Server发送消息。
"C:\Program Files\Java\jdk1.8.0_91\bin\java" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2017.2.2\lib\idea_rt.jar=63920:C:\Program Files\JetBrains\IntelliJ IDEA 2017.2.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_91\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_91\jre\lib\rt.jar;D:\work\wanda\IntelliJ\web-socket\complete\target\test-classes;D:\work\wanda\IntelliJ\web-socket\complete\target\classes;D:\work\mavensettings\wanda\repository\org\springframework\boot\spring-boot-starter-websocket\1.5.7.RELEASE\spring-boot-starter-websocket-1.5.7.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\boot\spring-boot-starter\1.5.7.RELEASE\spring-boot-starter-1.5.7.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\boot\spring-boot\1.5.7.RELEASE\spring-boot-1.5.7.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\boot\spring-boot-autoconfigure\1.5.7.RELEASE\spring-boot-autoconfigure-1.5.7.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\boot\spring-boot-starter-logging\1.5.7.RELEASE\spring-boot-starter-logging-1.5.7.RELEASE.jar;D:\work\mavensettings\wanda\repository\ch\qos\logback\logback-classic\1.1.11\logback-classic-1.1.11.jar;D:\work\mavensettings\wanda\repository\ch\qos\logback\logback-core\1.1.11\logback-core-1.1.11.jar;D:\work\mavensettings\wanda\repository\org\slf4j\jcl-over-slf4j\1.7.25\jcl-over-slf4j-1.7.25.jar;D:\work\mavensettings\wanda\repository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;D:\work\mavensettings\wanda\repository\org\slf4j\log4j-over-slf4j\1.7.25\log4j-over-slf4j-1.7.25.jar;D:\work\mavensettings\wanda\repository\org\yaml\snakeyaml\1.17\snakeyaml-1.17.jar;D:\work\mavensettings\wanda\repository\org\springframework\boot\spring-boot-starter-web\1.5.7.RELEASE\spring-boot-starter-web-1.5.7.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\boot\spring-boot-starter-tomcat\1.5.7.RELEASE\spring-boot-starter-tomcat-1.5.7.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.20\tomcat-embed-core-8.5.20.jar;D:\work\mavensettings\wanda\repository\org\apache\tomcat\embed\tomcat-embed-el\8.5.20\tomcat-embed-el-8.5.20.jar;D:\work\mavensettings\wanda\repository\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.20\tomcat-embed-websocket-8.5.20.jar;D:\work\mavensettings\wanda\repository\org\hibernate\hibernate-validator\5.3.5.Final\hibernate-validator-5.3.5.Final.jar;D:\work\mavensettings\wanda\repository\javax\validation\validation-api\1.1.0.Final\validation-api-1.1.0.Final.jar;D:\work\mavensettings\wanda\repository\org\jboss\logging\jboss-logging\3.3.1.Final\jboss-logging-3.3.1.Final.jar;D:\work\mavensettings\wanda\repository\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;D:\work\mavensettings\wanda\repository\org\springframework\spring-web\4.3.11.RELEASE\spring-web-4.3.11.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\spring-aop\4.3.11.RELEASE\spring-aop-4.3.11.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\spring-webmvc\4.3.11.RELEASE\spring-webmvc-4.3.11.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\spring-expression\4.3.11.RELEASE\spring-expression-4.3.11.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\spring-messaging\4.3.11.RELEASE\spring-messaging-4.3.11.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\spring-beans\4.3.11.RELEASE\spring-beans-4.3.11.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\spring-context\4.3.11.RELEASE\spring-context-4.3.11.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\spring-websocket\4.3.11.RELEASE\spring-websocket-4.3.11.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\webjars\webjars-locator\0.32-1\webjars-locator-0.32-1.jar;D:\work\mavensettings\wanda\repository\org\webjars\webjars-locator-core\0.32\webjars-locator-core-0.32.jar;D:\work\mavensettings\wanda\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;D:\work\mavensettings\wanda\repository\org\apache\commons\commons-lang3\3.1\commons-lang3-3.1.jar;D:\work\mavensettings\wanda\repository\org\apache\commons\commons-compress\1.9\commons-compress-1.9.jar;D:\work\mavensettings\wanda\repository\com\fasterxml\jackson\core\jackson-core\2.8.10\jackson-core-2.8.10.jar;D:\work\mavensettings\wanda\repository\com\fasterxml\jackson\core\jackson-databind\2.8.10\jackson-databind-2.8.10.jar;D:\work\mavensettings\wanda\repository\com\fasterxml\jackson\core\jackson-annotations\2.8.0\jackson-annotations-2.8.0.jar;D:\work\mavensettings\wanda\repository\org\webjars\sockjs-client\1.0.2\sockjs-client-1.0.2.jar;D:\work\mavensettings\wanda\repository\org\webjars\stomp-websocket\2.3.3\stomp-websocket-2.3.3.jar;D:\work\mavensettings\wanda\repository\org\webjars\bootstrap\3.3.7\bootstrap-3.3.7.jar;D:\work\mavensettings\wanda\repository\org\webjars\jquery\3.1.0\jquery-3.1.0.jar;D:\work\mavensettings\wanda\repository\org\springframework\boot\spring-boot-starter-test\1.5.7.RELEASE\spring-boot-starter-test-1.5.7.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\boot\spring-boot-test\1.5.7.RELEASE\spring-boot-test-1.5.7.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\boot\spring-boot-test-autoconfigure\1.5.7.RELEASE\spring-boot-test-autoconfigure-1.5.7.RELEASE.jar;D:\work\mavensettings\wanda\repository\com\jayway\jsonpath\json-path\2.2.0\json-path-2.2.0.jar;D:\work\mavensettings\wanda\repository\net\minidev\json-smart\2.2.1\json-smart-2.2.1.jar;D:\work\mavensettings\wanda\repository\net\minidev\accessors-smart\1.1\accessors-smart-1.1.jar;D:\work\mavensettings\wanda\repository\org\ow2\asm\asm\5.0.3\asm-5.0.3.jar;D:\work\mavensettings\wanda\repository\junit\junit\4.12\junit-4.12.jar;D:\work\mavensettings\wanda\repository\org\assertj\assertj-core\2.6.0\assertj-core-2.6.0.jar;D:\work\mavensettings\wanda\repository\org\mockito\mockito-core\1.10.19\mockito-core-1.10.19.jar;D:\work\mavensettings\wanda\repository\org\objenesis\objenesis\2.1\objenesis-2.1.jar;D:\work\mavensettings\wanda\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\work\mavensettings\wanda\repository\org\hamcrest\hamcrest-library\1.3\hamcrest-library-1.3.jar;D:\work\mavensettings\wanda\repository\org\skyscreamer\jsonassert\1.4.0\jsonassert-1.4.0.jar;D:\work\mavensettings\wanda\repository\com\vaadin\external\google\android-json\0.0.20131108.vaadin1\android-json-0.0.20131108.vaadin1.jar;D:\work\mavensettings\wanda\repository\org\springframework\spring-core\4.3.11.RELEASE\spring-core-4.3.11.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\springframework\spring-test\4.3.11.RELEASE\spring-test-4.3.11.RELEASE.jar;D:\work\mavensettings\wanda\repository\org\eclipse\jetty\websocket\websocket-client\9.4.6.v20170531\websocket-client-9.4.6.v20170531.jar;D:\work\mavensettings\wanda\repository\org\eclipse\jetty\jetty-util\9.4.6.v20170531\jetty-util-9.4.6.v20170531.jar;D:\work\mavensettings\wanda\repository\org\eclipse\jetty\jetty-io\9.4.6.v20170531\jetty-io-9.4.6.v20170531.jar;D:\work\mavensettings\wanda\repository\org\eclipse\jetty\jetty-client\9.4.6.v20170531\jetty-client-9.4.6.v20170531.jar;D:\work\mavensettings\wanda\repository\org\eclipse\jetty\jetty-http\9.4.6.v20170531\jetty-http-9.4.6.v20170531.jar;D:\work\mavensettings\wanda\repository\org\eclipse\jetty\websocket\websocket-common\9.4.6.v20170531\websocket-common-9.4.6.v20170531.jar;D:\work\mavensettings\wanda\repository\org\eclipse\jetty\websocket\websocket-api\9.4.6.v20170531\websocket-api-9.4.6.v20170531.jar;D:\work\mavensettings\wanda\repository\org\projectlombok\lombok\1.16.16\lombok-1.16.16.jar" hello.StompClientTest
20:27:11.778 [main] DEBUG org.springframework.web.socket.sockjs.client.RestTemplateXhrTransport - Executing SockJS Info request, url=http://localhost:8898/gs-guide-websocket/info
20:27:11.825 [main] DEBUG org.springframework.web.client.RestTemplate - Created GET request for "http://localhost:8898/gs-guide-websocket/info"
20:27:11.856 [main] DEBUG org.springframework.web.client.RestTemplate - GET request for "http://localhost:8898/gs-guide-websocket/info" resulted in 200 (null)
20:27:11.872 [main] DEBUG org.springframework.web.socket.sockjs.client.WebSocketTransport - Starting WebSocket session on ws://localhost:8898/gs-guide-websocket/46/94ec7870e1e548b2bdb1a48780895e77/websocket
20:27:11.872 [main] DEBUG org.springframework.web.socket.client.standard.StandardWebSocketClient - Connecting to ws://localhost:8898/gs-guide-websocket/46/94ec7870e1e548b2bdb1a48780895e77/websocket
20:27:11.919 [main] INFO hello.StompClientTest - something is something in main
20:27:12.075 [SimpleAsyncTaskExecutor-1] DEBUG org.springframework.web.socket.sockjs.client.WebSocketClientSockJsSession - Processing SockJS open frame in WebSocketClientSockJsSession[id='94ec7870e1e548b2bdb1a48780895e77, url=ws://localhost:8898/gs-guide-websocket]
20:27:12.075 [SimpleAsyncTaskExecutor-1] DEBUG org.springframework.messaging.simp.stomp.DefaultStompSession - Connection established in session id=750c50a6-97cf-d7a3-d245-1a693cb7a6f3
20:27:12.263 [WebSocketClient-AsyncIO-2] INFO hello.StompClientTest - message directly from '/topic/greetings'
20:27:12.271 [WebSocketClient-AsyncIO-2] DEBUG org.springframework.web.socket.sockjs.client.WebSocketClientSockJsSession - Closing session with CloseStatus[code=1000, reason=null] in WebSocketClientSockJsSession[id='94ec7870e1e548b2bdb1a48780895e77, url=ws://localhost:8898/gs-guide-websocket]
20:27:12.271 [WebSocketClient-AsyncIO-2] DEBUG org.springframework.web.socket.adapter.NativeWebSocketSession - Closing StandardWebSocketSession[id=0, uri=null]
20:27:12.271 [WebSocketClient-AsyncIO-2] DEBUG org.springframework.web.socket.sockjs.client.WebSocketClientSockJsSession - Transport closed with CloseStatus[code=1000, reason=null] in WebSocketClientSockJsSession[id='94ec7870e1e548b2bdb1a48780895e77, url=ws://localhost:8898/gs-guide-websocket]
20:27:12.271 [WebSocketClient-AsyncIO-2] DEBUG org.springframework.messaging.simp.stomp.DefaultStompSession - Connection closed in session id=750c50a6-97cf-d7a3-d245-1a693cb7a6f3
20:27:12.271 [WebSocketClient-AsyncIO-2] ERROR org.springframework.web.socket.sockjs.client.WebSocketClientSockJsSession - Ignoring received message due to state CLOSED in WebSocketClientSockJsSession[id='94ec7870e1e548b2bdb1a48780895e77, url=ws://localhost:8898/gs-guide-websocket]
20:27:12.271 [WebSocketClient-AsyncIO-2] ERROR org.springframework.web.socket.sockjs.client.WebSocketClientSockJsSession - Ignoring received message due to state CLOSED in WebSocketClientSockJsSession[id='94ec7870e1e548b2bdb1a48780895e77, url=ws://localhost:8898/gs-guide-websocket]
20:27:12.271 [WebSocketClient-AsyncIO-2] ERROR org.springframework.web.socket.sockjs.client.WebSocketClientSockJsSession - Ignoring received message due to state CLOSED in WebSocketClientSockJsSession[id='94ec7870e1e548b2bdb1a48780895e77, url=ws://localhost:8898/gs-guide-websocket]
20:27:12.271 [WebSocketClient-AsyncIO-2] DEBUG org.springframework.web.socket.sockjs.client.WebSocketClientSockJsSession - Processing SockJS close frame with CloseStatus[code=1002, reason=null] in WebSocketClientSockJsSession[id='94ec7870e1e548b2bdb1a48780895e77, url=ws://localhost:8898/gs-guide-websocket]
20:27:12.271 [WebSocketClient-AsyncIO-2] DEBUG org.springframework.web.socket.sockjs.client.WebSocketClientSockJsSession - Ignoring close (already closing or closed): current state CLOSED
Process finished with exit code 0
页面显示收到的实际信息
将gs-messaging-stomp-websocket部署到服务器上,将connect连接地址换为服务器地址同样可以。
stompClient.connect("ws://localhost:{port}/gs-guide-websocket", headers, handler, port);
不同语言的WebSocket Client
http://stomp.github.io/implementations.html