发表时间:2022-03-23来源:网络
WebSocket是一种在单个TCP连接上进行全双工通信的协议,允许服务端主动向客户端推送数据,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
通过一次tcp长连接,经过一次三次握手,持续的双向传输数据。
特点:实时双向通讯
协议与http类似,ws对应http,wss对应https协议
注意事项:
1、ServerEndpoint注解声明的类是多例,每多一个客户端连接,就会创建一个类
2、onMessage 处理客户端传递消息
sendMessage 传递消息给客户端
``
package com.neil.demo.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import io.micrometer.core.instrument.util.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; /** *@Description: *@Author:lilong */ @ServerEndpoint("/websocket/{userId}") @Component public class WebSocketServer { static Loglog= LogFactory.getLog(WebSocketServer.class); private static intonlineCount= 0; /** * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 */ private static ConcurrentHashMapwebSocketMap= new ConcurrentHashMap(); /** *与某个客户端的连接会话,需要通过它来给客户端发送数据 */ private Session session; /** *接收userId */ private String userId = ""; @OnOpen public void onOpen(Session session, @PathParam("userId") String userId) { this.session = session; this.userId = userId; if (webSocketMap.containsKey(userId)) { webSocketMap.remove(userId); webSocketMap.put(userId, this); //加入set中 } else { webSocketMap.put(userId, this); //加入set中 addOnlineCount(); //在线数加1 } log.info("用户连接:" + userId + ",当前在线人数为:" +getOnlineCount()); try { sendInfo("连接成功",userId); } catch (IOException e) { log.error("用户:" + userId + ",网络异常!!!!!!"); } } /** *连接关闭调用的方法 */ @OnClose public void onClose() { if (webSocketMap.containsKey(userId)) { webSocketMap.remove(userId); //从set中删除 subOnlineCount(); } log.info("用户退出:" + userId + ",当前在线人数为:" +getOnlineCount()); } /** *收到客户端消息后调用的方法 * *@parammessage客户端发送过来的消息 */ @OnMessage public void onMessage(String message, Session session) { log.info("用户消息:" + userId + ",报文:" + message); //可以群发消息 //消息保存到数据库、redis if (StringUtils.isNotBlank(message)) { try { //解析发送的报文 JSONObject jsonObject = JSON.parseObject(message); //追加发送人(防止串改) jsonObject.put("fromUserId", this.userId); String toUserId = jsonObject.getString("toUserId"); //传送给对应toUserId用户的websocket if (StringUtils.isNotBlank(toUserId) &&webSocketMap.containsKey(toUserId)) { webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString()); } else { log.error("请求的userId:" + toUserId + "不在该服务器上"); //否则不在这个服务器上,发送到mysql或者redis } } catch (Exception e) { e.printStackTrace(); } } } /** *@paramsession *@paramerror */ @OnError public void onError(Session session, Throwable error) { log.error("用户错误:" + this.userId + ",原因:" + error.getMessage()); error.printStackTrace(); } /** *实现服务器广播推送 */ public void sendMessage(String message) throws IOException { System.out.println("send"); for (WebSocketServer value :webSocketMap.values()) { value.session.getBasicRemote().sendText(message); } } /** *发送自定义消息 */ public static void sendInfo(String message, String userId) throws IOException { log.info("发送消息到:" + userId + ",报文:" + message); if (StringUtils.isNotBlank(userId) &&webSocketMap.containsKey(userId)) { webSocketMap.get(userId).sendMessage(message); } else { log.error("用户" + userId + ",不在线!"); } } public static synchronized int getOnlineCount() { returnonlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; } }``
public class WebsocketUtils { /** *获取客户端连接实例 *@paramuri *@return */ public WebSocketClient getClient(String uri){ try { //创建客户端连接对象 WebSocketClient client = new WebSocketClient(new URI(uri),new Draft_6455()) { /** *建立连接调用 *@paramserverHandshake */ @Override public void onOpen(ServerHandshake serverHandshake) { log.info("建立连接"); } /** *收到服务端消息调用 *@params */ @Override public void onMessage(String s) { log.info("收到来自服务端的消息:::" + s); } /** *断开连接调用 *@parami *@params *@paramb */ @Override public void onClose(int i, String s, boolean b) { log.info("关闭连接:::" + "i = " + i + ":::s = " + s +":::b = " + b); } /** *连接报错调用 *@parame */ @Override public void onError(Exception e) { log.error("报错了:::" + e.getMessage()); } }; //请求与服务端建立连接 client.connect(); //判断连接状态,0为请求中 1为已建立 其它值都是建立失败 while(client.getReadyState().ordinal() == 0){ try { Thread.sleep(200); }catch (Exception e){ log.warn("延迟操作出现问题,但并不影响功能"); } log.info("连接中。。。"); } //连接状态不再是0请求中,判断建立结果是不是1已建立 if (client.getReadyState().ordinal() == 1){ return client; } }catch (URISyntaxException e){ log.error(e.getMessage()); } return null; } }调用方式
WebSocketClient client = new WebsocketUtils().getClient("ws://127.0.0.1:8080/websocket/web01");
有些服务端证书存在问题,wss协议无法访问,需设置忽略证书
SSL(Secure Sockets Layer 安全套接字协议),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层与应用层之间对网络连接进行加密。
在SSL通信协议中,我们都知道首先服务端必须有一个数字证书,当客户端连接到服务端时,会得到这个证书,然后客户端会判断这个证书是否是可信的,如果是,则交换信道加密密钥,进行通信。如果不信任这个证书,则连接失败。
SSLContext: 此类的实例表示安全套接字协议的实现, 它是SSLSocketFactory、SSLServerSocketFactory和SSLEngine的工厂。
SSLSocket: 扩展自Socket
SSLServerSocket: 扩展自ServerSocket
SSLSocketFactory: 抽象类,扩展自SocketFactory, SSLSocket的工厂
SSLServerSocketFactory: 抽象类,扩展自ServerSocketFactory, SSLServerSocket的工厂
KeyStore: 表示密钥和证书的存储设施
KeyManager: 接口,JSSE密钥管理器
TrustManager: 接口,信任管理器
X509TrustManager:TrustManager的子接口,管理X509证书,验证远程安全套接字
X509ExtendedTrustManager:实现X509TrustManager的抽象类
理论上实现TrustManager接口,对checkServerTrusted和checkClientTrusted空实现即可.
如下代码,但是在运行时发现报错了:
``
import org.java_websocket.client.WebSocketClient; import org.java_websocket.drafts.Draft; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.net.URI; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** *构建SSLWebSocket客户端,忽略证书 */ public abstract class SSLWebSocketClient extends WebSocketClient { //构造方法 public SSLWebSocketClient(URI serverURI,String message) { super(serverURI); if(serverURI.toString().contains("wss://")){ trustAllHosts(this); this.send(message); } } public SSLWebSocketClient(URI serverURI,Draft draft) { super(serverURI,draft); if(serverURI.toString().contains("wss://")) trustAllHosts(this); } /** *忽略证书 *@paramclient */ void trustAllHosts(SSLWebSocketClient client) { TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { // return new java.security.cert.X509Certificate[]{}; // System.out.println("getAcceptedIssuers"); return null; } @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { System.out.println("checkClientTrusted"); } @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { System.out.println("checkServerTrusted"); } }}; try { SSLContext ssl = SSLContext.getInstance("SSL"); ssl.init(null, trustAllCerts, new java.security.SecureRandom()); SSLSocketFactory socketFactory = ssl.getSocketFactory(); this.setSocketFactory(socketFactory); } catch (Exception e) { e.printStackTrace(); } } }运行中报错:No subject alternative names present
排查过程:
查询后这个是证书设置的ip和当前websocket服务端配置的ip不符导致校验不通过报错。
但是我们命名已经设置了忽略证书,为什么还会校验呢?
2、同时,打断点发现,在执行sslWebSocketClient.connect();时,socket中的trustmanager就已经是系统默认的抽象类了。
3、那么只有一种可能,我们在ssl.init中传入的自定义trustmanager并没有被使用,打开后果然发现,在choose方法中,我们传入的是父类TrustManager,会默认返回抽象类的包装器。
而包装器中除了执行原本的trust方法,还会进行加强处理,除了自身判断参数等外, 还会调用实现类X509TrustManagerImpl的静态方法校验证书的ip,dns等,当ip与证书不匹配时就会报出上述异常。


处理方法:
既然他会用包装器类加强,根据ssl.init初始化代码,我们直接写一个抽象类的子类,在舒适化时舍弃包装类即可。
完整代码
``
import org.java_websocket.client.WebSocketClient; import org.java_websocket.drafts.Draft; import javax.net.ssl.*; import java.net.Socket; import java.net.URI; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** *构建SSLWebSocket客户端,忽略证书 */ public abstract class SSLWebSocketClient extends WebSocketClient { //构造方法 public SSLWebSocketClient(URI serverURI,String message) { super(serverURI); if(serverURI.toString().contains("wss://")){ trustAllHosts(this); this.send(message); } } public SSLWebSocketClient(URI serverURI,Draft draft) { super(serverURI,draft); if(serverURI.toString().contains("wss://")) trustAllHosts(this); } /** *忽略证书 *@paramclient */ void trustAllHosts(SSLWebSocketClient client) { TrustManager[] trustAllCerts = new TrustManager[]{new X509ExtendedTrustManager() { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { } public java.security.cert.X509Certificate[] getAcceptedIssuers() { // return new java.security.cert.X509Certificate[]{}; // System.out.println("getAcceptedIssuers"); return null; } @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { System.out.println("checkClientTrusted"); } @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { System.out.println("checkServerTrusted"); } }}; try { SSLContext ssl = SSLContext.getInstance("SSL"); ssl.init(null, trustAllCerts, new java.security.SecureRandom()); SSLSocketFactory socketFactory = ssl.getSocketFactory(); this.setSocketFactory(socketFactory); } catch (Exception e) { e.printStackTrace(); } } }验证:
初始化加载自定义类,信息实时收到


1、客户端依赖为1.5.2,在1.3.8中不支持设置ssl忽略证书
org.java-websocket Java-WebSocket 1.5.2
上一篇:软考java后端证书
下一篇:学习java考什么证有用
皓盘云建最新版下载v9.0 安卓版
53.38MB |商务办公
ris云客移动销售系统最新版下载v1.1.25 安卓手机版
42.71M |商务办公
粤语翻译帮app下载v1.1.1 安卓版
60.01MB |生活服务
人生笔记app官方版下载v1.19.4 安卓版
125.88MB |系统工具
萝卜笔记app下载v1.1.6 安卓版
46.29MB |生活服务
贯联商户端app下载v6.1.8 安卓版
12.54MB |商务办公
jotmo笔记app下载v2.30.0 安卓版
50.06MB |系统工具
鑫钜出行共享汽车app下载v1.5.2
44.7M |生活服务
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-03-26
2022-02-15
2022-03-26
2022-02-14