介绍

在使用websocket来制作多人即时聊天工具的时候,难免会遇到一个问题,如何区分不同的客户端。想要解决这个问题就等于是要解决这样一个问题:如何把当前登录用户的userId传给服务端呢?因为不同的客户端代表着不同的用户,做到了获取不同客户端的userId那么自然就把不同的客户端区分开来了。经过查找资料和试验,我找到了两种可行获取客户端userId的方法。一种是通过在Server取HttpSession中的值获取当前用户,一种是直接在客户端建立连接时附带上用户的值。

开发环境和工具

MyEclipse,Tomcat8.0

WebSocket

获取HttpSession值

当我们在完成用户登录的功能时,用户登录成功,则将当前用户放入HttpSession中,这是一种很常见的做法,这一部分代码如下(框架是SpringMVC,不详细介绍,具体代码请以自己所用框架为准):

if(Objects.equals(userDetail.getUserDetailPassword(), userPassword)){

//如果当前用户登录成功,则将user对象放入httpSession的currentUser

httpSession.setAttribute("currentUser",user);

resoult = "success";

}

那么接下来问题的关键就来了,我们怎么在Server中获取在这里放入HttpSession中的User对象呢,直接获取肯定是不行的,不卖关子,直接放代码。

注意,结构如图:

新建一个GetHttpSessionConfigurator类,内容如下:

import javax.servlet.http.HttpSession;

import javax.websocket.HandshakeResponse;

import javax.websocket.server.HandshakeRequest;

import javax.websocket.server.ServerEndpointConfig;

import javax.websocket.server.ServerEndpointConfig.Configurator;

public class GetHttpSessionConfigurator extends Configurator{

@Override

public void modifyHandshake(ServerEndpointConfig sec,HandshakeRequest request, HandshakeResponse response) {

HttpSession httpSession=(HttpSession) request.getHttpSession();

sec.getUserProperties().put(HttpSession.class.getName(),httpSession);

}

}

然后在Server里面注解的地方加上一句:

@ServerEndpoint(value="/server/",configurator=GetHttpSessionConfigurator.class)

此时,我们就已经可以用

@OnOpen

public void onOpen(Session session, EndpointConfig config){

HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());

}

来获取httpSession对象了,然后直接取出currentUser存储的用户对象就可以了。

但是,在这里我产生了一个问题:原则上来讲我在server获取的httpsession中取出来的用户对象就是现在正和服务端建立连接的对象,因为这种情况的操作肯定是先登录,然后直接建立连接,可是在实际中多用户同时登录时就不一定是这样子了。因为登录是客户端发起的操作,建立连接也是客户端发起的操作,且不说在客户端这两个操作是否是紧密相连,就算是紧密相连,从服务器验证成功(此时已经放入currentUser对象)返回登录结果给客户端到客户端向服务端发起连接这中间因为网络原因也是会消耗一定时间的。那么这时候一件尴尬的事情就发生了:此时,另一个用户也在登录,并且在之前用户两个操作期间完成了登录验证操作,那么第一个用户连接建立之后取出的use对象就不是这个用户的而是第二个用户的,这就乱套了。这种方法相当于是 ,用户A先对服务器说,记住了,我叫A,然后过了一会儿来说,我要建立连接,我是刚刚告诉你名字那个人。那如果B在A离开那会儿也告诉了服务器我叫B,那么服务器就会把A当成B了。

当前,上面我所说的我没办法去验证,如果我对HttpSession理解错误那就另当别论了。(应该没理解错吧)如果理解有误还请大神指正。

所以,感觉上来讲还是第二种方法靠谱一点。

@PathParam获取用户对象

这种方法是在建立连接时把userId放在建立连接的申请之中,这样的话就不会乱掉了:因为用户A登录成功之后我就把用户A的user对象传回去了,然后用户A拿着自己的userId来对客户端说我要建立连接我是A,服务端自然不会搞错。实现方法如下:

服务端注解地方如下:

@ServerEndpoint(value="/server/{userId}")

方法参数如下:

@OnOpen

public void onOpen(@PathParam("userId")String userId,Session session)

服务端在建立连接请求时路径如下(cp是jsp中的:)

set var="cp" value="${pageContext.request.contextPath}" />

currentUser就是我们之前登录成功时放入httpSession的值,这个值即便别的用户登录也不会被刷新因为它是被保存在自己的浏览器之中的。

ws = "ws://localhost:8080" + "${cp}" + "/server"+"/${currentUser.userId}";

这样的话,服务端就获取到了当前建立连接的用户了。

区分不同客户端

能够获取不同用户userId之后,我们就可以在服务端进行如下操作来区分用户了,具体见注释。(为了突出要点,代码做了精简,仅仅用于示范区分不同的用户)

public class Server {

//存放每个客户端对应的Server对象,可以考虑使用Map来代替,key作为用户标识

private static CopyOnWriteArraySetserver = new CopyOnWriteArraySet();

//表示与某个用户的连接会话,通过它给客户端发送数据

@SuppressWarnings("unused")

private Session session;

//用户id

private String userId;

//用户id和websocket的session绑定的路由表

@SuppressWarnings("rawtypes")

private static Map routeTable = new HashMap<>();

/**

* 连接建立成功调用的方法

*@param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据

*/

@SuppressWarnings("unchecked")

@OnOpen

public void onOpen(@PathParam("userId")String userIds,Session session){

this.session = session;

//获取当前登录用户的id

this.userId=userIds;

//将用户id和session绑定到路由表

//绑定之后就可以在其它地方根据id来获取session,这时两个用户私聊就可以实现了

routeTable.put(userId, session);

}

//其它部分代码就不放了

大概就是这样子了,这是我查找了很多资料自己也做过试验的结果,希望能够帮助到大家。

好好学习,天天向上,加油!

代码

现在回头看一看,发现博客讲的不是很清楚,实在是抱歉了,此处把我项目的源码放上来,希望对各位有所帮助。

Github:https://github.com/IcedSoul/WithMe

项目本身比较大,包含1.0和2.0版本,它们使用的WebScoket版本不一样,1.0使用的是JavaEE7.0带的WebSocket(JSR356),只支持Web端聊天,2.0使用的是Github上的一个开源项目Java-WebScoket,同时支持Web端和Android端。查看代码的时候注意留意一下你所使用的WebSocket版本,然后去查看对应的代码。

websocket如何区分用户_WebSocket区分不同客户端两种方法(HttpSession和@PathParam)相关推荐

  1. WebSocket区分不同客户端两种方法(HttpSession和@PathParam)

    介绍 在使用websocket来制作多人即时聊天工具的时候,难免会遇到一个问题,如何区分不同的客户端.想要解决这个问题就等于是要解决这样一个问题:如何把当前登录用户的userId传给服务端呢?因为不同 ...

  2. java模拟网易邮箱登录_使用服务端和客户端两种方法 模拟网易邮箱实现全选,全不选的功能...

    服务端和客户端的差别是 服务端在每次全选或全不选是都要刷新界面 而客户端不会 服务端: 前台 DataKeyNames="id" DataSourceID="SqlDat ...

  3. C++ 区分中文,非中文,截取含有中文的string字符串的两种方法

    C++ 区分中文,非中文,截取含有中文的string字符串的两种方法 方法一 根据中文在ASCII中的范围判断 方法二 把string转成wstring 转自: http://blog.51cto.c ...

  4. JS区分中英文字符的两种方法

    JS区分中英文字符的两种方法: 正则和charCodeAt()方法. 正则无疑是最强大的判断各种条件的方法, 最近也在研习它, 虽然枯燥, 但仍有乐趣. 用它来判断一个双字节的中文字符也是轻而易举地. ...

  5. 转:实例学习PHP程序对用户身份认证实现两种方法

    用户在设计和维护站点的时候,经常需要限制对某些重要文件或信息的访问.通常,我们可以采用内置于WEB服务器的基于HTTP协议的用户身份验证机制. 当访问者浏览受保护页面时,客户端浏览器会弹出对话窗口要求 ...

  6. 进Linux系统单用户模式,Linux进入单用户模式的两种方法

    单用户模式的作用 在使用Linux系统中,维护人员经常会碰到一个问题,就是在拥有root账号权限和密码的用户中,总是会出现忘记root密码的情况. 遇到这种情况,一般情况下,维护人员就会通过最常用的方 ...

  7. GridView 实现服务器端和客户端全选的两种方法

    代码很简单,这里就不累述了.看代码如下: C# GridView 实现服务器端和客户端全选的两种方法 全选 转载于:https://www.cnblogs.com/rocky99261/archive ...

  8. C#代码像QQ的右下角消息框一样,无论现在用户的焦点在哪个窗口,消息框弹出后都不影响焦点的变化,那么有两种方法...

    你QQ的右下角消息框一样,无论现在用户的焦点在哪个窗口,消息框弹出后都不影响焦点的变化,那么有两种方法: 要么重写需要弹出的窗体的事件: protected override CreateParams ...

  9. mysql数据库运行远程用户访问不了_MySQL数据库远程访问权限如何打开(两种方法)...

    下载GPL版本安装 MySQL Community Edition(GPL) Community (GPL) Downloads » 在我们使用mysql数据库时,有时我们的程序与数据库不在同一机器上 ...

最新文章

  1. R语言使用caretEnsemble包的caretList函数一次性构建多个机器学习模型、使用lattice包的bwplot函数使用箱图对比多个模型在多个指标上的性能差异
  2. 马斯克脑机接口遭质疑:不是新技术,没体现神经解码进展
  3. tushare 金融数据获取(R语言版)
  4. python utf 8_python写utf-8文件的问题
  5. Hadoop 单机与完全分布式配置
  6. 如何编写高效android代码
  7. 腾讯云CentOS 7 上安装Nginx
  8. 库克谈iPhone 12供应紧张问题;2020中国互联网百强名单:阿里、腾讯、美团分列前三;Dgraph新版发布|极客头条
  9. 使用ORB_SLAM2的方式进行特征检测和提取
  10. 怎样在Linux中用Vim对文件进行密码保护
  11. 再读simpledb 之 SQL语句解析(1)
  12. 字典树-大量字符串前缀及出现次数是否存在统计(Trie树-java)算法实现
  13. 华为NP课程笔记2-OSPF2
  14. 实现虚拟机VMware上Centos操作系统与主机windows之间互相复制与粘贴
  15. ORA-01033错误解决方法
  16. 老师用计算机教我们画画拼音,小学一年级语文《汉语拼音13angengingong》第三课时教学设计.docx...
  17. Learning Agile software Development
  18. 为什么局部下降最快的方向就是梯度的负方向?
  19. c语言五子棋人工智能算法,五子棋人工智能算法实现研究,优化五子棋智能算法的思路...
  20. 红米k30s至尊纪念版参数配置

热门文章

  1. 类似蚂蚁森林html5游戏源码,js仿照 蚂蚁森林 效果
  2. 为什么偏偏是 1024 ?
  3. IPC_PRIVATE
  4. 2019年,离开京东后的跳槽之旅(历史回顾和感悟)
  5. 观测天体物理学 第一章 天体信息
  6. 华为S5720交换机配置stelnet和sftp远程服务
  7. linux 安装mysql8
  8. 公交车的自动报站名是如何实现的?
  9. 关于郭德纲徒弟打人事件的一点看法
  10. Android 使用 vivo手机调试无法安装apk