Spring+Websocket实现服务器与Andoird端通信
第一步:使用Maven(不会自行百度,这个很实用)自动更新添加依赖包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${srping.version}</version> </dependency>
另外还有配置Sping的相关包,不在此全部列出。
第二步:配置WebSocket的入口,编写WebSocketConfig类实现WebSocketConfigurer 接口
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(webSocketHandler(),"/websocket/socketServer.do").addInterceptors(new SpringWebSocketHandlerInterceptor()); registry.addHandler(webSocketHandler(), "/sockjs/socketServer.do").addInterceptors(new SpringWebSocketHandlerInterceptor()).withSockJS(); } @Bean public TextWebSocketHandler webSocketHandler(){ return new SpringWebSocketHandler(); } }
第三步:定义一个SpringWebSocketHandler类继承 文本流TextWebSocketHandler (二进制流为BinaryWebSocketHandler),这个类是用来处理Websocket连接建立、断开,消息发送的逻辑的,这个是消息处理的核心代码
public class SpringWebSocketHandler extends TextWebSocketHandler { private static final ArrayList<WebSocketSession> users;//这个会出现性能问题,最好用Map来存储,key用userid private static Logger logger = Logger.getLogger(SpringWebSocketHandler.class); static { users = new ArrayList<WebSocketSession>(); } public SpringWebSocketHandler() { // TODO Auto-generated constructor stub } /** * 连接成功时候,会触发页面上onopen方法 */ public void afterConnectionEstablished(WebSocketSession session) throws Exception { // TODO Auto-generated method stub System.out.println("connect to the websocket success......当前数量:"+users.size()); users.add(session); //这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户 // TextMessage returnMessage = new TextMessage("你将收到的离线"); // session.sendMessage(returnMessage); } /** * 关闭连接时触发 */ public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { logger.debug("websocket connection closed......"); String username= (String) session.getAttributes().get("WEBSOCKET_USERNAME"); System.out.println("用户"+username+"已退出!"); users.remove(session); System.out.println("剩余在线用户"+users.size()); } /** * js调用websocket.send时候(客户端发送消息之后),会调用该方法 */ @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { super.handleTextMessage(session, message); logger.debug("message:"+message.getPayload().toString()); TextMessage returnMessage = new TextMessage(message.getPayload().toString()); //session.sendMessage(returnMessage); //sendMessageToUser("123",returnMessage); sendMessageToUsers(returnMessage); } public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { if(session.isOpen()){session.close();} logger.debug("websocket connection closed......"); users.remove(session); } public boolean supportsPartialMessages() { return false; } /** * 给某个用户发送消息 * * @param userName * @param message */ public void sendMessageToUser(String userName, TextMessage message) { for (WebSocketSession user : users) { if (user.getAttributes().get("WEBSOCKET_USERNAME").equals(userName)) { try { if (user.isOpen()) { user.sendMessage(message); } } catch (IOException e) { e.printStackTrace(); } break; } } } /** * 给所有在线用户发送消息 * * @param message */ public void sendMessageToUsers(TextMessage message) { for (WebSocketSession user : users) { try { if (user.isOpen()) { user.sendMessage(message); } } catch (IOException e) { e.printStackTrace(); } } } }
第四步:拦截器配置
从WebSocketConfig中可以看到在注册WebSocket通道时,不仅设置了入口地址,还配置了拦截器,拦截器可以实现握手之前和之后的逻辑操作,这里配置的拦截器主要用于保存用户名以便于在Handler中定向发送消息。
public class SpringWebSocketHandlerInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { // TODO Auto-generated method stub System.out.println("Before Handshake"); if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request; HttpSession session = servletRequest.getServletRequest().getSession(false); if (session != null) { //使用userName区分WebSocketHandler,以便定向发送消息 String userName = (String) session.getAttribute("SESSION_USERNAME"); if (userName==null) { userName="default-system"; } attributes.put("WEBSOCKET_USERNAME",userName); } } return super.beforeHandshake(request, response, wsHandler, attributes); } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { super.afterHandshake(request, response, wsHandler, ex); } }
注:选配(非必要) 传输数据大小
@Configuration @EnableWebSocket //配置WebSocket的文本流/二进制流的大小 public class WebSocketConfig2 implements ServletContextInitializer { @Override public void onStartup(ServletContext servletContext) throws ServletException { servletContext.addListener(WebAppRootListener.class); //servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize","52428800"); servletContext.setInitParameter("org.apache.tomcat.websocket.binaryBufferSize","25165824"); } }
第五步:配置Websocket连接前台页面(只写后端的后面就不用看了)
网页端连接Websocket和推送消息的界面就是这里
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> </head> <body> <script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.1.0/jquery.min.js"></script> <script type="text/javascript" src="http://cdn.bootcss.com/sockjs-client/1.1.1/sockjs.js"></script> <script type="text/javascript"> var msgDiv = document.getElementById("#msgDiv"); var websocket = null; if ('WebSocket' in window) { websocket = new WebSocket("ws://localhost:8080/bank_pm/websocket/socketServer.do"); } else if ('MozWebSocket' in window) { websocket = new MozWebSocket("ws://localhost:8080/bank_pm/websocket/socketServer.do"); } else { websocket = new SockJS("http://localhost:8080/bank_pm/sockjs/socketServer.do"); } websocket.onopen = onOpen; websocket.onmessage = onMessage; websocket.onerror = onError; websocket.onclose = onClose; function onOpen(openEvt) { //alert(openEvt.Data+"onOpen"); } function onMessage(evt) { alert(evt.data+"onMessage"); } function onError() { alert("出错"+"onError"); } function onClose() { alert("关闭"+"onClose"); } function doSend() { if (websocket.readyState == websocket.OPEN) { var msg = document.getElementById("inputMsg").value; websocket.send(msg); //调用后台handleTextMessage方法 alert("发送成功!"); } else { alert("连接失败!"); } } window.close=function(){ websocket.onclose(); } </script> <body align="center"> <h3>消息推送</h3> 请输入:<textarea rows="8" cols="50" id="inputMsg" name="inputMsg"></textarea> <button onclick="doSend();">发送</button> <hr/> <textarea rows="10" cols="70" id="msgDiv"></textarea> </body> </html>
第六步:登录界面与登录的Controller配置
因为我们发送消息是需要发送给登录用户的,所以在这里补充登录的相关代码:
(1)登录页面,地址可自己换成自己的:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <body align="center"> <h2>银行绩效管理系统</h2> <form action="${pageContext.request.contextPath}/login.action" method="post"> 登录名:<input type="text" name="username"/><br/> 密码 : <input type="text" name="password"/><br/> <input type="submit" id="login_btn_id" value="登录"/> </form> </body> </html>
(2)登录的Controller编写:主要是在登录是用HttpSession保存了用户名,为拦截器获取用户名做了准备。
@ResponseBody
@RequestMapping(value="/login.action",method={RequestMethod.POST,RequestMethod.GET})
public User checkUserAndPassword(
@RequestParam(value="username",required=true) String username,
@RequestParam(value="password",required=true) String password,User user,HttpServletRequest request) throws Exception{
User u = new User();
//登录成功
if((u = userService.checkUsernameAndPassword(user)) != null){
HttpSession session = request.getSession(true);
session.setAttribute("SESSION_USERNAME", user.getUsername());
return u;
};
//登录失败,返回Null
return null;
}
另:web.xml中的servlet和filter中添加异步支持
<async-supported>true</async-supported>
Android端
现在,服务器端通过前台界面websocket.jsp可以发送和接收消息了,那么Android端是如何接收和发送消息的呢?同样,Android端的处理方法也是用到了WebSocket。
第一步:Android项目中导入JavaWebSocket_fat.jar包,放到项目的libs目录下,记得添加文件依赖哦。
编写ManActivity测试代码
实现思路:在导好包后,在MainActivity中使用WebSocketClient这个类来进行Websocket网络通信,详细解释见代码。点击andorid界面上的TextView实现网络连接,在网页上推送消息,界面上就会以Toast形式弹出发送过来的消息。
MainActivity:
public class MainActivity extends AppCompatActivity { private WebSocketClient mWebSocketClient; //注意更改成为你的服务器地址,格式:"ws://ip:port/项目名字/websocket入口地址 private String address = "ws://192.168.1.115:8080/bank_pm/websocket/socketServer.do"; private TextView tv; private Handler handler = new Handler(){ public void handleMessage(Message msg){ if(msg.what == 0x111){ String news = ""; news = msg.getData().getString("news"); Toast.makeText(MainActivity.this, "收到消息:"+news, Toast.LENGTH_SHORT).show(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { initSocketClient(); mWebSocketClient.connect(); } catch (URISyntaxException e) { e.printStackTrace(); } } }); } private void initSocketClient() throws URISyntaxException { if(mWebSocketClient == null) { mWebSocketClient = new WebSocketClient(new URI(address)) { @Override public void onOpen(ServerHandshake serverHandshake) { //连接成功 Log.i("LOG","opened connection"); } ‘ @Override public void onMessage(String s) { //服务端消息来了 Log.i("LOG","received:" + s); Message msg = Message.obtain(); msg.what = 0x111; Bundle bundle = new Bundle(); bundle.putString("news",s); msg.setData(bundle); handler.sendMessage(msg); } @Override public void onClose(int i, String s, boolean remote) { //连接断开,remote判定是客户端断开还是服务端断开 Log.i("LOG","Connection closed by " + ( remote ? "remote peer" : "us" ) + ", info=" + s); // closeConnect(); } @Override public void onError(Exception e) { Log.i("LOG","error:" + e); } }; } } //断开连接 private void closeConnect() { try { mWebSocketClient.close(); } catch(Exception e) { e.printStackTrace(); } finally { mWebSocketClient = null; } } //向服务器发送消息的方法 private void sendMsg(String msg) { mWebSocketClient.send(msg); } }
注意:websocket的网络连接自己已经是用了线程的,所以不用我们再去写线程操作了。android项目记得添加网络权限。
转载链接https://blog.csdn.net/csdn_blog_lcl/article/details/73277960 感谢