webSocket长连接实现微信扫码登录

我们是要在网页端生成小程序码,用微信扫码,点击登录后。该二维码关闭跳转到指定界面.

那就简单说一下HTTP协议,WebSocket协议. (如果对于这些协议和网络传输过程比较模糊的话,请看下这篇文章 http://blog.csdn.net/gordohu/article/details/54097841 )

一、websocket与http

WebSocket是HTML5出的(协议)可以理解为HTTP的加强版.( 推荐文章: http://blog.csdn.net/frank_good/article/details/50856585 )

HTTP:链接分为短链接,长链接,短链接是每次请求都要三次握手才能发送自己的信息。即每一个request对应一个response。长链接是在一定的期限内保持链接。保持TCP连接不断开。客户端与服务器通信,必须要有客户端发起然后服务器返回结果。客户端是主动的,服务器是被动的。

WenSocket:建立了WenSocket之后服务器不必在浏览器发送request请求之后才能发送信息到浏览器。这时的服务器已有主动权想什么时候发就可以发送信息到服务器。而且信息当中不必再带有head的部分信息了,与http的长链接通信来说,这种方式,不仅能降低服务器的压力。而且信息当中也减少了部分多余的信息。

websocket特点:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

二、代码Demo

环境,我们是Grails 框架.需要通过 intellij idea 中的 Terminal 命令框输入命令:grails install-templates.会在项目目录下出现web.xml.( 使用Grails 不清楚的 推荐: http://blog.csdn.net/fengbaozonghuiguoqu/article/details/77770999 ) 引入
Servlet配置的基类.

package websocket;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
@WebServlet(urlPatterns = { "/zy" }) //注解简化
public class SocketService extends WebSocketServlet {
 
   private static final long serialVersionUID = 1L;
   public String getId(HttpServletRequest request){
      String webSocketId = request.getParameter("webSocketId");
      System.out.println("@@建立连接webSocketId="+webSocketId);
      return webSocketId;
   }
   
   @Override
   protected StreamInbound createWebSocketInbound(String arg0,
         HttpServletRequest request) {
      return new SocketServiceInbound(this.getId(request));
   }
 
}

socket操作类

package websocket;

import java.nio.CharBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class SocketServiceUtil {
   // 保存连接的MAP容器
   private static final Map connections = new HashMap();

   /**
    * 向连接池中添加连接
    * @param inbound
    */
   public static void addMessageInbound(SocketServiceInbound inbound) {
      System.out.println("id : " + inbound.getId() + " join..");
      connections.put(inbound.getId(), inbound);
   }

   /**
    * 移除连接池中的连接
    * @param id 用户名
    */
   public static void removeMessageInbound(String id) {
      System.out.println("id : " + id + " exit..");
      connections.remove(id);
   }

   /**
    * 获取所有的在线用户
    * @return
    */
   public static Set getOnlineId() {
      return connections.keySet();
   }

   /**
    * 向指定的用户发送消息
    * @param id 用户名
    * @param message 消息
    */
   public static void sendMessageToUser(String id, String message) {
      try {
         // 向特定的用户发送数据
         System.out.println("send message to id : " + id
               + " ,message content : " + message);
         SocketServiceInbound inbound = connections.get(id);
         if (inbound != null) {
            inbound.getWsOutbound().writeTextMessage(
                  CharBuffer.wrap(message));
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   /**
    * 向所有的用户发送消息
    * @param message 消息
    */
   public static void sendMessageToAll(String message) {
      try {
         Set keySet = connections.keySet();
         for (String key : keySet) {
            SocketServiceInbound inbound = connections.get(key);
            if (inbound != null) {
               System.out.println("send message to user : " + key
                     + " ,message content : " + message);
               inbound.getWsOutbound().writeTextMessage(
                     CharBuffer.wrap(message));
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

静态常量Map connections 用于保证每个id 对应一个连接.

链接消息绑定.继承MessageInbound 自定义String id ,与Util中参数数据结构一致.

package websocket;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.WsOutbound;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;

public class SocketServiceInbound extends MessageInbound {
   
   // 当前连接的用户名称
   private final String id;

   public SocketServiceInbound(String id) {
      this.id = id;
   }

   public String getId() {
      return this.id;
   }

   @Override
   protected void onOpen(WsOutbound outbound) {
      // TODO Auto-generated method stub
      //向连接池添加当前的连接对象
      SocketServiceUtil.addMessageInbound(this);
      //向当前连接发送当前在线用户的列表
      //SocketServiceUtil.sendMessageToUser(this.user, "uuuuuuuuuu");
   }

   @Override
   protected void onClose(int status) {
      // TODO Auto-generated method stub
      // 触发关闭事件,在连接池中移除连接
      SocketServiceUtil.removeMessageInbound(this.id);
   }

   @Override
   protected void onTextMessage(CharBuffer message) throws IOException {
      // TODO Auto-generated method stub
      SocketServiceUtil.sendMessageToUser(this.id, message.toString());
   }

   @Override
   protected void onBinaryMessage(ByteBuffer arg0) throws IOException {
      // TODO Auto-generated method stub
      throw new UnsupportedOperationException("Binary message not supported.");
   }
}

继承后的方法的重写 可直接在页面中调用.eg:如下

<%@ page contentType="text/html;charset=UTF-8" %>


    



WebSocket Demo

webSocket的四种状态:
CONNECTING:值为0,表示正在连接。
OPEN:值为1,表示连接成功,可以通信了。
CLOSING:值为2,表示连接正在关闭。
CLOSED:值为3,表示连接已经关闭,或者打开连接失败。

测试一下.(这里测试消息的发送,回调)
1:下面就启动项目初始化Servlet,访问页面,创建id为"hujunbao"的链接.

查看回调信息websocket.onopen 和 websocket.onerror反馈!
2:“链接成功” or “链接失败”
3:服务器高兴的时候就向客户端发送message:“testMessageToJunBao” or “testMessageToAll” …根据消息不同,做出相对应的业务!
4:业务完成后可在业务中关闭,或者添加监听事件,来控制链接状态.

你可能感兴趣的:(网络)