WEB通知和React Native之即时通讯(iOS Android)

react

一,需求分析

1.1,允许服务器主动发送信息给客户端,客户端能监听到并且能接收。

1.2,为了方便同一个系统内的用户可以指定某个用户可以私聊。

1.3,给指定用户或多个用户发送通知。

二,技术介绍

2.1.WebSocket介绍

1,WebSocket 是什么?

  • WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。

  • WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

2,WebSocket 的作用

  • WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
  • HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

其他特点包括:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

3,WebSocket 构造函数

WebSocket 对象作为一个构造函数,用于新建 WebSocket 实例。执行如下语句之后,客户端就会与服务器进行连接。

1 var ws = new WebSocket('ws://localhost:8080');

4,webSocket.readyState

  • CONNECTING:值为0,表示正在连接。
  • OPEN:值为1,表示连接成功,可以通信了。
  • CLOSING:值为2,表示连接正在关闭。
  • CLOSED:值为3,表示连接已经关闭,或者打开连接失败。

5,webSocket.onopen

实例对象的onopen属性,用于指定连接成功后的回调函数。

1 ws.onopen = function () {

2 ws.send('Hello Server!');

3 }

如果要指定多个回调函数,可以使用addEventListener方法。

1 ws.addEventListener('open', function (event) {

2 ws.send('Hello Server!');

3 });

6,webSocket.send()

实例对象的send()方法用于向服务器发送数据。

(1)发送文本

1 ws.send('your message');

(2)发送 Blob 对象

1 var file = document

2 .querySelector('input[type="file"]')

3 .files[0];

4 ws.send(file);

(3)发送 ArrayBuffer 对象

1 // Sending canvas ImageData as ArrayBuffer

2 var img = canvas_context.getImageData(0, 0, 400, 320);

3 var binary = new Uint8Array(img.data.length);

4 for (var i = 0; i < img.data.length; i++) {

5 binary[i] = img.data[i];

6 }

7 ws.send(binary.buffer);

(4)发送json对象

1 var messageObj = {fromUserId:1,message:'您好,jackson影琪',toUserId:2};

2 var messageJson = JSON.stringify(messageObj);

3 ws.send(messageJson);

7,webSocket.onmessage

对象的onmessage属性,用于指定收到服务器数据后的回调函数。

1 ws.onmessage = function(event) {

2 var data = event.data;

3 // 处理数据

4 };

5

6 ws.addEventListener("message", function(event) {

7 var data = event.data;

8 // 处理数据

9 });

服务器数据可能是文本,也可能是二进制数据(blob对象或Arraybuffer对象)

 1 ws.onmessage = function(event){

2 if(typeof event.data === String) {

3 console.log("Received data string");

4 }

5

6 if(event.data instanceof ArrayBuffer){

7 var buffer = event.data;

8 console.log("Received arraybuffer");

9 }

10 }

8,webSocket.onclose

对象的onclose属性,用于指定连接关闭后的回调函数。

 1 ws.onclose = function(event) {

2 var code = event.code;

3 var reason = event.reason;

4 var wasClean = event.wasClean;

5 // handle close event

6 };

7

8 ws.addEventListener("close", function(event) {

9 var code = event.code;

10 var reason = event.reason;

11 var wasClean = event.wasClean;

12 // handle close event

13 });

9,webSocket.onerror

对象的onerror属性,用于指定报错时的回调函数。

1 ws.onerror = function(event) {

2 // handle error event

3 };

4

5 ws.addEventListener("error", function(event) {

6 // handle error event

7 });

2.2,react-native-gifted-chat介绍

  1. messages(Array) - 消息数组,用于展示消息 有特定的格式

     1 {

    2 _id: 1, //消息的ID

    3 text: 'My message', //发送的消息内容

    4 createdAt: new Date(), //发送的时间

    5 user: {/发送方的用户信息

    6 _id: 2, //发送方的ID

    7 name: 'Jackson', //发送方的昵称

    8 avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',, //发送方的头像

    9 },

    10 image: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',

    11 //添加你所需要扩展的键值对

    12 }

  2. user(Object) - 配置用户信息

    1 {

    2 _id: 1, //发送消息需要和配置的id一致 avatar:'https://pic.cnblogs.com/avatar/1040068/20181013100635.png', //头像 若不设置则不显示

    3 name:'jackson影琪', //昵称

    4 }

  3. renderBubble(Function) - 自定义气泡

     1 //气泡

    2 renderBubble(props) {

    3 return (

    4 <Bubble

    5 {...props}

    6 wrapperStyle={{

    7 left: {//对方的气泡

    8 backgroundColor: '#ffffff',

    9 },

    10 right: {//我方的气泡

    11 backgroundColor: '#1fb922',

    12 }

    13 }}

    14 />

    15 );

    16 }

  4. text(String) - 输入框的默认值;默认是undefined
  5. placeholder(String) - 输入框的占位字符
  6. messageIdGenerator(Function) - 为你的新消息自动生成一个id. 默认是用 UUID v4, 由uuid库实现uuid

  7. onSend(Function) - 点击send时的回调
  8. locale(String) -本地化日期
  9. timeFormat(String) - 格式化时间,默认是本地时间,即当前时区的时间
  10. dateFormat(String) - 日期格式化
  11. isAnimated(Bool) - 键盘出现时,是否有动画
  12. loadEarlier(Bool) - 是否显示加载更早的消息按钮 "Load earlier messages"
  13. onLoadEarlier(Function) - 加载更多消息时的回调
  14. isLoadingEarlier(Bool) - 点击加载更早的消息时是否出现转菊花的图标
  15. renderLoading(Function) - 加载页面未加载出来时的页面

     1  //加载更多消息

    2 loadEarlier={self.state.isMore}//

    3 isLoadingEarlier={self.state.isMore}//

    4 renderLoadEarlier={() => {

    5 return (

    6 <Text

    7 onPress={self.onLoadEarlier}

    8 style={[

    9 styles.LookMoreStyle

    10 ]}

    11 >{self.state.moreData}</Text>

    12 );

    13 }}

  16. renderLoadEarlier(Function) - 配置 "Load earlier messages" 加载更早消息的按钮
  17. renderAvatar(Function) - 配置头像,如果设置'null'则头像都不显示

    1 //头像

    2 renderAvatar(props) {

    3 return (

    4 <Avatar

    5 {...props}

    6 />

    7 );

    8 }

  18. showUserAvatar(Bool) - 是否展示自己的头像,默认时false 只展示别人的头像
  19. onPressAvatar(Function(user)) - 点击头像时的回调
  20. renderAvatarOnTop(Bool) 头像显示在顶部还是底部,默认是底部
  21. renderSystemMessage(Function) - 自定义系统消息
  22. onLongPress(Function(context,message)) - 长按消息气泡时的回调,详细可以看github的演示 showActionSheetWithOptions()
  23. inverted(Bool) - 反转消息的显示顺序,默认是true 即消息显示的顺序是否和你message数组的顺序相同
  24. renderMessage(Function) - 自定义消息的内容View
  25. renderMessageText(Function) - 自定义消息的文本
  26. renderMessageImage(Function) - 自定义图片消息
  27. imageProps(Object) - 额外的属性要传递给默认创建的组件rendermessageimage点去去查看文档
  28. lightboxProps(Object) - 额外的属性传递给Modal框(体现在点击图片的Modal)
  29. 点击查看第三方 - Lightbox
  30. renderCustomView(Function) - 在气泡内创建一个自己自定义的视图,即创建自定义的消息
  31. renderDay(Function) - 自定义消息上方的日期
  32. renderTime(Function) - 自定义消息中的时间
  33. renderFooter(Function) - 自定义listView的底部, 例如.'User is typing...'; 点击查看示例 example/App.js for an example
  34. renderChatFooter(Function) - 自定义组件的渲染下messagecontainer(从ListView分开)
  35. renderInputToolbar(Function) - 自定义你的底部工具栏
  36. renderComposer(Function) - 自定义textInput输入框
  37. renderActions(Function) - 自定义输入框左边的按钮
  38. renderSend(Function) -自定义发送按钮;您可以很容易地将子组件传递给原始组件,例如使用自定义图标。
  39. renderAccessory(Function) - 在消息编辑器下面的自定义第二行操作
  40. onPressActionButton\(Function) - 当点击输入框左边的按钮时的回调 (如果设置了 actionSheet将不会执行)
  41. bottomOffset(Integer) - 从屏幕底部的聊天距离(如显示选项卡栏,则非常有用)
  42. minInputToolbarHeight(Integer) - 工具栏的最小高度,默认是44
  43. listViewProps(Object) - 列表的属性,用于扩展你的列表

     1  listViewProps={{

    2 // //ListView/FlatView中标识是否可以加载更多(当现在获取到的数据已经是全部了,不能再继续获取数据了,则设为false,当还有数据可以获取则设为true)

    3 canLoad: true,

    4 //标识现在是否ListView/FlatView现在正在加载(根据这个值来决定是否显示"正在加载的cell")(loadMore()方法进去后设为true,fetch加载完数据后设为false)

    5 isLoadding: false,

    6 //是否显示下拉刷新的cell

    7 ifShowRefresh: true,

    8 //ListView/FlatList是否可以滚动

    9 scrollEnabled: true,

    10 //记录当前加载到了哪一页

    11 page: 1,

    12 onScroll:self._onScroll.bind(this)

    13 }}

  44. textInputProps(Object) - 输入框的属性,用于扩展你的输入框
  45. keyboardShouldPersistTaps(Enum) - 确定键盘在敲击后是否应该保持可见。一个枚举; 详情见 <ScrollView>
  46. onInputTextChanged(Function) - 输入框编辑时的回调
  47. maxInputLength(Integer) - 输入框输入的最多字符数
  48. showAvatarForEveryMessage(Bool) - 默认是false每条消息都显示头像

系统消息格式

1 {

2 _id: 1,

3 text: 'This is a system message',

4 createdAt: new Date(),

5 system: true,

6 // Any additional custom parameters are passed through

7 }

三,即时通讯实现

3.1,实现步骤

第一步:建立链接

 1 componentWillMount() {

2 let self = this;

3 //建立链接

4 ws = new WebSocket('ws://127.0.0.1:8080/websocket/'+str);

5 ws.onopen = (evt) => {

6 // 打开一个连接

7 // console.log('WebSocket==' + evt)

8 alert("连接成功啦")

9 //ws.send('something'); // 发送一个消息

10 };

11 ws.onmessage = (e) => {

12 // }

13 // 接收到了一个消息

14 //alert(JSON.parse(e.data).text)

15 console.log('e.data==' + e.data);

16 };

17

18 ws.onerror = (e) => {

19 // 发生了一个错误

20 console.log('e.message==' + e.message);

21 };

22

23 ws.onclose = (e) => {

24 // 连接被关闭了

25 console.log('e.code===' + e.code, 'e.reason===' + e.reason);

26 };

27 }

第二步:发送消息

 1 onSend(messages = []) {

2 let self = this

3 this.setState(previousState => ({

4 messages: GiftedChat.append(previousState.messages, messages),

5 }))

6 // alert(messages[0].text)

7 this.doSend(messages[0].text)

8 }

9

10 // 发送消息

11 doSend = (message) => {

12 var messageObj = {

13 fromUserId: this.state.userData._id,

14 fromNickName: this.state.userData.name,

15 message: message,

16 toUserId: this.props.Account.id,

17 toNickName: this.props.Account.name,

18 sendTime: new Date()

19

20 };

21 var messageJson = JSON.stringify(messageObj);

22 ws.send(messageJson);

23 }

第三步:接收消息

 1  ws.onmessage = (e) => {

2 // {

3 // _id: 1, //消息的ID

4 // text: 'My message', //发送的消息内容

5 // createdAt: new Date(), //发送的时间

6 // user: {/发送方的用户信息

7 // _id: 2, //发送方的ID

8 // name: 'Jackson', //发送方的昵称

9 // avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',, //发送方的头像

10 // },

11 // }

12 // 接收到了一个消息

13 //alert(JSON.parse(e.data).text)

14 console.log('e.data==' + e.data);

15 }

 

第四步:关闭链接

1 componentWillUnmount() {

2 ws.close()

3 this.setState = (state, callback) => {

4 return;

5 };

6 }

3.2.聊天界面构建

1,使用react-native-gifted-chat,安装

1 npm install react-native-gifted-chat --save

2,引入使用

  1 /**

2 * Created by Jackson on 2018/11/12.

3 * 聊天界面

4 */

5 import React, { PureComponent } from 'react';

6 import {

7 View,

8 Text,

9 StyleSheet,

10 TouchableOpacity,

11 Keyboard,

12 Platform,

13 StatusBar

14 } from 'react-native';

15 //聊天

16 import { GiftedChat, Bubble, Avatar } from 'react-native-gifted-chat'

17 export default class ChatBox extends PureComponent {

18 constructor(props) {

19 super(props);

20 this.renderBubble = this.renderBubble.bind(this);

21 this.renderAvatar = this.renderAvatar.bind(this);

22 this.state = {

23 //聊天

24 messages: [],

25 userData: {

26 _id: 1,

27 name:'jackson影琪',

28 avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',

29 },

30 messageId: 1,

31 }

32

33 }

34 componentDidMount() {

35 let self = this

36 /****************************聊天组件 start **************************************************/

37 setTimeout(function(){

38 self.setState({

39 messages: [

40

41 {

42 _id: 2,

43 text: '微信小程序开发的基本流程',

44 createdAt: new Date('2018-10-25T15:41:00+08:00'),

45 user: {

46 _id: 1,

47 name: 'jackson影琪',

48 avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',

49 },

50 //image: 'https://img2018.cnblogs.com/blog/1040068/201810/1040068-20181024162047704-1159291775.png',

51 },

52 {

53 _id: 1,

54 text: 'Hello jackson影琪',

55 createdAt: new Date('2016-06-07T10:00:00+08:00'),

56 user: {

57 _id: 2,

58 name: 'jackson',

59 avatar: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181101192529807-2132606645.jpg'

60 },

61 image: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',

62 },

63 ],

64 })

65 },2000)

66 /****************************聊天组件 end **************************************************/

67

68 }

69

70

71 /****************************聊天 start **************************************************/

72 onSend(messages = []) {

73 this.setState(previousState => ({

74 messages: GiftedChat.append(previousState.messages, messages),

75 }))

76 // alert(messages[0].text)

77 let self = this

78 self.state.messageId += 2

79 let m = {

80 _id: self.state.messageId,

81 text: '前端知识点总结(HTML)',

82 createdAt: new Date(),

83 user: {

84 _id: 2,

85 name: '',

86 avatar: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181101192529807-2132606645.jpg'

87 },

88 image: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181109115100292-977588541.png',

89 }

90 self.setState(previousState => ({

91 messages: GiftedChat.append(previousState.messages, m),

92 }))

93 }

94 //气泡

95 renderBubble(props) {

96 return (

97 <Bubble

98 {...props}

99 wrapperStyle={{

100 left: {

101 backgroundColor: '#ffffff',

102 },

103 right: {

104 backgroundColor: '#1fb922',

105 }

106 }}

107 />

108 );

109 }

110 //头像

111 renderAvatar(props) {

112 return (

113 <Avatar

114 {...props}

115 />

116 );

117 }

118 /****************************聊天 end **************************************************/

119 render() {

120 let self = this;

121 return (

122 <TouchableOpacity

123 activeOpacity={1}

124 style={{ flex: 1,}}

125 onPress={() => { Keyboard.dismiss() }}

126 >

127

128 {/* //聊天 */}

129 <GiftedChat

130 // onPressAvatar={()=>{alert('Keyboard.dismiss'); Keyboard.dismiss()}}

131 messages={this.state.messages}

132 onSend={messages => this.onSend(messages)}

133 renderBubble={this.renderBubble}//气泡

134 renderAvatar={this.renderAvatar}//头像

135 showUserAvatar={true}//是否显示自己的头像,默认不显示

136 //onLongPress={()=>{alert('onLongPress')}}//长按消息

137 // 输入组件

138 placeholder={'请输入内容'}//输入框占位符

139 // label={'发送'}

140 containerStyle={{ marginBottom: 2 }}//发送按钮

141 children={

142 <View

143 style={[

144 styles.buttonBoxBorder

145 ]}

146 >

147 <Text

148 style={[

149 styles.buttonText,

150 ]}

151 >发送</Text>

152 </View>

153 }

154 // textStyle={{ color: '#70b24e' }}//按钮字的颜色

155 timeFormat={'MM月DD日 HH:mm:ss'}//格式化日前

156 dateFormat={'YYYY年MM月DD日'}

157 // locale={'zh-cn'}

158 isAnimated={true}

159 // renderAvatarOnTop={true}

160 user={this.state.userData}//用户信息

161 />

162 </TouchableOpacity>

163 )

164 }

165

166 }

167

168 const styles = StyleSheet.create({

169 buttonText: {

170 paddingHorizontal: 15,

171 paddingVertical: 5,

172 textAlign: 'center',

173 color: '#fff',

174 fontSize: 14

175 },

176 buttonBoxBorder: {

177 overflow: 'hidden',

178 borderRadius: 5,

179 borderWidth: 1,

180 backgroundColor: "#70b24e",

181 borderColor: "#70b24e",

182 marginRight: 12,

183 marginBottom: 6,

184 },

185 })

效果如下:

3.3,使用的方法

1,下拉加载更多

 1  {/* //聊天 */}

2 <GiftedChat

3 // onPressAvatar={()=>{alert('Keyboard.dismiss'); Keyboard.dismiss()}}

4 messages={this.state.messages}

5 onSend={messages => this.onSend(messages)}//发送消息

6

7

8 ...

9

10

11 //加载更多消息

12 loadEarlier={self.state.isMore}//

13 isLoadingEarlier={self.state.isMore}//

14 renderLoadEarlier={() => {

15 return (

16 <Text

17 onPress={self.onLoadEarlier}

18 style={[

19 styles.LookMoreStyle

20 ]}

21 >{self.state.moreData}</Text>

22 );

23 }}

24

25 listViewProps={{

26 // //ListView/FlatView中标识是否可以加载更多(当现在获取到的数据已经是全部了,不能再继续获取数据了,则设为false,当还有数据可以获取则设为true)

27 canLoad: true,

28 //标识现在是否ListView/FlatView现在正在加载(根据这个值来决定是否显示"正在加载的cell")(loadMore()方法进去后设为true,fetch加载完数据后设为false)

29 isLoadding: false,

30 //是否显示下拉刷新的cell

31 ifShowRefresh: true,

32 //ListView/FlatList是否可以滚动

33 scrollEnabled: true,

34 //记录当前加载到了哪一页

35 page: 1,

36 onScroll:self._onScroll.bind(this)

37 }}

38 />

 1   //加载更早的数据

2 onLoadEarlier = () => {

3 let self = this;

4 self.state.Currentpage += 1;

5 self.setState({

6 isMore: true,

7 moreData: '正在加载更多...'

8 })

9 self.getMessageData()

10 }

11

12 //上拉加载//翻页

13 _onScroll(event) {

14 let self = this

15 let y = event.nativeEvent.contentOffset.y;

16 let height = event.nativeEvent.layoutMeasurement.height;

17 let contentHeight = event.nativeEvent.contentSize.height;

18 if (y + height >= contentHeight - 20 && y > 0 && this.state.contentHeight != contentHeight) {//上啦下一页

19 self.state.contentHeight=contentHeight

20 self.onLoadEarlier()

21

22 }

23 else if (y < 0 || y == 0) {//下拉上一页ios

24

25 }

26 }

2,在消息前后追加消息

1 //prepend(),在父级最前面追加一个子元素 

2 self.setState(previousState => ({

3 messages: GiftedChat.prepend(previousState.messages, ReceivedMessageData),

4 }))

5

6 //append(),在父级最后追加一个子元素

7 this.setState(previousState => ({

8 messages: GiftedChat.append(previousState.messages, messages),

9 }))

3,完整代码

 1  {/* //聊天 */}

2 <GiftedChat

3 // onPressAvatar={()=>{alert('Keyboard.dismiss'); Keyboard.dismiss()}}

4 messages={this.state.messages}

5 onSend={messages => this.onSend(messages)}//发送消息

6 renderBubble={this.renderBubble}//气泡

7 renderAvatar={this.renderAvatar}//头像

8 showUserAvatar={true}// 显示发送方的头像

9 showAvatarForEveryMessage={true}//每条消息都显示头像

10 //onLongPress={()=>{alert('onLongPress')}}

11 // 输入组件

12 placeholder={'请输入内容'}

13 // label={'发送'}

14 containerStyle={{ marginBottom: 2 }}

15 children={

16 <View

17 style={[

18 styles.buttonBoxBorder

19 ]}

20 >

21 <Text

22 style={[

23 styles.buttonText,

24 ]}

25 >发送</Text>

26 </View>

27 }//渲染发送按钮

28 // textStyle={{ color: '#70b24e' }}

29 timeFormat={'MM月DD日 HH:mm:ss'}

30 dateFormat={'YYYY年MM月DD日'}

31 // locale={'zh-cn'}

32 isAnimated={true}

33 // renderAvatarOnTop={true}

34 user={this.state.userData}

35

36 // 系统消息样式

37 wrapperStyle={{ paddingLeft: 12, paddingRight: 12 }}

38 textStyle={{ lineHeight: 20 }}

39 //加载更多消息

40 loadEarlier={self.state.isMore}//

41 isLoadingEarlier={self.state.isMore}//

42 renderLoadEarlier={() => {

43 return (

44 <Text

45 onPress={self.onLoadEarlier}

46 style={[

47 styles.LookMoreStyle

48 ]}

49 >{self.state.moreData}</Text>

50 );

51 }}

52

53 listViewProps={{

54 // //ListView/FlatView中标识是否可以加载更多(当现在获取到的数据已经是全部了,不能再继续获取数据了,则设为false,当还有数据可以获取则设为true)

55 canLoad: true,

56 //标识现在是否ListView/FlatView现在正在加载(根据这个值来决定是否显示"正在加载的cell")(loadMore()方法进去后设为true,fetch加载完数据后设为false)

57 isLoadding: false,

58 //是否显示下拉刷新的cell

59 ifShowRefresh: true,

60 //ListView/FlatList是否可以滚动

61 scrollEnabled: true,

62 //记录当前加载到了哪一页

63 page: 1,

64 onScroll:self._onScroll.bind(this)

65 }}

66 />

效果展示:

注意:

1,如下格式的图片链接不能正常显示

1 avatar: 'http://img3.imgtn.bdimg.com/it/u=1614455141,2952757874&fm=26&gp=0.jpg',

四,后台实现

4.1,Java spring cloud实现

Java 的 web 一般都依托于 servlet 容器。Tomcat、Jetty、Resin等。Spring 框架对 WebSocket 也提供了支持。

1.Spring 对于 WebSocket 的支持基于下面的 jar 包:

1  <dependency>

2 <groupId>javax.websocket</groupId>

3 <artifactId>spring-websocket</artifactId>

4 <version>${spring.version}</version>

5 </dependency>

2.Spring 在收到 WebSocket 事件时,会自动调用事件对应的方法。

 1  import javax.websocket.*;

2 import javax.websocket.server.PathParam;

3 import javax.websocket.server.ServerEndpoint;

4 import java.io.IOException;

5 import java.util.Date;

6 import java.util.Map;

7 import java.util.concurrent.ConcurrentHashMap;

8 public class WebSocketService {

9

10 private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketService.class);

11 // ...

12

13

14 }

3.完整代码实现

 1 ...

2

3 public class WebSocketService {

4 private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketService.class);

5

6 public static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();

7

8 private HcAppchatService hcAppchatService = SpringContextHandler.getBean(HcAppchatService.class);

9 /**

10 * 建立连接后触发的回调

11 */

12 @OnOpen

13 public void onOpen(@PathParam("userId") String userId, Session session) {

14 LOGGER.info("聊天打开onOpen:userId={}", userId);

15 if (sessionMap == null) {

16 sessionMap = new ConcurrentHashMap<String, Session>();

17 }

18 /**

19 * 断开连接后触发的回调

20 */

21 @OnClose

22 public void OnClose(@PathParam("userId") String userId) {

23 LOGGER.info("聊天关闭OnClose:userId={}", userId);

24 sessionMap.remove(userId);

25 }

26 /**

27 * 收到消息时触发的回调

28 */

29 @OnMessage

30 public void OnMessage(@PathParam("userId") String userId, Session session, String message) throws IOException{

31 LOGGER.info("发送消息:userId={}", userId);

32 LOGGER.info("发送消息:message={}", message);

33 HcAppchat hcAppchat = JSON.parseObject(message, HcAppchat.class);

34 sendMessageTo(hcAppchat);

35 //sendMessageAll(message);

36 }

37 /**

38 * 传输消息出错时触发的回调

39 */

40 @OnError

41 public void error(Session session, Throwable t) {

42 LOGGER.error("socket通讯出现异常:", t.getMessage());

43 t.printStackTrace();

44 }

45

46 public void sendMessageTo(HcAppchat hcAppchat) throws IOException {

47 Session se = sessionMap.get(String.valueOf(hcAppchat.getAcceptId()));

48 Date now = new Date();

49 hcAppchat.setCreateDate(now);

50 if(se != null){

51 WebMessage webms = new WebMessage();

52 hcAppchat.setStatus(1);

53 boolean result = hcAppchatService.insert(hcAppchat);

54 LOGGER.info("用户在线,直接发送消息:result={}", result);

55 webms.setId(hcAppchat.getId());

56 webms.setCreatedAt(DateUtil.dateStr(now, "yyyy-MM-dd HH:mm:ss"));

57 webms.setText(hcAppchat.getText());

58 User user = hcAppchatService.queryUserInfo(hcAppchat.getSendId());

59 webms.setUser(user);

60 LOGGER.info("发送消息给【" + user.getName() + "】, message={}", JSON.toJSONString(webms));

61 se.getAsyncRemote().sendText(JSON.toJSONString(webms));

62 }else{

63 hcAppchat.setStatus(0);

64 boolean result = hcAppchatService.insert(hcAppchat);

65 if(result){

66 LOGGER.info("接受消息用户不在线,将消息保存数据库成功!");

67 }else{

68 LOGGER.info("接受消息用户不在线,将消息保存数据库失败!");

69 }

70 }

71 }

72 se.getAsyncRemote().sendText(message);

73 }

74 }

75 }

4.2,nodeJS实现

常用的 Node 实现有以下三种。

  • µWebSockets
  • Socket.IO
  • WebSocket-Node

下面以socket.io为例

 1 var IO = require('socket.io');

2 //var dbservice = require('./services/db_mssql.js');//链接数据库

3 //var settingConfig = require('./config/settingConfig.js');//解析存储过程

4

5 //var dbName = settingConfig.getValueByKey("dbName");

6

7 var socketFun = function (server) {

8 var socketIO = IO(server);

9 var userSockets = {};

10 socketIO.on('connection', function (socket) {

11

12 //已建立链接 加入

13 socket.on('join', function (userId) {

14 socket.userId = userId;

15 userSockets[userId] = socket;

16 })

17 //发送通知

18 socket.on('notification', function (json) {

19 if (socket.userId == undefined) {

20 socket.emit('notification', {

21 "httpCode": 500,

22 "message": "请登录后再发送消息",

23 "data": {}

24 });

25 return;

26 }

27 //var spName = "存储过程的代称";

28 json.createPeopleId = socket.userId;

29 //支持多人接收消息

30 var receivePeopleIds = [];

31 if (json.receivePeopleId!=null)

32 receivePeopleIds = json.receivePeopleId.split(';');

33 for (var i = 0; i < receivePeopleIds.length; i++) {

34

35 var json = {

36 "receivePeopleId": receivePeopleIds[i],

37 "content": json.content,

38 "url": json.url,

39 "creatPeopleId": json.creatPeopleId

40 };

41 console.log('-------json---------',json);

42 //dbservice.operateDatabase(dbName, spName, json, function (data) {//存进数据库

43 //console.log(data);

44 //});

45 var otherSocket = userSockets[json.receivePeopleId]

46 if (otherSocket != null) {

47 otherSocket.emit('notification', {

48 "httpCode": 200,

49 "message": "",

50 "data": json

51 });

52 }

53 }

54 });

55 //关闭链接

56 socket.on('disconnect', function () {

57 var userId = socket.userId;

58 delete userSockets[userId];

59 });

60 })

61 }

62

63 module.exports = socketFun;

web端调用实例

 1 var socket = io('ws://127.0.0.1:3000');//链接消息系统

2

3 socket.on('connect', function () {//建立链接

4 socket.emit('join', userId);

5 console.log('1')

6 });

7 var json = {

8 "receivePeopleId": createId,

9 "content": content,

10 "url": TaskUrl,

11 "creatPeopleId": d.CreateUserId

12 };

13 socket.emit('notification', json);//发送通知

以上是 WEB通知和React Native之即时通讯(iOS Android) 的全部内容, 来源链接: utcz.com/z/382057.html

回到顶部