微信小程序使用websocket通讯的demo,含前后端代码,亲测可用


0、概述websocket

(1) 个人总结:后台设置了websocket地址,服务器开启后等待有人去连接它。 一个客户端一打开就去连接websocket地址,同时传递某些识别参数。这样一来后台和客户端连接成功了,然后后台就可以发消息给客户端了,(客户端也可以再回话给后台)。
(2) socket叫套接字,应用程序用socket向网络发出请求或者应答网络请求。
(3) 官方解释的socket 建立连接四步骤:

服务器端开启socket,然后accep方法处于监听状态,等待客户端的连接。
客户端开启,指定服务器名称和端口号来请求连接服务器端的socket。
服务器端收到客户端连接请求,返回连接确认。在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。
客户端收到连接确认,两个人就连接好了,双方开始通讯

(4)注意:
客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。
TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.

1、app.js写法

在app.js下添加三个函数openSocket(), closeSocket(),sendMessage(),在app初始化的onLunch函数里面调用openSocket(),这样子用户一进入小程序就会自动连接websocket

App({
      globalData: {
        socketStatus: 'closed',
      },
    onLaunch: function() {   
        var that = this;
        if (that.globalData.socketStatus === 'closed') {
              that.openSocket();
        }
    }
    openSocket() {
       //打开时的动作
        wx.onSocketOpen(() => {
          console.log('WebSocket 已连接')
          this.globalData.socketStatus = 'connected';
          this.sendMessage();
        })
        //断开时的动作
        wx.onSocketClose(() => {
          console.log('WebSocket 已断开')
          this.globalData.socketStatus = 'closed'
        })
        //报错时的动作
        wx.onSocketError(error => {
          console.error('socket error:', error)
        })
        // 监听服务器推送的消息
        wx.onSocketMessage(message => {
          //把JSONStr转为JSON
          message = message.data.replace(" ", "");
          if (typeof message != 'object') {
            message = message.replace(/\ufeff/g, ""); //重点
            var jj = JSON.parse(message);
            message = jj;
          }
          console.log("【websocket监听到消息】内容如下:");
          console.log(message);
        })
        // 打开信道
        wx.connectSocket({
          url: "ws://" + "localhost" + ":8888",
        })
      },
        
    //关闭信道
      closeSocket() {
        if (this.globalData.socketStatus === 'connected') {
          wx.closeSocket({
            success: () => {
              this.globalData.socketStatus = 'closed'
            }
          })
        }
      },
        
     //发送消息函数
      sendMessage() {
        if (this.globalData.socketStatus === 'connected') {
        //自定义的发给后台识别的参数 ,我这里发送的是name
          wx.sendSocketMessage({
            data: "{\"name\":\"" + wx.getStorageSync('openid') + "\"}"  
          })
        }
      },
})

2、后台写法

主要有两个类,一个是websocket启动监听交互类,一个是存储当前所有已经连接好的用户池以及对这些用户的操作封装类。
然后在项目启动类里面调用websocke启动监听交互类的启动方法。(如果是springboot项目,就直接在主类中调用)
(1)导入包

<dependency>
    <groupId>org.java-websocket</groupId>
    <artifactId>Java-WebSocket</artifactId>
    <version>1.3.0</version>
</dependency>            

(2)启动websocket的方法,放在启动类里面

     /**
     * 启动websocket
     */
    public void startWebsocketInstantMsg() {
        WebSocketImpl.DEBUG = false;
        MyWebScoket s;
        s = new MyWebScoket(8888);
        s.start();
        System.out.println("websocket启动成功");
    }

(3)websocket监听交互类如下
该类涉及的监听方法有:监听用户连入;监听用户断开;监听消息发过来;监听有错误等

import com.alibaba.fastjson.JSONObject;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Map;

public class MyWebScoket extends WebSocketServer {
	
	public MyWebScoket() throws UnknownHostException {
		super();
	}
	
	public MyWebScoket(int port)  {
        super(new InetSocketAddress(port));
    }

    public MyWebScoket(InetSocketAddress address) {
        super(address);
    }

    @Override
    public void onOpen(WebSocket conn, ClientHandshake handshake) {
        // ws连接的时候触发的代码,onOpen中我们不做任何操作
    }

    @Override
    public void onClose(WebSocket conn, int code, String reason, boolean remote) {
        //断开连接时候触发代码
        userLeave(conn);
        System.out.println(reason);
    }

    @Override
    public void onMessage(WebSocket conn, String message) {
        //有用户连接进来
    	Map<String, String> obj =  (Map<String,String>) JSONObject.parse(message);
        System.out.println(message);
       	String username = obj.get("name");
       	userJoin(conn,  username);
    }

    @Override
    public void onError(WebSocket conn, Exception ex) {
        //错误时候触发的代码
        System.out.println("on error");
        ex.printStackTrace();
    }

    /**
     * 去除掉失效的websocket链接
     */
    private void userLeave(WebSocket conn){
        WsPool.removeUser(conn);
    }
    /**
     * 将websocket加入用户池
     * @param conn
     * @param userName
     */
    private void userJoin(WebSocket conn,String userName){
        WsPool.addUser(userName, conn);
    }
}

(4)用户池类如下
该类包含的方法有:从池中移除或添加用户;获取当前在线的所有用户;通过参数"name"获取某个用户的当前WebSocket连接;给某个WebSocket连接发送消息;为所有WebSocket连接发送消息等等

import com.td.yousan.util.StringUtils;
import org.java_websocket.WebSocket;
import java.util.*;

public class WsPool {
    private static final Map<WebSocket, String> wsUserMap = new HashMap<WebSocket, String>();

    /**
     * 通过websocket连接获取其对应的用户
     */
    public static String getUserByWs(WebSocket conn) {
        return wsUserMap.get(conn);
    }

    /**
     * 根据userName获取WebSocket,这是一个list,此处取第一个
     * 因为有可能多个websocket对应一个userName(但一般是只有一个,因为在close方法中,我们将失效的websocket连接去除了)
     */
    public static WebSocket getWsByUser(String userName) {
        Set<WebSocket> keySet = wsUserMap.keySet();
        synchronized (keySet) {
            for (WebSocket conn : keySet) {
                String cuser = wsUserMap.get(conn);
                if (cuser.equals(userName)) {
                    return conn;
                }
            }
        }
        return null;
    }

    /**
     * 向连接池中添加连接
     */
    public static void addUser(String userName, WebSocket conn) {
        wsUserMap.put(conn, userName); // 添加连接
    }

    /**
     * 获取所有连接池中的用户,因为set是不允许重复的,所以可以得到无重复的user数组
     */
    public static Collection<String> getOnlineUser() {
        List<String> setUsers = new ArrayList<String>();
        Collection<String> setUser = wsUserMap.values();
        for (String u : setUser) {
            setUsers.add(u);
        }
        return setUsers;
    }

    /**
     * 移除连接池中的连接
     */
    public static boolean removeUser(WebSocket conn) {
        if (wsUserMap.containsKey(conn)) {
            wsUserMap.remove(conn); // 移除连接
            return true;
        } else {
            return false;
        }
    }

    /**
     * 向特定的用户发送数据
     */
    public static void sendMessageToUser(WebSocket conn, String message) {
        if (null != conn && null != wsUserMap.get(conn)) {
            conn.send(message);
        }
    }

    /**
     * 向所有用户名中包含某个特征得用户发送消息
     */
    public static void sendMessageToSpecialUser(String message,String special) {
    	Set<WebSocket> keySet = wsUserMap.keySet();
    	if (special == null) {
    		special = "";
    	}
    	synchronized (keySet) {
    		for (WebSocket conn:keySet) {
    			String user = wsUserMap.get(conn);
    			try {
	    			if (user != null) {
	    				String [] cus = user.split("_");
	    				if (!StringUtils.isNullOrEmpty(cus[0])) {
	    					String cusDot = "," + cus[0] + ",";
	    					if (cusDot.contains(","+special+",")) {
	    							conn.send(message);
	    					}
	    				}else {
	    					conn.send(message);
	    				}
	    			}
    			}catch (Exception e) {
    				e.printStackTrace();
    				//wsUserMap.remove(conn);
    			}
    		}
    			
		}
    }
    /**
     * 向所有的用户发送消息
     */
    public static void sendMessageToAll(String message) {
        Set<WebSocket> keySet = wsUserMap.keySet();
        synchronized (keySet) {
            for (WebSocket conn : keySet) {
                String user = wsUserMap.get(conn);
                if (user != null) {
                    conn.send(message);
                }
            }
        }
    }
    

}

posted on 2019-05-22 14:54  华哒妹妹  阅读(16668)  评论(7编辑  收藏  举报