认识WebSocket并搭建服务端

前言

一、概述

WebSocket 协议在2008年诞生,2011年成为国际标准。webSocket能够在建立连接之后,在服务器端推送数据到客户端,解决HTTP协议的弊端。

特点:

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

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

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

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

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

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

为什么需使用webSocket?

因为HTTP协议的通信只能由客户端发起,无法服务端主动推送数据。我们当然可以使用HTTP然后轮询探测数据变化,但效率低,性能差,不建议使用。

二、客户端说明

1.新建 WebSocket 实例

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

2.webSocket.readyState

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

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

3.webSocket.onopen

onopen属性,用于指定连接成功后的回调函数。

4.webSocket.onclose

onclose属性,用于指定连接关闭后的回调函数。

5.webSocket.onmessage

onmessage属性,用于指定收到服务器数据后的回调函数。

6.webSocket.send()

send()方法用于向服务器发送数据。

7.webSocket.bufferedAmount

bufferedAmount属性,表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。

8.webSocket.onerror

onerror属性,用于指定报错时的回调函数。

三、服务端说明

常用的 Node 实现有以下三种。

  • µWebSockets
  • Socket.IO
  • WebSocket-Node

Java搭建webSocket服务

使用SpringBoot作为基础环境

导入依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>2.0.4.RELEASE</version>
</dependency>

添加webSocket配置:

package cn.hacah.websocketdemo.config;import cn.hacah.websocketdemo.handle.SpringWebSocketHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;@Configuration
@EnableWebSocket
public class SpringWebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(getSpringWebSocketHandler(), "/websocket/server").addInterceptors(getInterceptor()).setAllowedOrigins("*");registry.addHandler(getSpringWebSocketHandler(), "/sockjs/server").setAllowedOrigins("*").addInterceptors(getInterceptor()).withSockJS();}@Beanpublic SpringWebSocketHandler getSpringWebSocketHandler() {return new SpringWebSocketHandler();}@Beanpublic SpringWebSocketHandlerInterceptor getInterceptor() {return new SpringWebSocketHandlerInterceptor();}
}

拦截器:

package cn.hacah.websocketdemo.config;import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;import javax.servlet.http.HttpSession;
import java.util.Map;public class SpringWebSocketHandlerInterceptor extends HttpSessionHandshakeInterceptor {@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,Map<String, Object> attributes) throws Exception {System.out.println("Before Handshake");if (request instanceof ServletServerHttpRequest) {ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;// 获取session时,如果没有则返回nullHttpSession session = servletRequest.getServletRequest().getSession(false);if (session != null) {// 在登录时保存的用户名String userName = (String) session.getAttribute("SESSION_USERNAME");if (userName != null) {// 放入attributes中,可以在处理器的WebSocketSession中取出attributes.put("WEBSOCKET_USERID", userName);}}}return super.beforeHandshake(request, response, wsHandler, attributes);}@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,Exception ex) {super.afterHandshake(request, response, wsHandler, ex);System.out.println("after Handshake");}}

webSocket处理代码

package cn.hacah.websocketdemo.handle;import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;public class SpringWebSocketHandler extends TextWebSocketHandler {/*** 存储用户id和其对应的session*/private static final Map<String, WebSocketSession> users = new HashMap<>();/*** 用户名key值*/private static final String USER_ID = "WEBSOCKET_USERID";/*** 连接建立后触发*/@Overridepublic void afterConnectionEstablished(WebSocketSession session) {System.out.println("成功建立websocket连接!");// 取出在拦截器中存储的usernameString userId = (String) session.getAttributes().get(USER_ID);users.put(userId, session);System.out.println("当前线上用户数量:" + users.size());}/*** 关闭连接时触发*/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) {String userId = (String) session.getAttributes().get(USER_ID);System.out.println("用户" + userId + "已退出!");users.remove(userId);System.out.println("剩余在线用户" + users.size());}/*** 接收消息*/@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {super.handleTextMessage(session, message);System.out.println("收到消息:" + message);if (message.getPayload().contains("在吗")) {session.sendMessage(new TextMessage("对方不在线!"));}}@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {if (session.isOpen()) {session.close();}System.out.println("传输出现异常,关闭websocket连接... ");String userId = (String) session.getAttributes().get(USER_ID);users.remove(userId);}@Overridepublic boolean supportsPartialMessages() {return false;}/*** 给某个用户发送消息*/public void sendMessageToUser(String userId, TextMessage message) {for (String id : users.keySet()) {if (id.equals(userId)) {try {if (users.get(id).isOpen()) {users.get(id).sendMessage(message);}} catch (IOException e) {e.printStackTrace();}break;}}}/*** 给所有在线用户发送消息*/public void sendMessageToUsers(TextMessage message) {for (String userId : users.keySet()) {try {if (users.get(userId).isOpen()) {users.get(userId).sendMessage(message);}} catch (IOException e) {e.printStackTrace();}}}
}

controller代码:

package cn.hacah.websocketdemo.controller;import cn.hacah.websocketdemo.handle.SpringWebSocketHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.socket.TextMessage;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;/*GET请求开放用于测试,最好只允许POST请求*/
@Controller
@RequestMapping(value = "/websocket", method = {RequestMethod.POST, RequestMethod.GET})
public class WebSocketController {@AutowiredSpringWebSocketHandler springWebSocketHandler;/*** 登录将username放入session中,然后在拦截器HandshakeInterceptor中取出*/@ResponseBody@RequestMapping("/login")public String login(HttpServletRequest request, @RequestParam(value = "username") String username, @RequestParam(value = "password") String password) {System.out.println("登录:" + username + ":" + password);HttpSession session = request.getSession();if (null != session) {session.setAttribute("SESSION_USERNAME", username);return "success";} else {return "fail";}}/*** 指定发送*/@ResponseBody@RequestMapping("/sendToUser")public String send(@RequestParam(value = "username") String username, @RequestParam(value = "info") String info) {springWebSocketHandler.sendMessageToUser(username, new TextMessage(info));System.out.println("发送至:" + username);return "success";}/*** 广播*/@ResponseBody@RequestMapping("/broadcast")public String broadcast(@RequestParam(value = "info") String info) {springWebSocketHandler.sendMessageToUsers(new TextMessage("广播消息:" + info));System.out.println("广播成功");return "success";}
}

测试:

1.登录

打开http://localhost:8080/websocket/login?username=guest&password=123

2.连接

localhost:8080/index.html

3.推送消息到客户端

http://localhost:8080/websocket/sendToUser?username=river&info=%E4%BD%A0%E5%A5%BD

代码地址:gitee

参考或相关文章

https://blog.csdn.net/river66/article/details/102940323

https://www.ruanyifeng.com/blog/2017/05/websocket.html

认识WebSocket并搭建服务端相关推荐

  1. Java Websocket实例【服务端与客户端实现全双工通讯】

    Java Websocket实例[服务端与客户端实现全双工通讯] 现很多网站为了实现即时通讯,所用的技术都是轮询(polling).轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发 出HTTP ...

  2. mysql第五章项目二_Todo List:Node+Express 搭建服务端毗邻Mysql – 第五章(第1节)

    点击右上方红色按钮关注"web秀",让你真正秀起来 前言 万丈高楼平地起,我们的Todo List项目也是越来越结实了.Todo List的前面4章内容都是在为Client端开发, ...

  3. Netty的Socket编程详解-搭建服务端与客户端并进行数据传输

    场景 Netty在IDEA中搭建HelloWorld服务端并对Netty执行流程与重要组件进行介绍: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article ...

  4. 原神私服搭建二: 搭建服务端

    原神私服搭建二: 搭建服务端 所需资源: 一:下载jar包 链接:https://github.com/Grasscutters/Grasscutter/releases 二:下载资源 resourc ...

  5. 最详细的【微信小程序+阿里云Web服务】开发部署指引(四):搭建服务端数据库

    文章目录 前言 一.连接主机数据库 二.创建数据表结构 三.准备测试数据 专题文章链接 前言 做完了前面的注册申请工作,今天我们开始进行程序的开发. 这篇文章,我们要完成的是服务端数据库表的创建. 一 ...

  6. .NET 使用sock5做代理(不是搭建服务端)

    在日常开发中经常会遇到这些需求,爬取数据,都知道现在通常用python爬取是很方便的,但是免不了还有很多伙伴在用NET来爬取,在爬取数据的时候我们知道需要使用代理服务器,如果不用代理,你的IP很有可能 ...

  7. 使用tomcat方式实现websocket即时通讯服务端讲解

    使用tomcat方式实现websocket即时通讯服务端讲解 第一种方案:使用Tomcat的方式实现 tomcat版本要求:tomcat7.0+.需要支持Javaee7 导入javeee-api的ja ...

  8. SVN服务端的搭建及客户端连接(云上搭建服务端)

    SVN服务端: 环境:linux下搭建 1,安装软件(系统自带) SVN 的一些概念 repository(源代码库):源代码统一存放的地方 Checkout(提取):当您手上没有源代码时,您需要从r ...

  9. 服务器怎么向指定客户端发送信息,WebSocket 如何实现服务端向客户端发送消息?...

    我们都知道, Websocket 是一个双向的通讯方式,一般情况下,我们都是根据 Client 的情况返回信息,但是在一个更加健壮的系统,我们可能需要主动的向客户端发送消息.我试图在中文网络去搜索,查 ...

最新文章

  1. idea 关于spring boot实现自动编译
  2. 张思华:希望通过创新加深NetApp与中国的联系
  3. redis报错(error) LOADING Redis is loading the dataset in memory
  4. 【通俗易懂】C语言中,for循环中i++与++i的区别
  5. 重装系统后sqlserver安装失败_Windows 10八月更新再遇尴尬:安装失败 或安装后随机重启...
  6. 深度学习和神经网络——第二周笔记
  7. java eclipse
  8. ie型lfsr_什么是PRBS
  9. SuperMap iClient3D for WebGL实现三维管线分析
  10. ppt流程图箭头分叉_PPT实用模版大全(最全箭头、流程图).ppt
  11. Elesticsearch(es)聚合搜索(入门到精通)4
  12. synchronized锁升级之重量级锁
  13. ASP.NET的图片上传和显示
  14. JS脚本实现模拟按钮点击:批量抓取百度推广中的关键词建议
  15. 小米笔记本Air 13.3 的键盘功能按键使用方式
  16. logrotate失效的简单排查
  17. ip iq 谐波检测matlab仿真,ip-iq谐波检测法的仿真及实验研究ip-iq谐波检测法的仿真及实验研究.pdf...
  18. 九年级计算机VB知识点,2020年全国计算机二级VB复习知识点:语句
  19. deepin美化,conky使用教程
  20. 这篇文章,我可能在贩卖“焦虑”。。

热门文章

  1. 嘘!P站数据分析年报;各省市疫情感染进度条;爱奇艺推出元宇宙App;You推出AI聊天机器人;GitHub今日热榜 | ShowMeAI资讯日报
  2. java疯狂讲义第四版第五章答案_疯狂java讲义第五章笔记
  3. 2023前端面试总结含参考答案
  4. Microsoft修复工具
  5. Proxmox VE 7.2 网卡直通
  6. 读论文 Attention Scaling for Crowd Counting
  7. 【Games101 作业6 + 附加题】渲染兔子 BVH SAH 代码
  8. 公司寄件报销难?你缺的只是高效的寄件管理解决方案
  9. “无法删除数据库,因为该数据库当前正在使用“问题解决
  10. Java常见面试题—”static”关键字有什么用?