WebSocket简介与消息推送

B/S架构的系统多使用HTTP协议,HTTP协议的特点:

1 无状态协议
2 用于通过 Internet 发送请求消息和响应消息
3 使用端口接收和发送消息,默认为80端口
底层通信还是使用Socket完成。

HTTP协议决定了服务器与客户端之间的连接方式,无法直接实现消息推送(F5已坏),一些变相的解决办法:

双向通信与消息推送

轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。 优点:后端程序编写比较容易。 缺点:请求中有大半是无用,浪费带宽和服务器资源。 实例:适于小型应用。

长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。 优点:在无消息的情况下不会频繁的请求,耗费资小。 缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。 Comet异步的ashx,实例:WebQQ、Hi网页版、Facebook IM。

长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。 优点:消息即时到达,不发无用请求;管理起来也相对便。 缺点:服务器维护一个长连接会增加开销。 实例:Gmail聊天

Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。 优点:实现真正的即时通信,而不是伪即时。 缺点:客户端必须安装Flash插件;非HTTP协议,无法自动穿越防火墙。 实例:网络互动游戏。

Websocket:
WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。依靠这种技术可以实现客户端和服务器端的长连接,双向实时通信。
特点:
事件驱动
异步
使用ws或者wss协议的客户端socket

能够实现真正意义上的推送功能

缺点:

少部分浏览器不支持,浏览器支持的程度与方式有区别。

JavaEE 7中出了JSR-356:Java API for WebSocket规范。不少Web容器,如Tomcat,Nginx,Jetty等都支持WebSocket。

一、下面demo使用jetty实现:

项目结构:

java后台代码:

package edu.nf.ws.server;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Set; /**
* @author wangl
* @date 2018-12-05
* websocket服务端
*/
@ServerEndpoint("/chat/server/{userName}")
public class ChatServer { /**
* 当有客户端连接到服务端的时候就会调用这个方法
* session代表客户端和服务端的一个连接会话对象
* ,由容器负责创建和维护
*/
@OnOpen
public void onOpen(Session session, @PathParam("userName") String userName){
System.out.println("有客户端连接..."+userName);
//将用户名保存到当前用户会话的属性中(有点类似作用域的概念)
session.getUserProperties().put("user", userName);
} /**
* 客户端和服务器之间通信的方法,
* 服务端每当接收到客户端的消息就会调用这个方法
* ,注意:必须指定一个String类型的参数,表示接收到客户端的文本消息
*/
@OnMessage
public void onMessage(String message, Session session) throws IOException{
System.out.println("接收消息..." + message);
//将消息发送给所有人
sendAllUser(message, session);
} /**
* 当客户端关闭或者断开连接时,服务端会调用此方法
* @param session
*/
@OnClose
public void opnClose(Session session) throws IOException{
System.out.println("客户端失去连接...");
//关闭会话
session.close();
} private void sendAllUser(String message, Session session) throws IOException{
//获取所有人的会话对象
Set<Session> users = session.getOpenSessions();
//获取发送人
String sendUser = session.getUserProperties().get("user").toString();
//发送给所有人
for (Session user : users) {
user.getBasicRemote().sendText(sendUser + " : " + message);
}
}
}

html代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="loginDiv">
用户名:<input type="text" id="userName" name="userName"/>
<input type="button" id="login" value="login"/>
</div> <div id="container" style="display: none">
<div id="content"></div>
<input type="text" name="msg" id="msg"/>
<input type="button" id="send" value="send"/>
</div> <script src="js/jquery-3.3.1.min.js"></script>
<script>
$(function(){
var ws;
//登陆
$('#login').on('click',function(){
var userName = $('#userName').val();
//创建websocket对象并连接服务端
ws = new WebSocket('ws://localhost:8080/chat/server/' + userName); //客户端打开连接时会回调此方法
/*ws.onopen = function(){
//...
}*/ //客户端关闭或断开连接时执行此方法
/*ws.onclose = function(){
//...
}*/ //接收服务端发送的消息
ws.onmessage = function(message){
$('#content').append(message.data + "<br>");
}
$('#loginDiv').css('display','none');
$('#container').css('display','block');
});
//发送消息
$('#send').on('click',function(){
var msg = $('#msg').val();
//发送消息
ws.send(msg);
});
})
</script>
</body>
</html>


二、spring+jetty实现

项目结构:

pom 配置:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>edu.nf</groupId>
<artifactId>spring-ws</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
<!-- spring版本 -->
<spring.version>5.1.1.RELEASE</spring.version>
<servlet.version>4.0.1</servlet.version>
<jackson.version>2.9.7</jackson.version>
</properties> <!-- 添加依赖 -->
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies> <!-- war插件 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.2</version>
<configuration>
<warSourceDirectory>web</warSourceDirectory>
<!-- 指定web.xml路径 -->
<webXml>web\WEB-INF\web.xml</webXml>
</configuration>
</plugin>
</plugins>
</build> </project>

web.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"> <servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dispatcher-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> </web-app>

ServerEndpointHandler(服务端)

package edu.nf.demo.websocket;

import edu.nf.demo.entity.Users;
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.util.Map;
import java.util.concurrent.ConcurrentHashMap; /**
* @author wangl
* @date 2018-12-06
* websocket服务端
*/
public class ServerEndpointHandler extends TextWebSocketHandler { /**
* 维护一个用户列表(key存放用户名,value存放每一个用户的WebSocketSession)
*/
private static Map<String, WebSocketSession> users = new ConcurrentHashMap<>(); /**
* 客户端建立连接之后执行此方法(onOpen)
* @param session 每当客户端连接后,容器会为其创建一个Session对象,
* 这个对象在Spring中就是WebSocketSession
* @throws Exception
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("客户端建立了连接...");
//获取用户名,getAttributes方法得到的是一个Map,
//这个map里面存放了握手拦截器将HttpSession作用域拷贝过去的数据
Users user = (Users)session.getAttributes().get("user");
//将用户的session保存到用户列表中
users.put(user.getUserName(), session);
} /**
* 每当客户端发送消息时执行此方法(onmessage)
* @param session
* @param message TextMessage对象表示接收客户端的文本消息对象,
* 它的getPayload方法将获取具体消息内容
* @throws Exception
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
System.out.println("接收客户端消息..." + message.getPayload());
//获取用户名
Users sendUser = (Users)session.getAttributes().get("user");
//群发消息
for(String userName : users.keySet()){
//重新构建一个TextMessage对象
TextMessage newMessage = new TextMessage(sendUser.getUserName() + " : " + message.getPayload());
//发送所有人
users.get(userName).sendMessage(newMessage);
}
} /**
* 哭护短关闭或断开连接时执行此方法(onclose)
* @param session
* @param status
* @throws Exception
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("客户端断开连接...");
session.close();
}
}

UserController(请求控制类)

package edu.nf.demo.controller;

import edu.nf.demo.controller.vo.ResponseVO;
import edu.nf.demo.entity.Users;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpSession; /**
* @author wangl
* @date 2018-12-06
*/
@RestController
public class UserController { @PostMapping("/userLogin")
public ResponseVO login(Users user, HttpSession session){
//执行用户验证
//代码省略.......
//验证成功后将用户放入会话作用域
session.setAttribute("user", user);
ResponseVO vo = new ResponseVO();
vo.setCode(HttpStatus.OK.value());
vo.setData("index.html");
return vo;
}
}

index.html(登录成功的聊天网页)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jquery-3.3.1.min.js"></script>
</head>
<body>
<div id="content"></div>
<input type="text" id="msg" name="msg"/>
<input type="button" value="send"/>
<script>
$(function () {
var ws = new WebSocket('ws://localhost:8080/websocket');
ws.onmessage = function (event) {
$('#content').append(event.data + '<br>');
}
$(':button').on('click',function () {
var msg = $('#msg').val();
ws.send(msg);
}); })
</script>
</body>
</html>

运行结果:

原理参照博客:https://www.cnblogs.com/best/p/5695570.html

websocket简单实现在线聊天的更多相关文章

  1. 基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍。最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室。

    基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍.最后我们将会实现一个基于S ...

  2. 基于PHP实现一个简单的在线聊天功能(轮询ajax )

    基于PHP实现一个简单的在线聊天功能(轮询ajax ) 一.总结 1.用的轮询ajax 二.基于PHP实现一个简单的在线聊天功能 一直很想试着做一做这个有意思的功能,感觉复杂的不是数据交互和表结构,麻 ...

  3. 使用WebSocket实现简单的在线聊天室

    前言:我自已在网上找好了好多 WebSocket 制作 在线聊天室的案列,发现大佬们写得太高深了 我这种新手看不懂,所以就自已尝试写了一个在线简易聊天室 (我只用了js 可以用jq ) 话不多说,直接 ...

  4. Spring Websocket实现简易在线聊天功能

    针对Spring Websocket的实现,我参照了其他博主的文章https://www.cnblogs.com/leechenxiang/p/5306372.html 下面直接给出实现: 一.引入相 ...

  5. WebSocket实现简单的在线聊天

    SuperWebSocket在WebService中的应用 最开始使用是寄托在IIS中,发布之后测试时半个小时就会断开,所以改为WindowsService 1. 新建Windows服务项目[Test ...

  6. springboot+websocket实现简单的在线聊天功能

    效果如下: java实现逻辑: 1.引入maven依赖 <dependency> <groupId>org.springframework.boot</groupId&g ...

  7. Netty+WebSocket简单实现网页聊天

    基于Netty+WebSocket的网页聊天简单实现 一.pom依赖 <dependency>        <groupId>io.netty</groupId> ...

  8. application session 实现简单的在线聊天人数的统计

    写了快一年的asp.net,application对象还真没怎么用过.看了看书,根据这两个对象的特性写了一个简单的聊天室程序.真的是非常的简陋 ASP.Net中有两个重要的对象,一个是applicat ...

  9. javaweb学习路之三--websocket多人在线聊天

    在之前的项目基础上,加入了一个聊天室的功能,为了界面好看 引入了AmazeUI和umeditor最终效果图如下: 源码在 https://github.com/Zering/MyWeb 目前练习都在这 ...

随机推荐

  1. 选中FeatureLayer元素并高亮显示

    点击FeatureLayer要素会弹出popup弹出框以显示要素的相关内容.这个例子实现点击要素,选中并高亮显示.例子使用ArcGIS API for JavaScript 4.8. 一.代码框架 & ...

  2. Dynamics Business Central-如何配置VS Code连接BC环境

    最近在研究Business Central,也就是以前的Dynamics NAV,需要配置Visual Studio Code连接BC环境,以下是配置的具体步骤. 1. VS Code下载,这个不多说 ...

  3. Github排序(转载)

    目录 1. 冒泡排序 2. 选择排序 3. 插入排序 4. 希尔排序 5. 归并排序 6. 快速排序 7. 堆排序 8. 计数排序 9. 桶排序 10. 基数排序 参考:https://mp.weix ...

  4. idea解决Maven jar依赖冲突(四)

    首先点击右侧的MavenProjects打开以下界面: 这个界面是maven的命令界面: 点击这个图标会进入如下界面: 左上角可以缩放,点击线可以取消冲突依赖,红色线为冲突依赖. 上图为无依赖冲突的s ...

  5. Redis入门教程(二)

    推荐阅读: Redis入门教程(一)https://www.cnblogs.com/jichi/p/10285346.html 5. Redis 的数据结构 5.1 Redis 数据结构介绍 redi ...

  6. SSIS-导入Excel文件时记录行号

    SSIS导入Excel时记录行号 1. "Excel源"后添加"脚本组件" 2. "脚本组件"中新增输出列,命名为"RowNumb ...

  7. 【RL-TCPnet网络教程】第40章 RL-TCPnet之TFTP客户端(精简版)

    第40章      RL-TCPnet之TFTP客户端 本章节为大家讲解RL-TCPnet的TFTP客户端应用,学习本章节前,务必要优先学习第38章的TFTP基础知识.有了这些基础知识之后,再搞本章节 ...

  8. Linux下单机实现Zookeeper集群

    安装配置JAVA开发环境 下载ZOOKEEPER zookeeper下载地址 在下载的zookeeper目录里创建3个文件,zk1,zk2,zk3,用于存放每个集群的数据文件. 并在三个目录下创建da ...

  9. Linux 使用 free 命令查看内存使用情况

    1.free 命令的选项 使用 free 命令查看服务器内存使用情况. free [-b|-k|-m|-g|-h] [-l] [-o] [-t] [-s delay] [-c count] [-V] ...

  10. React 与 React-Native 使用同一个 meteor 后台

    meteor 可以快速构建 pc,移动端,桌面端应用. 最大的优点是:数据库的数据发生变化时,可以实时推送到前端,非常适用于实时展示的应用开发. 在 react,react-native 应用中,可以 ...