java聊天服务器架构_基于Spring 4.0 的 Web Socket 聊天室/游戏服务端简单架构

在现在很多业务场景(比如聊天室),又或者是手机端的一些online游戏,都需要做到实时通信,那怎么来进行双向通信呢,总不见得用曾经很破旧的ajax每隔10秒或者每隔20秒来请求吧,我的天呐(

dd4122db3d6206a74fa1de5484135a23.png),这尼玛太坑了

跟webservice来相比,Web Socket可以做到保持长连接,或者说强连接,一直握手存在两端可以互相发送消息互相收到消息,而webservice是一次性的,你要我响应就必须要请求我一次(黄盖:“请鞭挞我吧!”)

注:浏览器需要使用高版本的chrome或者Firefox,Tomcat使用8

先来了解一下基本概念

一、WebSocket是HTML5出的,是一种协议,也就是说原版的HTTP协议没有变化的,又或者说这两者压根就是不一样的东西,HTTP本身就不支持强连接

二、Websocket是什么样的协议,具体有什么优点

首先,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。

举个栗子吧,简单来说

1) HTTP的生命周期通过Request来界定,也就是一个Request 对应一个Response,或者多个Request 对应多个Response,

也就是说request对应的response数量是恒定不变的。而且这个response也是被动的,不能主动发起,必须有request才会有response

那么Websocket究竟是啥玩意呢

首先Websocket是基于HTTP协议的,或者说引用了HTTP的协议来完成一小部分的握手

简单来说,客服的发起请求到服务端,服务端找到对应的小弟(服务助理),找到好,这个小弟就会一直和老大保持联系,为老大服务

三、Websocket的作用

曾经接触WebSocket之前,我接触过ajax轮询以及long poll ,先来说说这2个概念,因为至今还有一些小项目是这么做的

ajax轮询:

原理非常简单,JS控制让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息,有的话就响应给客户端

以此循环获取后端的数据,同时浏览器又不需要刷新

简单的例子:OA首页显示流程,每个几秒刷新看看有没有需要处理的新流程出现

long poll:

long poll 其实原理跟 ajax轮询 差不多,都是采用循环的方式,不过采取的手段不太友好,是阻塞模型,客户端发起请求后,如果没响应,就一直不返回Response,直到有响应才返回,返回完之后,客户端再次建立连接,如此循环往复不亦乐乎。。。

从上面这两种方式看出他们都是在不断地建立HTTP连接,然后等待服务器处理,这样显得十分被动

那么缺点也随之而来:

这两种形式非常消耗资源,性能也不不好

好!接下来说说Websocket

Websocket的出现,使得资源不需要像之前那种方式那么浪费

它非常主动,服务端就可以主动推送信息给客户端

所以,只需建立一次HTTP请求,就可以做到源源不断的信息传送了。(就像你在手机上玩ol游戏,一开始建立连接后,你就一直保持在线,除非你断线再连)

下面贴出我的代码片段以及github地址

功能点:

spring websocket chating room

使用spring websocket实现聊天室基本功能

1.群发消息给所有人

2.悄悄话给某个人

效果:

5abed846250c80e4024f64a7f3ccb459.png

主要代码:

pom.xml引入必要的库

1 <?xml version="1.0" encoding="UTF-8"?>

2

3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

4 4.0.0

5 com.lee

6 websocket

7 maven-spring-websocket-01

8 war

9 1.0.0-BUILD-SNAPSHOT

10

11

12

13 1.7

14 UTF-8

15 UTF-8

16

17 4.0.0.RELEASE

18

19 4.11

20

21

22 1.0.13

23 1.7.7

24

25

26

27

28

29 org.springframework

30 spring-core

31 ${spring.version}

32

33

34

35 org.springframework

36 spring-web

37 ${spring.version}

38

39

40

41 org.springframework

42 spring-webmvc

43 ${spring.version}

44

45

46

47

48 jstl

49 jstl

50 1.2

51

52

53

54

55 org.springframework

56 spring-test

57 ${spring.version}

58 test

59

60

61

62

63 org.springframework

64 spring-jdbc

65 ${spring.version}

66

67

68

69 junit

70 junit

71 4.8.2

72 test

73

74

75

76

77 org.springframework

78 spring-websocket

79 ${spring.version}

80

81

82 org.springframework

83 spring-messaging

84 ${spring.version}

85

86

87

88

89 com.fasterxml.jackson.core

90 jackson-databind

91 2.3.0

92

93

94

95 commons-fileupload

96 commons-fileupload

97 1.2.2

98

99

100 commons-io

101 commons-io

102 2.2

103

104

105

106

107 org.slf4j

108 slf4j-api

109 ${slf4j.version}

110 compile

111

112

113 ch.qos.logback

114 logback-classic

115 ${logback.version}

116 runtime

117

118

119

120

121 com.alibaba

122 druid

123 1.0.4

124

125

126

127

128 mysql

129 mysql-connector-java

130 5.1.29

131

132

133

134

135

136

137

138 org.apache.maven.plugins

139 maven-compiler-plugin

140

141 1.7

142 1.7

143

144

145

146

147

148

主要结构

836c99db7269009790f9ac8078614e41.png

HandshakeInterceptor.java

1 packagecom.lee.websocket;2

3 importjava.util.Map;4

5 importjavax.servlet.http.HttpSession;6

7 importorg.springframework.http.server.ServerHttpRequest;8 importorg.springframework.http.server.ServerHttpResponse;9 importorg.springframework.http.server.ServletServerHttpRequest;10 importorg.springframework.web.socket.WebSocketHandler;11

12 public class HandshakeInterceptor implementsorg.springframework.web.socket.server.HandshakeInterceptor {13

14 //进入hander之前的拦截

15 @Override16 public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map map) throwsException {17 if (request instanceofServletServerHttpRequest) {18 ServletServerHttpRequest servletRequest =(ServletServerHttpRequest) request;19

20 String clientName = (String)servletRequest.getServletRequest().getParameter("name");21 System.out.println(clientName);22

23 HttpSession session = servletRequest.getServletRequest().getSession(true);24 //String userName = "lee";

25 if (session != null) {26 //使用userName区分WebSocketHandler,以便定向发送消息27 //String clientName = (String) session.getAttribute("WEBSOCKET_USERNAME");

28 map.put("WEBSOCKET_USERNAME", clientName);29 }30 }31 return true;32 }33

34 @Override35 public voidafterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {36

37 }38

39 }

HomeController.java

1 packagecom.lee.websocket;2

3 importjava.text.DateFormat;4 importjava.util.Date;5 importjava.util.Locale;6

7 importorg.slf4j.Logger;8 importorg.slf4j.LoggerFactory;9 importorg.springframework.stereotype.Controller;10 importorg.springframework.ui.Model;11 importorg.springframework.web.bind.annotation.RequestMapping;12 importorg.springframework.web.bind.annotation.RequestMethod;13

14 /**

15 * Handles requests for the application home page.16 */

17 @Controller18 public classHomeController {19

20 private static final Logger logger = LoggerFactory.getLogger(HomeController.class);21

22 /**

23 * Simply selects the home view to render by returning its name.24 */

25 @RequestMapping(value = "/", method =RequestMethod.GET)26 publicString home(Locale locale, Model model) {27 logger.info("Welcome home! The client locale is {}.", locale);28

29 Date date = newDate();30 DateFormat dateFormat =DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);31

32 String formattedDate =dateFormat.format(date);33

34 model.addAttribute("serverTime", formattedDate );35

36 return "home";37 }38

39 @RequestMapping(value = "/chat", method =RequestMethod.GET)40 publicString chat(Locale locale, Model model) {41 return "chat";42 }43

44 }

WebSocketConfig.java

1 packagecom.lee.websocket;2

3 importorg.springframework.context.annotation.Configuration;4 importorg.springframework.web.socket.config.annotation.EnableWebSocket;5 importorg.springframework.web.socket.config.annotation.WebSocketConfigurer;6 importorg.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;7

8 @Configuration9 @EnableWebSocket//开启websocket

10 public class WebSocketConfig implementsWebSocketConfigurer {11 @Override12 public voidregisterWebSocketHandlers(WebSocketHandlerRegistry registry) {13 registry.addHandler(new WebSocketHander(),"/echo").addInterceptors(new HandshakeInterceptor()); //支持websocket 的访问链接

14 registry.addHandler(new WebSocketHander(),"/sockjs/echo").addInterceptors(new HandshakeInterceptor()).withSockJS(); //不支持websocket的访问链接

15 }16 }

WebSocketHander.java

1 packagecom.lee.websocket;2

3 importjava.io.IOException;4 importjava.util.ArrayList;5

6 importorg.slf4j.Logger;7 importorg.slf4j.LoggerFactory;8 importorg.springframework.web.socket.CloseStatus;9 importorg.springframework.web.socket.TextMessage;10 importorg.springframework.web.socket.WebSocketHandler;11 importorg.springframework.web.socket.WebSocketMessage;12 importorg.springframework.web.socket.WebSocketSession;13

14 public class WebSocketHander implementsWebSocketHandler {15 private static final Logger logger = LoggerFactory.getLogger(WebSocketHander.class);16

17 private static final ArrayList users = new ArrayList<>();18

19 //初次链接成功执行

20 @Override21 public void afterConnectionEstablished(WebSocketSession session) throwsException {22 logger.debug("链接成功......");23 users.add(session);24 String userName = (String) session.getHandshakeAttributes().get("WEBSOCKET_USERNAME");25 if(userName!= null){26 session.sendMessage(new TextMessage("欢迎来到Nathan的聊天室,我们开始聊天吧!~"));27 }28 }29

30 //接受消息处理消息

31 @Override32 public void handleMessage(WebSocketSession session, WebSocketMessage> webSocketMessage) throwsException {33 String clientName = (String) session.getHandshakeAttributes().get("WEBSOCKET_USERNAME");34

35 clientName = "" + clientName + "";36

37 String msg =webSocketMessage.getPayload().toString();38 String charter = "";39

40 String msgs[] = msg.split("\\|");41 if (msgs.length > 1) {42 msg = msgs[1];43 charter = msgs[0];44 sendMessageToUser(charter, new TextMessage(clientName + " 悄悄地对你说 :" +msg));45 } else{46 sendMessageToUsers(new TextMessage(clientName + " 说:" +msg));47 }48

49 }50

51 @Override52 public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throwsException {53 if(webSocketSession.isOpen()){54 webSocketSession.close();55 }56 logger.debug("链接出错,关闭链接......");57 users.remove(webSocketSession);58 }59

60 @Override61 public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throwsException {62 logger.debug("链接关闭......" +closeStatus.toString());63 users.remove(webSocketSession);64 }65

66 @Override67 public booleansupportsPartialMessages() {68 return false;69 }70

71 /**

72 * 给所有在线用户发送消息73 *74 *@parammessage75 */

76 public voidsendMessageToUsers(TextMessage message) {77 for(WebSocketSession user : users) {78 try{79 if(user.isOpen()) {80 user.sendMessage(message);81 }82 } catch(IOException e) {83 e.printStackTrace();84 }85 }86 }87

88 /**

89 * 给某个用户发送消息90 *91 *@paramuserName92 *@parammessage93 */

94 public voidsendMessageToUser(String userName, TextMessage message) {95 for(WebSocketSession user : users) {96 if (user.getHandshakeAttributes().get("WEBSOCKET_USERNAME").equals(userName)) {97 try{98 if(user.isOpen()) {99 user.sendMessage(message);100 }101 } catch(IOException e) {102 e.printStackTrace();103 }104 break;105 }106 }107 }108 }

Person.java

1 packagecom.lee.websocket.entity;2

3 public classPerson {4

5 private intage;6 privateString name;7 privateString sex;8

9 public intgetAge() {10 returnage;11 }12 public void setAge(intage) {13 this.age =age;14 }15 publicString getName() {16 returnname;17 }18 public voidsetName(String name) {19 this.name =name;20 }21 publicString getSex() {22 returnsex;23 }24 public voidsetSex(String sex) {25 this.sex =sex;26 }27

28 }

chat.jsp

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

webSocket测试

16

17 varchater;18

19 $(function(){20

21 varwebsocket;22 functionconnectServer() {23 var clientName = $("#client_name").val();24 if ("WebSocket" inwindow) {25 websocket = new WebSocket("ws://127.0.0.1:8080/websocket/echo?name=" +clientName);26 } else if ("MozWebSocket" inwindow) {27 alert("MozWebSocket");28 websocket = new MozWebSocket("ws://echo");29 } else{30 alert("SockJS");31 websocket = new SockJS("http://127.0.0.1:8080/websocket/sockjs/echo");32 }33 }34

35 //websocket.onopen = function (evnt) {

36 //$("#tou").html("链接服务器成功!")

37 //};

38 //websocket.onmessage = function (evnt) {

39 //$("#msg").html($("#msg").html() + "
" + evnt.data);

40 //};

41 //websocket.onerror = function (evnt) {

42 //};

43 //websocket.onclose = function (evnt) {

44 //$("#tou").html("与服务器断开了链接!")

45 //}

46

47 $("#conncet_server").bind("click", function() {48 connectServer();49

50 websocket.onopen = function(evnt) {51 $("#tou").html("链接服务器成功!")52 };53 websocket.onmessage = function(evnt) {54 $("#msg").html($("#msg").html() + "
" +evnt.data);55 };56 websocket.onerror = function(evnt) {57 };58 websocket.onclose = function(evnt) {59 $("#tou").html("与服务器断开了链接!")60 }61 });62

63 $("#send").bind("click", function() {64 send();65 });66

67 functionsend(){68 if (websocket != null) {69 var message = document.getElementById("message").value;70

71 if ($.trim(chater) != "") {72 message = chater + "|" +message;73 }74

75 websocket.send(message);76 } else{77 alert("未与服务器链接.");78 }79 }80 });81

82 functionchangeChater(e) {83 chater =$(e).html();84 alert("您将和" + chater + "进行聊天...");85 }86

87

88

89

90

91

92

93

94

95

96

97 连接服务器

98

99

100

101

102

103

104

105

106

107

108 发送

109

110

111

112

113

114

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值