• java实现websocket 终极指南


    大概思路:  首先用户登陆  获取用户信息存储到httpsession中,然后客户端链接服务端websocket,首先HandshakeInterceptor这个拦截器会拦截请求 调用 beforeHandshake方法进行握手操作,然后吧httpsession 和 websocketsession进行绑定 , 然后执行MySocketHandler 类得afterConnectionEstablished方法 ,创建一个静态map  吧所有连接得客户端存到map里 以便用户退出登陆时清空session。 客户端发来消息会调用 handleMessage  

    1、pom中添加依赖

    <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-websocket</artifactId>
                <version>${spring-version}</version>
            </dependency>
            
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-messaging</artifactId>
                <version>${spring-version}</version>
            </dependency>
    

     2、spring-mvc.xml 中添加websocket拦截

     <websocket:handlers allowed-origins="*">
            <websocket:mapping path="/ws" handler="myHandler"/>
            <websocket:handshake-interceptors>
                <bean class="com.cloudunicomm.interceptor.HandshakeInterceptor"/>
            </websocket:handshake-interceptors>
        </websocket:handlers>
    
        <bean id="myHandler" class="com.cloudunicomm.interceptor.MySocketHandler"/>
    

     3、添加MySocketHandler类

    package com.cloudunicomm.interceptor;
    
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Set;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.ListOperations;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.web.socket.CloseStatus;
    import org.springframework.web.socket.TextMessage;
    import org.springframework.web.socket.WebSocketMessage;
    import org.springframework.web.socket.WebSocketSession;
    import org.springframework.web.socket.handler.TextWebSocketHandler;
    
    import com.alibaba.fastjson.JSONObject;
    import com.cloudunicomm.vo.User;
       
    public class MySocketHandler extends TextWebSocketHandler {  
       
    	private static final Logger logger = LoggerFactory.getLogger(MySocketHandler.class);
        // 线上人数  
        private static int count;  
        private static CopyOnWriteArraySet<WebSocketSession> set = new CopyOnWriteArraySet<>(); 
        public static Map<String,WebSocketSession> sessionid = new HashMap<String,WebSocketSession>();
        private WebSocketSession session;  
        
        @Autowired
    	private RedisTemplate<String,String> redisTemplate;
        
        
        @Override  
        public void afterConnectionEstablished(WebSocketSession session) {  
        	Map<String, Object> map = session.getAttributes();
        	String userid = map.get("userid").toString();
        	sessionid.put(userid, session);
           this.session = session;  
           try{  
               set.add(this.session);  
           }catch(Exception e) {  
               e.printStackTrace();  
           }  
           MySocketHandler.addOnlineCount();  
           System.out.println("目前连接人数:" + getOnlineCount());  
        }  
    
        public void afterConnectionClosed(WebSocketSession session,CloseStatus closeStatus) {  
           this.session = session;
           String userid = session.getAttributes().get("userid").toString();
           redisTemplate.delete("SEAT_"+userid);
           session.getAttributes().remove("userid");
           set.remove(this.session);  
           subOnlineCount();  
           System.out.println("目前连接人数:" + getOnlineCount());  
        }  
         
        public void handleMessage(WebSocketSession session,WebSocketMessage<?>message){  
           System.out.println("来自客户端消息: "+message.getPayload()+ "_"+ session.getId() ); 
           //发送给所有人
          /* for(WebSocketSession ssion : set) {  
               try {  
                  ssion.sendMessage(message); 
               }catch(IOException e) {  
                  e.printStackTrace();  
               }  
           } */
           //解析message 修改用户状态
           try{
        	   //id:state,
        	   //username:sip:$tU@123.57.144.26:9060
        	
    	       String[] state =   message.getPayload().toString().split("_");
    	       ListOperations<String,String> value = redisTemplate.opsForList();
    	       String val = value.rightPop("SEAT_"+state[0]).toString();
    	       User user = (User)JSONObject.toJavaObject(JSONObject.parseObject(val), User.class);
    	       user.setState(Integer.parseInt(state[1]));
    	       //实际是解析出来得 现在先写死
    	       user.setNext_hop("123.57.144.26:9060/udp");
    	       user.setTo("<sip:$tU@123.57.144.26:9060>");
    	       String struser = JSONObject.toJSONString(user);
    	       value.leftPush("SEAT_"+user.getId().toString(), struser);
           }catch(Exception e){
        	   logger.error(e.getMessage(),e);
           }
        }  
         
        public static int getOnlineCount() {  
           return count;  
        }  
       
        public static void addOnlineCount() {  
           count++;  
        }  
       
        public static void subOnlineCount() {  
           count--;  
        } 
        /** 
         * 给指定连接推消息 
         * @param session 
         * @param message 
         */  
        public String pushMsg(String sessionid, String message){  
            for(WebSocketSession ssion : set) {  
               try {  
                   if(sessionid.equals(ssion.getId())){  
                      ssion.sendMessage(new TextMessage(message));  
                      return "机器:" + sessionid+ "推送成功";  
                   }  
               }catch(IOException e) {  
                   e.printStackTrace();  
               }  
            }  
            return "推送失败";  
        }  
           
        /** 
         * 给全部连接 
         * @param message 
         * @return 
         */  
        public String pushMsg(String message) {  
            int i = 0;  
            for(WebSocketSession ssion : set) {  
               try {  
                   ssion.sendMessage(new TextMessage(message));  
                   i++;  
               }catch(IOException e) {  
                   e.printStackTrace();  
               }  
            }  
            return "共有" + i + "得到推送";  
           }
    }  
    

      4、添加 HandshakeInterceptor 类

     

    package com.cloudunicomm.interceptor;
    
    import java.util.Map;
    
    import javax.servlet.http.HttpSession;
    
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.http.server.ServletServerHttpRequest;
    import org.springframework.web.socket.WebSocketHandler;
    import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
       
    public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor {  
    	  
        /* 
         * 握手前处理动作 
         */  
        @Override  
        public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,  
               Map<String,Object> map)throws Exception {  
           System.out.println("握手前");  
           
          if(request instanceof ServletServerHttpRequest){
        	   ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
               HttpSession httpSession = servletRequest.getServletRequest().getSession(true);
              if(null != httpSession){
            	// String userid =  httpSession.getAttribute("userid").toString();
            	 String userid = httpSession.getAttribute("userid").toString();
                 map.put("userid",userid);
              }
           }
           return super.beforeHandshake(request, response, handler, map);  
        }  
         
        @Override  
        public void afterHandshake(ServerHttpRequest request,ServerHttpResponse response,WebSocketHandler wsHandler,Exception ex) {  
           super.afterHandshake(request, response, wsHandler, ex);  
        }  
     
    }  
    

      5、添加TestController

    package com.cloudunicomm.controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller  
    @RequestMapping("/im")  
    public class TestController {  
        
    	/*@Bean  
        public MySorketHandle mySorketHandle() {  
           return new MySorketHandle();  
        }  */
    	
        @RequestMapping("/page")  
        public String page(HttpServletRequest request, HttpServletResponse response) { 
        
           return "IMpage";  
        }  
        
        /*@ResponseBody  
        @RequestMapping("/push")  
        public String push(@RequestParam(required = false) String sessionId,  
               HttpServletResponse response){  
           String msg= "";  
           if (StringUtils.isEmpty(sessionId)) {  
               msg =mySorketHandle().pushMsg("服务器推送信息了");  
               System.out.println(msg);  
           }else{  
               msg =mySorketHandle().pushMsg(sessionId, "服务器推送信息了");  
               System.out.println(msg);  
           }  
           return msg;  
        }  */
    } 
    

     6、添加 jsp 

    <%@ 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 charset="UTF-8">  
           <title>socket</title>  
           <script type="text/javascript" src="http://cdn.static.runoob.com/libs/jquery/2.1.1/jquery.min.js"></script>  
        </head>  
        <body>  
           welcome<br />  
           <input id="text" type="text"/>  
           <button onclick="sendMsg()">sendMsg</button>  
           <hr/>  
           <button onclick="closeWebSocket()">close WebSocketconnection</button>  
           <hr/>  
           <div id="message"></div>  
        </body>  
         
        <script type="text/javascript">  
        
           var websocket = null;  
           //判断浏览器是否支持websocket  
           if('WebSocket' in window) {  
               websocket = new WebSocket("ws://localhost:8081/preSend/websocket");  
           }else{  
               $("#message").html("该浏览器不支持实时通信功能");  
           }  
           window.onbeforeunload = function () {
        	    alert("x帆帆帆帆");
        	   websocket.close();
        	 }
           websocket.onopen= function() {  
               console.log("websocket连接成功");  
           }  
            
           websocket.onclose= function() {  
        	   closewebsocket();
               console.log("websocket连接关闭");  
           }  
            
           websocket.onmessage= function(event) {  
               console.log("接收消息");  
               console.log(event);  
               printMsg(event.data);  
           }  
            
           //打印消息  
           function printMsg(msg) {  
               $("#message").append(msg+ "<br/>");  
           }  
            
           function sendMsg() {  
               var msg = $("#text").val();  
               websocket.send("3_0");
           }  
            
           function closeWebSocket(){  
               websocket.close();  
           } 
           //离线
           function closewebsocket(){
        	   alert("用户离线了");
        	   
           }
           
        </script>  
    </html>  
    

     7、登陆controller

    package com.cloudunicomm.controller;
    
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.ListOperations;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.alibaba.fastjson.JSONObject;
    import com.cloudunicomm.service.UserService;
    import com.cloudunicomm.utils.ResultMessage;
    import com.cloudunicomm.vo.User;
    
    @Controller
    public class LoginController {
    	
    	@Autowired
    	private UserService userService;
    	@Autowired
    	private RedisTemplate<String,String> redisTemplate;
    	
    	@CrossOrigin(origins = "*",maxAge = 3000)
    	@RequestMapping("login")
    	@ResponseBody
    	public ResultMessage login(HttpServletRequest request,HttpServletResponse response,
    			@RequestParam(name="username",required=false)String username,
    			@RequestParam(name="password",required=false)String password){
    		if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
    			return ResultMessage.getFail().setMessage("用户名密码不能为空!");
    		}
    		User user = userService.AuthUserNmaAndPassword(username,password);
    		if(user == null){
    			return ResultMessage.getFail().setMessage("用户名密码错误!");
    		}
    		String str = redisTemplate.opsForList().rightPopAndLeftPush("SEAT_"+user.getId(), "SEAT_"+user.getId());
    		if(null != str){
    			return ResultMessage.getFail().setMessage("请勿重复登陆!");
    		}
    		//登陆成功 添加 用户到redis中 islogin修改为在线    state修改为闲
    		user.setIslogin(0);
    		user.setState(0);
    		ListOperations<String,String> redislist = redisTemplate.opsForList();
    		String struser = JSONObject.toJSONString(user);
    		//从左向右存压栈
    		redislist.leftPush("SEAT_"+user.getId().toString(), struser);
    		request.getSession().setAttribute("userid", user.getId());
    		return ResultMessage.getSuccess().setData(user.getId());
    	}
    	
    	
    }
    

      

     

    8、注意事项

      1)项目中又拦截器要注释掉 否则websocket会链接失败抛出 'Upgrade' header is missing 异常

      2)如果是远程调用测试时地址必须是同一个

  • 相关阅读:
    浅析Vue CompositionAPI和React Hooks对比:hook的意义、两者差别(原理链表/Proxy、代码执行每次渲染都执行/组件创建时运行、声明响应式状态、如何跟踪依赖、生命周期、自定义hook、Ref获取元素、计算属性附加函数、Context和provide/inject、在渲染上下文中暴露值)
    算法设计和数据结构学习_1(一道堆排序作业题)
    总结系列_14(OpenCV2.4.3的新特征以及安装方法)
    Kinect+OpenNI学习笔记之8(Robert Walter手部提取代码的分析)
    基础学习笔记之opencv(21):一个简单有趣的皮肤检测代码
    基础学习笔记之opencv(16):grabcut使用例程
    Eigen初步1:初步体验Eigen库
    基础学习笔记之opencv(19):有关图像序列的直方图计算
    基础学习笔记之opencv(17):皮肤检测类CvAdaptiveSkinDetector的使用
    ChaLearn Gesture Challenge_4:one shot learning比赛结果简单分析
  • 原文地址:https://www.cnblogs.com/xdcr/p/9234118.html
Copyright © 2020-2023  润新知