websocket&nginx

您所在的位置:网站首页 websocketserver安装报错 websocket&nginx

websocket&nginx

2023-04-11 13:14| 来源: 网络整理| 查看: 265

WebSocket和nginx使用 http&ws https&wss

WebSocket 协议在2008年诞生࿰c;2011年成为国际标准。所有浏览器都已经支持了。 它的最大特点就是࿰c;服务器可以主动向客户端推送信息࿰c;客户端也可以主动向服务器发送信息࿰c;是真正的双向平等对话࿰c;属于服务器推送技术的一种。 WebSocket 协议在2008年诞生࿰c;2011年成为国际标准。所有浏览器都已经支持了。

它的最大特点就是࿰c;服务器可以主动向客户端推送信息࿰c;客户端也可以主动向服务器发送信息࿰c;是真正的双向平等对话࿰c;属于服务器推送技术的一种。

websocket&nginx

其他特点包括:

(1)建立在 TCP 协议之上࿰c;服务器端的实现比较容易。 (2)与 http 协议有着良好的兼容性。默认端口也是80和443࿰c;并且握手阶段采用 http 协议࿰c;因此握手时不容易屏蔽࿰c;能通过各种 http 代理服务器。 (3)数据格式比较轻量࿰c;性能开销小࿰c;通信高效。 (4)可以发送文本࿰c;也可以发送二进制数据。 (5)没有同源限制࿰c;客户端可以与任意服务器通信。 (6)协议标识符是ws(如果加密࿰c;则为wss)࿰c;服务器网址就是 URL。

@H_772_20@

ws://example.com:80/some/path # 与http协议并行 wss://example.com:80/some/path # 与https协议并行

websocket&nginx

前端后端示例 @H_772_31@vue window.webSocket = new WebSocket(`${wsUrl}${iD}`); // window.webSocket = new Rwebsocket(`${wsUrl}${iD}`, null, { debug: true, reconnecTinterval: 3000,automaticOpen:false }) /*建立连接*/ webSocket.onopen = e => { console.log('建立连接') heartcheck.reset().start(); // 成功建立连接后࿰c;重置心跳检测 }; /*连接关闭*/ webSocket.onclose = e => { console.log("webSocket连接关闭"); }; // /*接收服务器推送消息*/ webSocket.onmessage = e => { heartcheck.reset().start(); // 如果获取到消息࿰c;说明连接是正常的࿰c;重置心跳检测 console.log(e,'接收服务器推送消息') let res=JSON.parse(e.data) callBACk(res) }; // /*连接发生错误时*/ webSocket.onerror = e => { console.log('webSocket连接失败'); } // 心跳检测, 每隔一段时间检测连接状态࿰c;如果处于连接中࿰c;就向server端主动发送消息࿰c;来重置server端与客户端的最大连接时间࿰c;如果已经断开了࿰c;发起重连。 let heartcheck = { timeout: 55000, // 发一次心跳࿰c;比server端设置的连接时间稍微小一点࿰c;在接近断开的情况下以通信的方式去重置连接时间。 serverTimeoutObj: null, reset: function () { // clearTimeout(this.timeoutObj); clearTimeout(this.serverTimeoutObj); return this; }, start: function () { this.serverTimeoutObj = seTinterval(function () { if (window.webSocket&&window.webSocket.readyState == 1) { console.log("连接状态࿰c;发送消息保持连接"); window.webSocket.send(`{"toUserId":"${iD}"}`); heartcheck.reset().start(); // 如果获取到消息࿰c;说明连接是正常的࿰c;重置心跳检测 } else { console.log("断开状态࿰c;尝试重连"); window.webSocket.close(); Socket(); } }, this.timeout) } } @H_772_31@java pom.xml依赖 org.springframework.boot spring-boot-starter-websocket pojo消息实体 @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Socketmessage {@H_197_70@ /** 类型 (1-预警消息、2-通知通告、3-系统消息)*/ private Object type; /** 业务主键 */ private String businessID; /** 消息标题 */ private String title; /** 消息内容 */ private String message; /** 提报数据 */ private String commitType; /** 数据提报消息-组织ID */ private String orgId; } WebSocketServer /** * @description: WebSocket服务类 * @autor: WJY * @create: 2021-10-27 11:15 * @since: 1.8 */ @ServerEndpoint("/ws/{userID}") @Component public class WebSocketServer {@H_197_70@ static Log log= LogFactory.get(WebSocketServer.class); /**静态变量࿰c;用来记录当前在线连接数。应该把它设计成线程安全的。*/ private static int onlineCount = 0; /**concurrent包的线程安全Set࿰c;用来存放每个客户端对应的MyWebSocket对象。*/ private static ConcurrentHashMap webSocketMap = new ConcurrentHashMap(); /**与某个客户端的连接会话࿰c;需要通过它来给客户端发送数据*/ private Session session; /**接收userId*/ private String userId=""; /** * 连接建立成功调用的方法*/ @OnOpen public void onOpen(Session session,@PathParam("userId") String userId) {@H_197_70@ this.session = session; this.userId=userId; if(webSocketMap.containsKey(userId)){@H_197_70@ webSocketMap.remove(userId); webSocketMap.put(userId,this); //加入set中 }else{@H_197_70@ webSocketMap.put(userId,this); //加入set中 addOnlineCount(); //在线数加1 } log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount()); try {@H_197_70@ webSocketMap.get(userId). sendmessage(JSONUtil.toJsonStr(new Socketmessage())); session.setMaxIdleTimeout(3600000); } catch (IOException e) {@H_197_70@ log.error("用户:"+userId+",网络异常!!!!!!"); } } /** * 连接关闭调用的方法 */ @OnClose public void onClose() {@H_197_70@ try {@H_197_70@ if(webSocketMap.containsKey(userId)){@H_197_70@ webSocketMap.remove(userId); //从set中删除 subOnlineCount(); } log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount()); }catch (Exception e){@H_197_70@ log.error("用户关闭连接!!!"); } } /** * 实现服务器主动推送 */ public void sendmessage(String message) throws IOException {@H_197_70@ this.session.getBasicRemote().sendText(message); } /** * 发送自定义消息 * */ public void sendInfo(Object message, @PathParam("userId") String userId) throws IOException {@H_197_70@ log.info("发送消息到:"+userId+"࿰c;报文:"+message); if(StringUtils.isnotBlank(userId)&&webSocketMap.containsKey(userId)){@H_197_70@ webSocketMap.get(userId).sendmessage(JSONUtil.toJsonStr(message)); }else{@H_197_70@ log.error("用户"+userId+",不在线!"); } } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息*/ @Onmessage public void onmessage(String message, Session session) {@H_197_70@ log.info("用户消息:"+userId+",报文:"+message); //可以群发消息 //消息保存到数据库、redis if(StringUtils.isnotBlank(message)){@H_197_70@ try {@H_197_70@ //解析发送的报文 JSONObject jsonObject = JSON.parseObject(message); //追加发送人(防止串改) jsonObject.put("fromUserId",this.userId); String toUserId = this.userId; if (ObjectUtils.isnotEmpty(jsonObject) && ObjectUtils.isnotEmpty(jsonObject.getString("toUserId"))){@H_197_70@ toUserId =jsonObject.getString("toUserId"); } //传送给对应toUserId用户的websocket if(StringUtils.isnotBlank(toUserId)&&webSocketMap.containsKey(toUserId)){@H_197_70@ webSocketMap.get(toUserId).sendmessage(jsonObject.toJSONString()); }else{@H_197_70@ log.error("请求的userId:"+toUserId+"不在该服务器上"); //否则不在这个服务器上࿰c;发送到mysql或者redis } }catch (Exception e){@H_197_70@ e.printStackTrace(); } } } /** * 错误消息处理 * @param session * @param error */ @OnError public void onError(Session session, Throwable error) {@H_197_70@ log.error("用户错误:"+this.userId+",原因:"+error.getmessage()); error.printStackTrace(); } public static synchronized int getOnlineCount() {@H_197_70@ return onlineCount; } public static synchronized void addOnlineCount() {@H_197_70@ WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount() {@H_197_70@ WebSocketServer.onlineCount--; } } WebSocketConfig /** * @description: webSocket对象配置类 * @autor: WJY * @create: 2021-10-27 11:12 * @since: 1.8 */ @Configuration public class WebSocketConfig {@H_197_70@ @Bean public ServerEndpointexporter serverEndpointexporter(){@H_197_70@ return new ServerEndpointexporter(); } } WebSocketController /** * @description: WebSocket消息推送 * @autor: WJY * @create: 2021-10-27 11:18 * @since: 1.8 */ @RestController public class ForWARDNewsController {@H_197_70@ @resource private WebSocketUtils webSocketUtils; /** * 前端推送消息(测试消息推送接口) * * @param message * @param toUserId * @return */ @GetMapping("/push/{toUserID}") public ResponseEntity pushToWeb(String message, @PathVariable String toUserId) {@H_197_70@ // 模拟消息实时消息推送 Socketmessage socketmessage = new Socketmessage("业务类型(1-预警消息、2-通知通告、3-系统消息)", "业务主键", "消息标题", "消息内容", null, null); webSocketUtils.sendCustomizemessage(socketmessage, toUserId); return ResponseEntity.ok("MSG SEND succesS"); } } // @GetMapping("index") // public ResponseEntity index(){@H_197_70@ // return ResponseEntity.ok("请求成功"); // } // // /** // * 获取页面信息 // * @return // */ // @GetMapping("page") // public ModelAndView page(){@H_197_70@ // return new ModelAndView("websocket"); // } WebSocketUtils /** * @description: webSocket工具类 * @autor: WJY * @create: 2021-10-28 9:53 * @since: 1.8 */ @Component @Slf4j public class WebSocketUtils {@H_197_70@ @resource private WebSocketServer webSocketServer; /** * 发送自定义消息 * @param msg */ public void sendCustomizemessage(T msg, String userId){@H_197_70@ try {@H_197_70@ webSocketServer.sendInfo(@H_940_44@msg, userId); }catch (Exception i){@H_197_70@ log.error("WebSocketUtils.sendCustomizemessage消息发送异常:{}", i.getmessage()); } } } 权限路径过滤(springboog+springSecurity) security: permit: list: - /ws/* @H_772_31@前端API WebSocket 构造函数

WebSocket 对象作为一个构造函数࿰c;用于新建 WebSocket 实例。

var ws = new WebSocket('ws://localhost:8080');

执行上面语句之后࿰c;客户端就会与服务器进行连接。 实例对象的所有属性和方法清单࿰c;参见这里。

webSocket.readyState

readyState属性返回实例对象的当前状态࿰c;共有四种。

@H_772_20@

CONNECTinG:值为0࿰c;表示正在连接。 OPEN:值为1࿰c;表示连接成功࿰c;可以通信了。 CLOSING:值为2࿰c;表示连接正在关闭。 CLOSED:值为3࿰c;表示连接已经关闭࿰c;或者打开连接失败。

下面是一个示例。

switch (ws.readyStatE) { case WebSocket.CONNECTinG: // do something break; case WebSocket.oPEN: // do something break; case WebSocket.CLOSING: // do something break; case WebSocket.CLOSED: // do something break; default: // this never happens break; } webSocket.onopen

实例对象的onopen属性࿰c;用于指定连接成功后的回调函数。

ws.onopen = function () { ws.send('Hello Server!'); }

如果要指定多个回调函数࿰c;可以使用addEventListener方法。

ws.addEventListener('open', function (event) { ws.send('Hello Server!'); }); webSocket.onclose

实例对象的onclose属性࿰c;用于指定连接关闭后的回调函数。

ws.onclose = function(event) { var code = event.code; var reason = event.reason; var wasClean = event.wasClean; // handle close event }; ws.addEventListener("close", function(event) { var code = event.code; var reason = event.reason; var wasClean = event.wasClean; // handle close event }); webSocket.onmessage

实例对象的onmessage属性࿰c;用于指定收到服务器数据后的回调函数。

ws.onmessage = function(event) { var data = event.data; // 处理数据 }; ws.addEventListener("message", function(event) { var data = event.data; // 处理数据 });

注意࿰c;服务器数据可能是文本࿰c;也可能是二进制数据(blob对象或Arraybuffer对象)。

ws.onmessage = function(event){ if(typeof event.data === String) { console.log("Received data String"); } if(event.data instanceof ArrayBuffer){ var buffer = event.data; console.log("Received arraybuffer"); } }

除了动态判断收到的数据类型࿰c;也可以使用binaryType属性࿰c;显式指定收到的二进制数据类型。

// 收到的是 blob 数据 ws.binaryType = "blob"; ws.onmessage = function(E) { console.log(e.data.sizE); }; // 收到的是 ArrayBuffer 数据 ws.binaryType = "arraybuffer"; ws.onmessage = function(E) { console.log(e.data.byteLength); }; webSocket.send()

实例对象的send()方法用于向服务器发送数据。

发送文本的例子。

ws.send('your message');

发送 Blob 对象的例子。

var file = document .querySELEctor('input[type="file"]') .files[0]; ws.send(filE);

发送 ArrayBuffer 对象的例子。

// Sending canvas ImageData as ArrayBuffer var img = canvas_context.getImageData(0, 0, 400, 320); var binary = new Uint8Array(img.data.length); for (var i = 0; i < img.data.length; i++) { binarY[i] = img.data[i]; } ws.send(binary.buffer); webSocket.bufferedamount

实例对象的bufferedamount属性࿰c;表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。

var data = new ArrayBuffer(10000000); socket.send(data); if (socket.bufferedamount === 0) { // 发送完毕 } else { // 发送还没结束 } webSocket.onerror

实例对象的onerror属性࿰c;用于指定报错时的回调函数。

socket.onerror = function(event) { // handle error event }; socket.addEventListener("error", function(event) { // handle error event }); @H_772_31@nginx相关配置 # 重点的两行配置 # 将nginx的请求头从http升级到websocket. proxy_set_header Upgrade $http_upgrade; # 进行nginx连接websocket proxy_set_header Connection "upgrade"; user root; worker_processes 2; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events {@H_197_70@ worker_connections 1024; } http {@H_197_70@ include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forWARDed_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; tcp_nodelay on; proxy_http_version 1.1; #keepalive_timeout 0; 0是禁止等待后台服务响应时间 keepalive_timeout 65; server {@H_197_70@ listen 80; server_name localhost; client_max_body_size 2000M; # 开启gzip gzip on; # 启用gzip压缩的最小文件࿰c;小于设置值的文件将不会压缩 gzip_min_length 1k; # gzip 压缩级别࿰c;1-9࿰c;数字越大压缩的越好࿰c;也越占用CPU时间࿰c;后面会有详细说明 gzip_comp_level 9; # 进行压缩的文件类型。javascript有多种形式࿰c;后面的图片压缩不需要的可以自行删除 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; # 是否在http header中添加Vary: Accept-Encoding࿰c;建议开启 gzip_vary on; # 设置压缩所需要的缓冲区大小 gzip_buffers 4 16k; #获取用户真实ip proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-ForWARDed-For $proxy_add_x_forWARDed_for; proxy_connect_timeout 1800; proxy_send_timeout 1800; proxy_read_timeout 1800; LOCATIOn / {@H_197_70@ root /tmp/production; index index.html index.htm; } error_page 500 502 503 504 /50x.html; LOCATIOn = /50x.html {@H_197_70@ root /usr/local/nginx/html; } # webSocket使用过必要的配置项项(单独配置的路径代理) LOCATIOn /wsk/ {@H_197_70@ proxy_pass 代理的ip地址; # 重点的两行配置 # 将nginx的请求头从http升级到websocket. proxy_set_header Upgrade $http_upgrade; #进行nginx连接websocket proxy_set_header Connection "upgrade"; } } } @H_772_31@参考文献 @H_772_20@

https://www.ruanyifeng.com/blog/2017/05/websocket.html https://www.tutorialspoint.com/websockets/websockets_communicaTing_server.htm http://www.eclipse.org/jetty/documentation.php https://www.cnblogs.com/mafly/p/websocket.html (nginx中502报错) http://nginx.org/en/docs/http/websocket.html (nginx配置详解)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3