前言:
现时我们对“netmvcwebsocket”大致比较着重,朋友们都想要知道一些“netmvcwebsocket”的相关知识。那么小编在网上汇集了一些对于“netmvcwebsocket””的相关文章,希望咱们能喜欢,大家快快来学习一下吧!实现推送功能方案
在以前的消息推送机制中,用的都是 Ajax 轮询(polling),在特定的时间间隔由浏览器自动发出请求,将服务器的消息主动的拉回来,这种方式是非常消耗资源的,因为它本质还是http请求,而且显得非常笨拙。而WebSocket 在浏览器和服务器完成一个握手的动作,在建立连接之后,服务器可以主动传送数据给客户端,客户端也可以随时向服务器发送数据。
核心技术介绍:
WebSocket协议概述
WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。
WebSocket是真正实现了全双工通信的服务器向客户端推的互联网技术。
它是一种在单个TCP连接上进行全双工通讯协议。Websocket通信协议与2011年倍IETF定为标准RFC 6455,Websocket API被W3C定为标准。
WebSocket协议的优越性
以前不管使用HTTP轮询或使用TCP长连接等方式制作在线聊天系统,都有天然缺陷,随着Html5的兴起,其中有一个新的协议WebSocket protocol,可实现浏览器与服务器全双工通信(full-duplex),它可以做到:浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。这个新的协议的特点正好适合这种在线即时通信。
WebSocket协议实现方式:
它是一种长链接,只能通过一次请求来初始化链接,然后所有的请求和响应都是通过这个TCP链接进行通讯,这意味着它是一种基于事件驱动,异步的消息机制
服务端-服务器的支持
新版本的应用服务器新增了支持的API,如Tomcat 7.0.47+等
整体框架介绍
服务端:Maven+spring mvc+Spring WebSocket+jQuery+Gson
客户端:html5的WebSocket的api
图解:
Maven环境的搭建(略)
maven的项目之前一定先配置好maven的环境!
引入pom.xml配置:
<project xmlns="" xmlns:xsi="" xsi:schemaLocation=" ">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itcast.projects</groupId>
<artifactId>chatroomdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>chatroomdemo</name>
<description>聊天室的demo</description>
<!-- 自定义属性管理 -->
<properties>
<!-- 编译等所有操作使用utf-8编码 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 统一版本维护管理 -->
<spring.version>4.2.8.RELEASE</spring.version>
<servlet.version>3.1.0</servlet.version>
<jsp.version>2.0</jsp.version>
<gson.version>2.7</gson.version>
<junit.version>4.12</junit.version>
</properties>
<!-- 依赖管理 -->
<dependencies>
<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>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
</dependencies>
<!-- 构建信息管理 -->
<build>
<finalName>chatroom</finalName>
<plugins>
<!-- 编译的jdk版本 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/chatroom</path>
<uriEncoding>UTF-8</uriEncoding>
<finalName>chatroom</finalName>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
</build>
</project>
项目API讲解客户端的API
客户端如何去连接服务端?
客户端需要主动握手,
需要使用html5的一些代码
// 创建一个Socket实例(需要浏览器支持)ws:WebSocket协议地址开头
var socket = new WebSocket('ws://localhost:8080');
//下面有几个回调函数,自动调用(什么时候调用?)
// 打开Socket
socket.onopen = function(event) {
//握手成功后,会自动调用该函数
}
// 监听消息:用来获取服务端的消息
socket.onmessage = function(event) {
console.log('Client received a message',event);
};
// 监听Socket的关闭
socket.onclose = function(event) {
console.log('Client notified socket has closed',event);
};
// 关闭Socket....
//socket.close()
};
服务端的API
spring WebSocket:jee:WebSocket的封装。
用:只需要知道搭建步骤即可。
@Component("webSocketConfig")
//配置开启WebSocket服务用来接收ws请求
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
//注入处理器
@Autowired
private ChatWebSocketHandler webSocketHandler;
@Autowired
private ChatHandshakeInterceptor chatHandshakeInterceptor;
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//添加一个处理器还有定义处理器的处理路径
registry.addHandler(webSocketHandler, "/ws").addInterceptors(chatHandshakeInterceptor);
/*
* 在这里我们用到.withSockJS(),SockJS是spring用来处理浏览器对websocket的兼容性,
* 目前浏览器支持websocket还不是很好,特别是IE11以下.
* SockJS能根据浏览器能否支持websocket来提供三种方式用于websocket请求,
* 三种方式分别是 WebSocket, HTTP Streaming以及 HTTP Long Polling
*/
registry.addHandler(webSocketHandler, "/ws/sockjs").addInterceptors(chatHandshakeInterceptor).withSockJS();
}
}
/**
* websocket的链接建立是基于http握手协议,我们可以添加一个拦截器处理握手
@Component
public class ChatHandshakeInterceptor implements HandshakeInterceptor{
/**
* 握手之前,若返回false,则不建立链接
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
//如果用户已经登录,允许聊天
if(session.getAttribute("loginUser")!=null){
//获取登录的用户
User loginUser=(User)session.getAttribute("loginUser") ;
//将用户放入socket处理器的会话(WebSocketSession)中
attributes.put("loginUser", loginUser);
System.out.println("Websocket:用户[ID:" + (loginUser.getId() + ",Name:"+loginUser.getNickname()+"]要建立连接"));
}else{
//用户没有登录,拒绝聊天
//握手失败!
System.out.println("--------------握手已失败...");
return false;
}
}
System.out.println("--------------握手开始...");
return true;
}
/**
* 握手之后
*/
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception exception) {
System.out.println("--------------握手成功啦...");
}
}
@Component("chatWebSocketHandler")
public class ChatWebSocketHandler implements WebSocketHandler {
//在线用户的SOCKETsession(存储了所有的通信通道)
public static final Map<String, WebSocketSession> USER_SOCKETSESSION_MAP;
//存储所有的在线用户
static {
USER_SOCKETSESSION_MAP = new HashMap<String, WebSocketSession>();
}
/**
* webscoket建立好链接之后的处理函数--连接建立后的准备工作
*/
@Override
public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
//将当前的连接的用户会话放入MAP,key是用户编号
User loginUser=(User) webSocketSession.getAttributes().get("loginUser");
USER_SOCKETSESSION_MAP.put(loginUser.getId(), webSocketSession);
//群发消息告知大家
Message msg = new Message();
msg.setText("风骚的【"+loginUser.getNickname()+"】踩着轻盈的步伐来啦。。。大家欢迎!");
msg.setDate(new Date());
//获取所有在线的WebSocketSession对象集合
Set<Entry<String, WebSocketSession>> entrySet = USER_SOCKETSESSION_MAP.entrySet();
//将最新的所有的在线人列表放入消息对象的list集合中,用于页面显示
for (Entry<String, WebSocketSession> entry : entrySet) {
msg.getUserList().add((User)entry.getValue().getAttributes().get("loginUser"));
}
//将消息转换为json
TextMessage message = new TextMessage(GsonUtils.toJson(msg));
//群发消息
sendMessageToAll(message);
}
@Override
/**
* 客户端发送服务器的消息时的处理函数,在这里收到消息之后可以分发消息
*/
//处理消息:当一个新的WebSocket到达的时候,会被调用(在客户端通过Websocket API发送的消息会经过这里,然后进行相应的处理)
public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> message) throws Exception {
//如果消息没有任何内容,则直接返回
if(message.getPayloadLength()==0)return;
//反序列化服务端收到的json消息
Message msg = GsonUtils.fromJson(message.getPayload().toString(), Message.class);
msg.setDate(new Date());
//处理html的字符,转义:
String text = msg.getText();
//转换为HTML转义字符表示
String htmlEscapeText = HtmlUtils.htmlEscape(text);
msg.setText(htmlEscapeText);
System.out.println("消息(可存数据库作为历史记录):"+message.getPayload().toString());
//判断是群发还是单发
if(msg.getTo()==null||msg.getTo().equals("-1")){
//群发
sendMessageToAll(new TextMessage(GsonUtils.toJson(msg)));
}else{
//单发
sendMessageToUser(msg.getTo(), new TextMessage(GsonUtils.toJson(msg)));
}
}
@Override
/**
* 消息传输过程中出现的异常处理函数
* 处理传输错误:处理由底层WebSocket消息传输过程中发生的异常
*/
public void handleTransportError(WebSocketSession webSocketSession, Throwable exception) throws Exception {
// 记录日志,准备关闭连接
System.out.println("Websocket异常断开:" + webSocketSession.getId() + "已经关闭");
//一旦发生异常,强制用户下线,关闭session
if (webSocketSession.isOpen()) {
webSocketSession.close();
}
//群发消息告知大家
Message msg = new Message();
msg.setDate(new Date());
//获取异常的用户的会话中的用户编号
User loginUser=(User)webSocketSession.getAttributes().get("loginUser");
//获取所有的用户的会话
Set<Entry<String, WebSocketSession>> entrySet = USER_SOCKETSESSION_MAP.entrySet();
//并查找出在线用户的WebSocketSession(会话),将其移除(不再对其发消息了。。)
for (Entry<String, WebSocketSession> entry : entrySet) {
if(entry.getKey().equals(loginUser.getId())){
msg.setText("万众瞩目的【"+loginUser.getNickname()+"】已经退出。。。!");
//清除在线会话
USER_SOCKETSESSION_MAP.remove(entry.getKey());
//记录日志:
System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
break;
}
}
//并查找出在线用户的WebSocketSession(会话),将其移除(不再对其发消息了。。)
for (Entry<String, WebSocketSession> entry : entrySet) {
msg.getUserList().add((User)entry.getValue().getAttributes().get("loginUser"));
}
TextMessage message = new TextMessage(GsonUtils.toJson(msg));
sendMessageToAll(message);
}
@Override
/**
* websocket链接关闭的回调
* 连接关闭后:一般是回收资源等
*/
public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
// 记录日志,准备关闭连接
System.out.println("Websocket正常断开:" + webSocketSession.getId() + "已经关闭");
//群发消息告知大家
Message msg = new Message();
msg.setDate(new Date());
//获取异常的用户的会话中的用户编号
User loginUser=(User)webSocketSession.getAttributes().get("loginUser");
Set<Entry<String, WebSocketSession>> entrySet = USER_SOCKETSESSION_MAP.entrySet();
//并查找出在线用户的WebSocketSession(会话),将其移除(不再对其发消息了。。)
for (Entry<String, WebSocketSession> entry : entrySet) {
if(entry.getKey().equals(loginUser.getId())){
//群发消息告知大家
msg.setText("万众瞩目的【"+loginUser.getNickname()+"】已经有事先走了,大家继续聊...");
//清除在线会话
USER_SOCKETSESSION_MAP.remove(entry.getKey());
//记录日志:
System.out.println("Socket会话已经移除:用户ID" + entry.getKey());
break;
}
}
//并查找出在线用户的WebSocketSession(会话),将其移除(不再对其发消息了。。)
for (Entry<String, WebSocketSession> entry : entrySet) {
msg.getUserList().add((User)entry.getValue().getAttributes().get("loginUser"));
}
TextMessage message = new TextMessage(GsonUtils.toJson(msg));
sendMessageToAll(message);
}
@Override
/**
* 是否支持处理拆分消息,返回true返回拆分消息
*/
//是否支持部分消息:如果设置为true,那么一个大的或未知尺寸的消息将会被分割,并会收到多次消息(会通过多次调用方法handleMessage(WebSocketSession, WebSocketMessage). )
//如果分为多条消息,那么可以通过一个api:org.springframework.web.socket.WebSocketMessage.isLast() 是否是某条消息的最后一部分。
//默认一般为false,消息不分割
public boolean supportsPartialMessages() {
return false;
}
/**
*
* 说明:给某个人发信息
* @param id
* @param message
* @throws IOException
*
*/
private void sendMessageToUser(String id, TextMessage message) throws IOException{
//获取到要接收消息的用户的session
WebSocketSession webSocketSession = USER_SOCKETSESSION_MAP.get(id);
if (webSocketSession != null && webSocketSession.isOpen()) {
//发送消息
webSocketSession.sendMessage(message);
}
}
/**
*
* 说明:群发信息:给所有在线用户发送消息
* @autho
*
*/
private void sendMessageToAll(final TextMessage message){
//对用户发送的消息内容进行转义
//获取到所有在线用户的SocketSession对象
Set<Entry<String, WebSocketSession>> entrySet = USER_SOCKETSESSION_MAP.entrySet();
for (Entry<String, WebSocketSession> entry : entrySet) {
//某用户的WebSocketSession
final WebSocketSession webSocketSession = entry.getValue();
//判断连接是否仍然打开的
if(webSocketSession.isOpen()){
//开启多线程发送消息(效率高)
new Thread(new Runnable() {
public void run() {
try {
if (webSocketSession.isOpen()) {
webSocketSession.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
}
源码:转发,关注,私信websocket即可
标签: #netmvcwebsocket