1.服务端的实现,我尝试了两种方式:
第一种是用“@ServerEndPoint”注解来实现,实现简单; 第二种稍显麻烦,但是可以添加拦截器在WebSocket连接建立和断开前进行一些额外操作。不管用哪种实现方式,都需要先导入jar包(如下),其中version根据实际springboot版本选择,避免冲突
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <!--<version>1.3.5.RELEASE</version>--> </dependency>1.1第一种实现方法
(1)WebSocket业务逻辑实现。参数传递采用路径参数的方法,通过以下方式获取参数:
@ServerEndpoint("/testWebSocket/{id}/{name}")
publicvoidonOpen(Sessionsession,@PathParam("id")longid,@PathParam("name")Stringname)
import java.util.concurrent.CopyOnWriteArraySet;
importjavax.websocket.OnClose; importjavax.websocket.OnError; importjavax.websocket.OnMessage; importjavax.websocket.OnOpen; importjavax.websocket.Session; importjavax.websocket.server.ServerEndpoint;
importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.web.bind.annotation.RestController;
@ServerEndpoint("/testWebSocket/{id}/{name}") @RestController publicclassTestWebSocket{
//用来记录当前连接数的变量 privatestaticvolatileintonlineCount=0; //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象 privatestaticCopyOnWriteArraySet<TestWebSocket>webSocketSet=newCopyOnWriteArraySet<TestWebSocket>(); //与某个客户端的连接会话,需要通过它来与客户端进行数据收发 privateSessionsession; privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(TestWebSocket.class);@OnOpen publicvoidonOpen(Sessionsession,@PathParam("id")longid,@PathParam("name")Stringname)throwsException{ this.session=session; System.out.println(this.session.getId()); webSocketSet.add(this); LOGGER.info("Openawebsocket.id={},name={}",id,name); }
@OnClose publicvoidonClose(){ webSocketSet.remove(this); LOGGER.info("Closeawebsocket."); } @OnMessage publicvoidonMessage(Stringmessage,Sessionsession){ LOGGER.info("Receiveamessagefromclient:"+message); } @OnError publicvoidonError(Sessionsession,Throwableerror){ LOGGER.error("Errorwhilewebsocket.",error); } publicvoidsendMessage(Stringmessage)throwsException{ if(this.session.isOpen()){ this.session.getBasicRemote().sendText("Sendamessagefromserver."); } } publicstaticsynchronizedintgetOnlineCount(){ returnonlineCount; } publicstaticsynchronizedvoidaddOnlineCount(){ TestWebSocket.onlineCount++; } publicstaticsynchronizedvoidsubOnlineCount(){ TestWebSocket.onlineCount--; }}
(2)配置ServerEndpointExporter,配置后会自动注册所有“@ServerEndpoint”注解声明的WebsocketEndpoint
importorg.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; importorg.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration publicclassWebSocketConfig{ @Bean publicServerEndpointExporterserverEndpointExporter(){ returnnewServerEndpointExporter(); } }1.2第二种实现方法
(1)WebSocket业务逻辑实现。参数传递采用类似GET请求的方式传递,服务端的参数在拦截器中获取之后通过attributes传递给WebSocketHandler。
importjava.util.ArrayList; importjava.util.concurrent.atomic.AtomicInteger; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.web.bind.annotation.RestController; importorg.springframework.web.socket.CloseStatus; importorg.springframework.web.socket.WebSocketHandler; importorg.springframework.web.socket.WebSocketMessage; importorg.springframework.web.socket.WebSocketSession; @RestController publicclassTestWebSocketControllerimplementsWebSocketHandler{ privatestaticAtomicIntegeronlineCount=newAtomicInteger(0); privatestaticfinalArrayList<WebSocketSession>sessions=newArrayList<>(); privatefinalLoggerLOGGER=LoggerFactory.getLogger(TestWebSocketController.class); @Override publicvoidafterConnectionEstablished(WebSocketSessionsession)throwsException{ sessions.add(session); intonlineNum=addOnlineCount(); LOGGER.info("OprnaWebSocket.Currentconnectionnumber:"+onlineNum); } @Override publicvoidafterConnectionClosed(WebSocketSessionsession,CloseStatusstatus)throwsException{ sessions.remove(session); intonlineNum=subOnlineCount(); LOGGER.info("CloseawebSocket.Currentconnectionnumber:"+onlineNum); } @Override publicvoidhandleMessage(WebSocketSessionwsSession,WebSocketMessage<?>message)throwsException{ LOGGER.info("Receiveamessagefromclient:"+message.toString()); } @Override publicvoidhandleTransportError(WebSocketSessionsession,Throwableexception)throwsException{ LOGGER.error("ExceptionoccursonwebSocketconnection.disconnecting...."); if(session.isOpen()){ session.close(); } sessions.remove(session); subOnlineCount(); } /* *是否支持消息拆分发送:如果接收的数据量比较大,最好打开(true),否则可能会导致接收失败。 *如果出现WebSocket连接接收一次数据后就自动断开,应检查是否是这里的问题。 */ @Override publicbooleansupportsPartialMessages(){ returntrue; } publicstaticintgetOnlineCount(){ returnonlineCount.get(); } publicstaticintaddOnlineCount(){ returnonlineCount.incrementAndGet(); } publicstaticintsubOnlineCount(){ returnonlineCount.decrementAndGet(); } }(2)HandShake拦截器实现
importjava.util.Map; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.context.ApplicationContext; importorg.springframework.http.server.ServerHttpRequest; importorg.springframework.http.server.ServerHttpResponse; importorg.springframework.http.server.ServletServerHttpRequest; importorg.springframework.web.socket.WebSocketHandler; importorg.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; publicclassTestHandShakeInterceptorextendsHttpSessionHandshakeInterceptor{ privatefinalLoggerLOGGER=LoggerFactory.getLogger(TestHandShakeInterceptor.class); /* *在WebSocket连接建立之前的操作,以鉴权为例 */ @Override publicbooleanbeforeHandshake(ServerHttpRequestrequest,ServerHttpResponseresponse, WebSocketHandlerwsHandler,Map<String,Object>attributes)throwsException{ LOGGER.info("HandlebeforewebSocketconnected."); //获取url传递的参数,通过attributes在Interceptor处理结束后传递给WebSocketHandler //WebSocketHandler可以通过WebSocketSession的getAttributes()方法获取参数 ServletServerHttpRequestserverRequest=(ServletServerHttpRequest)request; Stringid=serverRequest.getServletRequest().getParameter("id"); Stringname=serverRequest.getServletRequest().getParameter("name"); if(tokenValidation.validateSign()){ LOGGER.info("Validationpassed.WebSocketconnecting...."); attributes.put("id",id); attributes.put("name",name); returnsuper.beforeHandshake(request,response,wsHandler,attributes); }else{ LOGGER.error("Validationfailed.WebSocketwillnotconnect."); returnfalse; } } @Override publicvoidafterHandshake(ServerHttpRequestrequest,ServerHttpResponseresponse, WebSocketHandlerwsHandler,Exceptionex){ //省略 } }(3)WebSocket配置类实现(注册WebSocket实现类,绑定接口,同时将实现类和拦截器绑定)
importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.context.annotation.Configuration; importorg.springframework.web.servlet.config.annotation.EnableWebMvc; importorg.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; importorg.springframework.web.socket.config.annotation.EnableWebSocket; importorg.springframework.web.socket.config.annotation.WebSocketConfigurer; importorg.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; importTestWebSocketController; importTestHandShakeInterceptor; @Configuration @EnableWebMvc @EnableWebSocket publicclassWebSocketConfigextendsWebMvcConfigurerAdapterimplementsWebSocketConfigurer{ @Autowired privateTestWebSocketControllertestWebSocketController; @Override publicvoidregisterWebSocketHandlers(WebSocketHandlerRegistryregistry){ registry.addHandler(TestWebSocketController,"/testWebSocket") .addInterceptors(newTestHandShakeInterceptor()).setAllowedOrigins("*"); } }1.3补充说明
(1)在WebSocket实现过程中,尤其是通过“@ServerEndpoint”实现的时候,可能会出现注入失败的问题,即注入的Bean为null的问题。可以通过手动注入的方式来解决,需要改造实现类和SpringBoot启动类,如下:
@ServerEndpoint("testWebsocket") @RestController publicclassWebSocketController{ privateTestServicetestService; privatestaticApplicationContextapplicationContext; @OnOpen publicvoidonOpen(Sessionsession){ testService=applicationContext.getBean(TestService.class); } @OnClose publicvoidonClose(){} @OnMessage publicvoidonMessage(Stringmessage,Sessionsession){} @OnError publicvoidonError(Sessionsession,Throwableerror){} publicstaticvoidsetApplicationContext(ApplicationContextapplicationContext){ WebSocketController.applicationContext=applicationContext; } } importorg.springframework.boot.SpringApplication; importorg.springframework.boot.autoconfigure.SpringBootApplication; importorg.springframework.context.ConfigurableApplicationContext; importWebSocketController; @SpringBootApplication publicclassApplication{ publicstaticvoidmain(String[]args){ //SpringApplication.run(Application.class,args); SpringApplicationspringApplication=newSpringApplication(Application.class); ConfigurableApplicationContextconfigurableApplicationContext=springApplication.run(args); WebSocketController.setApplicationContext(configurableApplicationContext);//解决WebSocket不能注入的问题 } } 客户端的实现,我尝试了html和javaWebSocketClient两种方式2.1html实现
<!DOCTYPEhtml> <html> <head> <title>WebSocket示例</title> <metacontent=width=device-width,initial-scale=1,maximum-scale=1,user-scalable=noname=viewport/> <metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/> </head> <body> <inputid="text"type="text"/> <buttononclick="send()">发送消息</button> <hr/> <buttononclick="closeWebSocket()">关闭WebSocket连接</button> <hr/> <divid="message"></div> </body> <scripttype="text/javascript"> varwebsocket=null; //判断当前浏览器是否支持WebSocket if(WebSocketinwindow){ //不带参数的写法 websocket=newWebSocket("ws://127.0.0.1:18080/testWebsocket"); //通过路径传递参数的方法(服务端采用第一种方法"@ServerEndpoint"实现) websocket=newWebSocket("ws://127.0.0.1:18080/testWebsocket/23/Lebron"); //通过类似GET请求方式传递参数的方法(服务端采用第二种方法"WebSocketHandler"实现) websocket=newWebSocket("ws://127.0.0.1:18080/testWebsocket?id=23&name=Lebron"); } else{ alert(当前浏览器Notsupportwebsocket) } //连接发生错误的回调方法 websocket.onerror=function(){ setMessageInnerHTML("WebSocket连接发生错误"); }; //连接成功建立的回调方法 websocket.onopen=function(){ setMessageInnerHTML("WebSocket连接成功"); } //接收到消息的回调方法 websocket.onmessage=function(event){ setMessageInnerHTML(event.data); } //连接关闭的回调方法 websocket.onclose=function(){ setMessageInnerHTML("WebSocket连接关闭"); } //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload=function(){ closeWebSocket(); } //将消息显示在网页上 functionsetMessageInnerHTML(innerHTML){ document.getElementById(message).innerHTML+=innerHTML+<br/>; } //关闭WebSocket连接 functioncloseWebSocket(){ websocket.close(); } //发送消息 functionsend(){ varmessage=document.getElementById(text).value; websocket.send(message); } </script> </html>2.2JavaWebSocketClient实现
(1)WebSocketClient实现类
importjava.net.URI; importorg.java_websocket.client.WebSocketClient; importorg.java_websocket.drafts.Draft; importorg.java_websocket.handshake.ServerHandshake; importorg.slf4j.Logger; importorg.slf4j.LoggerFactory; publicclassTestWebSocketClientextendsWebSocketClient{ privatefinalLoggerLOGGER=LoggerFactory.getLogger(TestWebSocketClient.class); publicTestWebSocketClient(URIserverUri){ super(serverUri); } publicTestWebSocketClient(URIserverUri,DraftprotocolDraft){ super(serverUri,protocolDraft); } @Override publicvoidonOpen(ServerHandshakeserverHandshake){ LOGGER.info("OpenaWebSocketconnectiononclient."); } @Override publicvoidonClose(intarg0,Stringarg1,booleanarg2){ LOGGER.info("CloseaWebSocketconnectiononclient."); } @Override publicvoidonMessage(Stringmsg){ LOGGER.info("WebSocketClientreceivesamessage:"+msg); } @Override publicvoidonError(Exceptionexception){ LOGGER.error("WebSocketClientexception.",exception); } }(2)WebSocketClient发送数据
StringserverUrl="ws://127.0.0.1:18080/testWebsocket" URIrecognizeUri=newURI(serverUrl); client=newTestWebSocketClient(recognizeUri,newDraft_6455()); client.connect(); client.send("Thisisamessagefromclient.");本文内容总结:
原文链接:https://www.cnblogs.com/strugglion/p/10021173.html