返回列表 发新帖

[科技] (建议收藏) | Spring Boot集成WebSocket

[复制链接]

4

主题

20

帖子

24

积分

耍耍新手

Rank: 1

积分
24
发表在  2019-9-13 13:57:26 | 显示全部楼层 | 阅读模式
本号重要用于分享企业中常用的技术,更加侧重于实用,欢迎关注,便于欣赏别的更多实用的汗青文章。

一:HTTP和WebSocket

HTTP : 客户端 -> 服务器端
传统客户端(欣赏器)向服务器获取数据只能利用http主动向服务器拉数据,由于http只能从客户端发起哀求,没办法从服务器端发起哀求。项目中如果要即时的获取消息就只能写个定时器,每隔几秒钟去向服务器发一个http哀求,获取最新的数据。http固然是短连接,但是定时每隔几秒钟去定时发哀求,会不停的占用哀求,当客户端从服务端没有拉取到数据时,此时这次连接就显的浪费。
WebSocket: 客户端  服务器端
WebSocket与http最大的不同就是客户端可以主动向服务器端拉取数据,服务器端也可以主动向客户端推送数据,当客户端启动的时候会起首建立一个长连接,当必要的时候服务器端就可以通过该长连接向客户端推送数据。
HTTP与WebSocket的区别

  • HTTP接纳http协议,WebSocket接纳ws协议
  • HTTP是短连接,连接相应后即断开;WebSocket是长连接(因是长连接以是同时连接的数量不能太大)
  • HTTP只能客户端向服务器发送哀求,不能服务器端向客户端发起哀求;WebSocket都可以
WebSocket常见利用场景

  • 谈天
  • 服务器端向客户端推送消息(如常见的右下角向上弹出个消息、新的未读消息数量等)
二:示例

135951i2fqi6da2kuq2tkz.jpg


1. pom.xml

org.springframework.boot
spring-boot-starter-thymeleaf


org.springframework.boot
spring-boot-starter-web


org.springframework.boot
spring-boot-starter-websocket


com.alibaba
fastjson
1.2.51


org.projectlombok
lombok
true

2. Configuration
/**
* 开启WebSocket支持
*/
@Configuration
public class WebSocketConfiguration {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3. WebSocketServer
@Data
@ToString
@RequiredArgsConstructor
public class Payload {
private String from;
private String to;
private String content;
}
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thymeleaf.util.DateUtils;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* ServerEndpoint 用户定义客户端连接的地点, 可以在路径上指定路径参数
*/
@Slf4j
@Component
@ServerEndpoint("/websocket/{userId}")
public class WebSocketServer {
/** 在线连接数量 */
private static final AtomicInteger onlineCount = new AtomicInteger(0);
private static ConcurrentHashMap sessionMap = new ConcurrentHashMap();
/** 当前连接的用户id */
private String userId;
/**
* 连接成功建立时调用该方法
* @param session
* @param userId
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
sessionMap.put(userId, session);
this.userId = userId;
int currentOnlineCount = onlineCount.incrementAndGet();
log.info("{} 连接创建成功,当前用户id为{}, 当前在线人数{}", now() , userId, currentOnlineCount);
JSONObject succesJson = new JSONObject();
succesJson.put("from", "服务器");
succesJson.put("to", userId);
succesJson.put("content", "WebSocket服务器连接成功!");
sendMessage(session, succesJson.toJSONString());
}
@OnMessage
public void onMessage(String message, Session session) {
sendMessage(session, message);
}
@OnError
public void onError(Session session, Throwable throwable) {
throwable.printStackTrace();
log.error(throwable.toString());
}
@OnClose
public void onClose(Session session) {
sessionMap.remove(this.userId);
int currentOnlineCount = onlineCount.decrementAndGet();
log.info("用户:{} 退出连接,当前连接数为:{}", this.userId, currentOnlineCount);
}
/**
* 向指定会话发送消息
* @param session 当前会话session
* @param message 消息内容
*/
public void sendMessage(Session session, String message) {
Payload payload = JSONObject.parseObject(message, Payload.class);
String toUserId = payload.getTo();
Session targetSession = sessionMap.get(toUserId);
if (targetSession == null) {
log.info("目标用户{} 已退出连接", toUserId);
return;
}
try {
targetSession.getBasicRemote().sendText(now() + " " + message);
} catch (IOException e) {
log.error("发送消息失败, 失败缘故原由为:{}", e);
}
}
/**
* 向指定用户发送消息
* @param userId 用户id
* @param message 消息
*/
public void sendMessage(String userId, String message) {
Session session = sessionMap.get(userId);
if (session != null) {
JSONObject succesJson = new JSONObject();
succesJson.put("from", "服务器");
succesJson.put("to", userId);
succesJson.put("content", message);
this.sendMessage(session, succesJson.toJSONString());
} else {
log.info("用户{} 已退出连接,无法发送消息", userId);
}
}
private String now() {
return DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss", Locale.CHINESE);
}
}
4. Controller
@Controller
@RequestMapping("/im")
public class WebSocketController {
@Autowired
private WebSocketServer webSocketServer;
@RequestMapping("/users/{from}/{to}")
public ModelAndView index(@PathVariable("from") String from, @PathVariable("to") String to){
ModelAndView modelAndView = new ModelAndView("index");
modelAndView.addObject("from", from);
modelAndView.addObject("to", to);
return modelAndView;
}
/**
* 模拟服务器端向客户端推送消息
* @param userId
* @param message
*/
@ResponseBody
@RequestMapping("/push/{userId}")
public void mockPushMessageToClient(@PathVariable String userId, String message) {
webSocketServer.sendMessage(userId, message);
}
}
5. html
templates/index.html




Title




发送消息
连接WebSocket(只需执行一次)




135951nid49f6r7gf63dmd.jpg


本号重要用于分享企业中常用的技术,更加侧重于实用,欢迎关注,便于欣赏别的更多实用的汗青文章。



声明:信息来源于主流新闻平台,如有侵权联系管理员删除(川渝耍耍网 www.cysua.com)。
回复

使用道具 举报

4

主题

20

帖子

24

积分

耍耍新手

Rank: 1

积分
24
发表于 2019-9-13 16:52:06 | 显示全部楼层
路过
回复

使用道具 举报

4

主题

15

帖子

19

积分

耍耍新手

Rank: 1

积分
19
发表于 6 天前 | 显示全部楼层
向楼主学习
回复

使用道具 举报

0

主题

17

帖子

17

积分

耍耍新手

Rank: 1

积分
17
发表于 5 天前 | 显示全部楼层
占坑编辑ing
回复

使用道具 举报

8

主题

21

帖子

29

积分

耍耍新手

Rank: 1

积分
29
发表于 3 天前 | 显示全部楼层
支持,赞一个
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表