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

其他特点包括:

(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介绍

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      }

复制代码

user(Object) - 配置用户信息

1 {

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

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

4  }

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  }

复制代码

text(String) - 输入框的默认值;默认是undefined

placeholder(String) - 输入框的占位字符

messageIdGenerator(Function) - 为你的新消息自动生成一个id. 默认是用 UUID v4, 由uuid库实现uuid

onSend(Function) - 点击send时的回调

locale(String) -本地化日期

timeFormat(String) - 格式化时间,默认是本地时间,即当前时区的时间

dateFormat(String) - 日期格式化

isAnimated(Bool) - 键盘出现时,是否有动画

loadEarlier(Bool) - 是否显示加载更早的消息按钮 "Load earlier messages"

onLoadEarlier(Function) - 加载更多消息时的回调

isLoadingEarlier(Bool) - 点击加载更早的消息时是否出现转菊花的图标

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          }}

复制代码

renderLoadEarlier(Function) - 配置 "Load earlier messages" 加载更早消息的按钮

renderAvatar(Function) - 配置头像,如果设置'null'则头像都不显示

复制代码

1 //头像

2  renderAvatar(props) {

3    return (

4      <Avatar

5        {...props}

6      />

7    );

8  }

复制代码

showUserAvatar(Bool) - 是否展示自己的头像,默认时false 只展示别人的头像

onPressAvatar(Function(user)) - 点击头像时的回调

renderAvatarOnTop(Bool) 头像显示在顶部还是底部,默认是底部

renderSystemMessage(Function) - 自定义系统消息

onLongPress(Function(context,message)) - 长按消息气泡时的回调,详细可以看github的演示 showActionSheetWithOptions()

inverted(Bool) - 反转消息的显示顺序,默认是true 即消息显示的顺序是否和你message数组的顺序相同

renderMessage(Function) - 自定义消息的内容View

renderMessageText(Function) - 自定义消息的文本

renderMessageImage(Function) - 自定义图片消息

imageProps(Object) - 额外的属性要传递给默认创建的组件rendermessageimage点去去查看文档

lightboxProps(Object) - 额外的属性传递给Modal框(体现在点击图片的Modal)

点击查看第三方 - Lightbox

renderCustomView(Function) - 在气泡内创建一个自己自定义的视图,即创建自定义的消息

renderDay(Function) - 自定义消息上方的日期

renderTime(Function) - 自定义消息中的时间

renderFooter(Function) - 自定义listView的底部, 例如.'User is typing...'; 点击查看示例 example/App.js for an example

renderChatFooter(Function) - 自定义组件的渲染下messagecontainer(从ListView分开)

renderInputToolbar(Function) - 自定义你的底部工具栏

renderComposer(Function) - 自定义textInput输入框

renderActions(Function) - 自定义输入框左边的按钮

renderSend(Function) -自定义发送按钮;您可以很容易地将子组件传递给原始组件,例如使用自定义图标。

renderAccessory(Function) - 在消息编辑器下面的自定义第二行操作

onPressActionButton\(Function) - 当点击输入框左边的按钮时的回调 (如果设置了 actionSheet将不会执行)

bottomOffset(Integer) - 从屏幕底部的聊天距离(如显示选项卡栏,则非常有用)

minInputToolbarHeight(Integer) - 工具栏的最小高度,默认是44

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          }}

复制代码

textInputProps(Object) - 输入框的属性,用于扩展你的输入框

keyboardShouldPersistTaps(Enum) - 确定键盘在敲击后是否应该保持可见。一个枚举; 详情见 <ScrollView>

onInputTextChanged(Function) - 输入框编辑时的回调

maxInputLength(Integer) - 输入框输入的最多字符数

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>();

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);//发送通知

深圳网站建设www.sz886.com

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容