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. MySQL5.7下面,误操作导致的drop table db1.tb1; 的恢复方法:

    MySQL5.7下面,误操作导致的drop table db1.tb1; 的恢复方法: 0.停业务数据写入.[iptables封禁] 1.从备份服务器上拉取最新的一个全备文件,恢复到一个临时的服务器上 ...

  2. openlayers4 入门开发系列之图层控制(附源码下载)

    前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...

  3. Intellij idea 离线安装activiti工作流插件

    想在Intellij idea上安装一个activiti插件玩玩,由于网络环境原因,不能使用网上已有的在线搜索acti bpm并安装的方式.也在网上找了好久没找到离线安装的方式.自己摸索了一下装好了, ...

  4. django中出现 错误 Errno 10053

    django中出现 错误 Errno 10053 pycharm里出现下面错误File "C:\Python27\lib\socket.py", line 307, in flus ...

  5. sql server2005安装时报 ‘服务无法启动’

    SQL server服务无法启动的原因分析: 在安装SQL 2005标准版(不多于四个CPU)和企业版(无限制)时,CPU的总核数必须是2的n次方.即核心数为1,2,4,8,16,32依次类推.因BL ...

  6. MonacoEditor 主动触发代码提示功能

    MonacoEditor是微软提供的代码编辑器 vscode即是使用它作为编辑器. 它的开发语言是ts,可以嵌入到浏览器中. 代码提示或者说代码补全功能是我们经常需要定制的部分. 目前它提供的快捷键是 ...

  7. .NET+PostgreSQL实践与避坑指南

    简介 .NET+PostgreSQL(简称PG)这个组合我已经用了蛮长的一段时间,感觉还是挺不错的.不过大多数人说起.NET平台,还是会想起跟它“原汁原味”配套的Microsoft SQL Serve ...

  8. Fork/Jion框架详解

    ◆Fork/Jion框架可以干什么◆ 如果你要处理1万条数据,但是你的能力暂时还不够,一个简单快捷的办法就是你可以把每次只处理100条,等到处理100次之后再把所有的结果聚合起来你就处理完了这1万条数 ...

  9. js node.js 编写命令工具demo

    1 创建文件夹cli-demo 2 执行npm init  3 创建cli.js 文件 内容如下: //js文件变成可执行文件 #!/usr/bin/env node console.log(&quo ...

  10. 聊聊Mysql索引和redis跳表

    摘要 面试时,交流有关mysql索引问题时,发现有些人能够涛涛不绝的说出B+树和B树,平衡二叉树的区别,却说不出B+树和hash索引的区别.这种一看就知道是死记硬背,没有理解索引的本质.本文旨在剖析这 ...