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

一,需求分析

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 实例。执行如下语句之后,客户端就会与服务器进行连接。

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

4,webSocket.readyState

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

5,webSocket.onopen

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

 ws.onopen = function () {
ws.send('Hello Server!');
}

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

 ws.addEventListener('open', function (event) {
ws.send('Hello Server!');
});

6,webSocket.send()

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

(1)发送文本

 ws.send('your message');

(2)发送 Blob 对象

 var file = document
.querySelector('input[type="file"]')
.files[0];
ws.send(file);

(3)发送 ArrayBuffer 对象

 // Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
ws.send(binary.buffer);

(4)发送json对象

 var messageObj = {fromUserId:1,message:'您好,jackson影琪',toUserId:2};
var messageJson = JSON.stringify(messageObj);
ws.send(messageJson);

7,webSocket.onmessage

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

 ws.onmessage = function(event) {
var data = event.data;
// 处理数据
}; ws.addEventListener("message", function(event) {
var data = event.data;
// 处理数据
});

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

 ws.onmessage = function(event){
if(typeof event.data === String) {
console.log("Received data string");
} if(event.data instanceof ArrayBuffer){
var buffer = event.data;
console.log("Received arraybuffer");
}
}

8,webSocket.onclose

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

 ws.onclose = function(event) {
var code = event.code;
var reason = event.reason;
var wasClean = event.wasClean;
// handle close event
}; ws.addEventListener("close", function(event) {
var code = event.code;
var reason = event.reason;
var wasClean = event.wasClean;
// handle close event
});

9,webSocket.onerror

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

 ws.onerror = function(event) {
// handle error event
}; ws.addEventListener("error", function(event) {
// handle error event
});

2.2,react-native-gifted-chat介绍

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

     {
    _id: 1, //消息的ID
    text: 'My message', //发送的消息内容
    createdAt: new Date(), //发送的时间
    user: {/发送方的用户信息
    _id: 2, //发送方的ID
    name: 'Jackson', //发送方的昵称
    avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',, //发送方的头像
    },
    image: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',
    //添加你所需要扩展的键值对
    }
  2. user(Object) - 配置用户信息
     {
    _id: 1, //发送消息需要和配置的id一致 avatar:'https://pic.cnblogs.com/avatar/1040068/20181013100635.png', //头像 若不设置则不显示
    name:'jackson影琪', //昵称
    }
  3. renderBubble(Function) - 自定义气泡
     //气泡
    renderBubble(props) {
    return (
    <Bubble
    {...props}
    wrapperStyle={{
    left: {//对方的气泡
    backgroundColor: '#ffffff',
    },
    right: {//我方的气泡
    backgroundColor: '#1fb922',
    }
    }}
    />
    );
    }
  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) - 加载页面未加载出来时的页面
      //加载更多消息
    loadEarlier={self.state.isMore}//
    isLoadingEarlier={self.state.isMore}//
    renderLoadEarlier={() => {
    return (
    <Text
    onPress={self.onLoadEarlier}
    style={[
    styles.LookMoreStyle
    ]}
    >{self.state.moreData}</Text>
    );
    }}
  16. renderLoadEarlier(Function) - 配置 "Load earlier messages" 加载更早消息的按钮
  17. renderAvatar(Function) - 配置头像,如果设置'null'则头像都不显示
     //头像
    renderAvatar(props) {
    return (
    <Avatar
    {...props}
    />
    );
    }
  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) - 列表的属性,用于扩展你的列表
      listViewProps={{
    // //ListView/FlatView中标识是否可以加载更多(当现在获取到的数据已经是全部了,不能再继续获取数据了,则设为false,当还有数据可以获取则设为true)
    canLoad: true,
    //标识现在是否ListView/FlatView现在正在加载(根据这个值来决定是否显示"正在加载的cell")(loadMore()方法进去后设为true,fetch加载完数据后设为false)
    isLoadding: false,
    //是否显示下拉刷新的cell
    ifShowRefresh: true,
    //ListView/FlatList是否可以滚动
    scrollEnabled: true,
    //记录当前加载到了哪一页
    page: 1,
    onScroll:self._onScroll.bind(this)
    }}
  44. textInputProps(Object) - 输入框的属性,用于扩展你的输入框
  45. keyboardShouldPersistTaps(Enum) - 确定键盘在敲击后是否应该保持可见。一个枚举; 详情见 <ScrollView>
  46. onInputTextChanged(Function) - 输入框编辑时的回调
  47. maxInputLength(Integer) - 输入框输入的最多字符数
  48. showAvatarForEveryMessage(Bool) - 默认是false每条消息都显示头像

系统消息格式

 {
_id: 1,
text: 'This is a system message',
createdAt: new Date(),
system: true,
// Any additional custom parameters are passed through
}

三,即时通讯实现

3.1,实现步骤

第一步:建立链接

 componentWillMount() {
let self = this;
//建立链接
ws = new WebSocket('ws://127.0.0.1:8080/websocket/'+str);
ws.onopen = (evt) => {
// 打开一个连接
// console.log('WebSocket==' + evt)
alert("连接成功啦")
//ws.send('something'); // 发送一个消息
};
ws.onmessage = (e) => {
// }
// 接收到了一个消息
//alert(JSON.parse(e.data).text)
console.log('e.data==' + e.data);
}; ws.onerror = (e) => {
// 发生了一个错误
console.log('e.message==' + e.message);
}; ws.onclose = (e) => {
// 连接被关闭了
console.log('e.code===' + e.code, 'e.reason===' + e.reason);
};
}

第二步:发送消息

 onSend(messages = []) {
let self = this
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, messages),
}))
// alert(messages[0].text)
this.doSend(messages[0].text)
} // 发送消息
doSend = (message) => {
var messageObj = {
fromUserId: this.state.userData._id,
fromNickName: this.state.userData.name,
message: message,
toUserId: this.props.Account.id,
toNickName: this.props.Account.name,
sendTime: new Date() };
var messageJson = JSON.stringify(messageObj);
ws.send(messageJson);
}

第三步:接收消息

  ws.onmessage = (e) => {
// {
// _id: 1, //消息的ID
// text: 'My message', //发送的消息内容
// createdAt: new Date(), //发送的时间
// user: {/发送方的用户信息
// _id: 2, //发送方的ID
// name: 'Jackson', //发送方的昵称
// avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',, //发送方的头像
// },
// }
// 接收到了一个消息
//alert(JSON.parse(e.data).text)
console.log('e.data==' + e.data);
}
 
第四步:关闭链接
 componentWillUnmount() {
ws.close()
this.setState = (state, callback) => {
return;
};
}

3.2.聊天界面构建

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

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

2,引入使用

 /**
* Created by Jackson on 2018/11/12.
* 聊天界面
*/
import React, { PureComponent } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Keyboard,
Platform,
StatusBar
} from 'react-native';
//聊天
import { GiftedChat, Bubble, Avatar } from 'react-native-gifted-chat'
export default class ChatBox extends PureComponent {
constructor(props) {
super(props);
this.renderBubble = this.renderBubble.bind(this);
this.renderAvatar = this.renderAvatar.bind(this);
this.state = {
//聊天
messages: [],
userData: {
_id: 1,
name:'jackson影琪',
avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',
},
messageId: 1,
} }
componentDidMount() {
let self = this
/****************************聊天组件 start **************************************************/
setTimeout(function(){
self.setState({
messages: [ {
_id: 2,
text: '微信小程序开发的基本流程',
createdAt: new Date('2018-10-25T15:41:00+08:00'),
user: {
_id: 1,
name: 'jackson影琪',
avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',
},
//image: 'https://img2018.cnblogs.com/blog/1040068/201810/1040068-20181024162047704-1159291775.png',
},
{
_id: 1,
text: 'Hello jackson影琪',
createdAt: new Date('2016-06-07T10:00:00+08:00'),
user: {
_id: 2,
name: 'jackson',
avatar: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181101192529807-2132606645.jpg'
},
image: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',
},
],
})
},2000)
/****************************聊天组件 end **************************************************/ } /****************************聊天 start **************************************************/
onSend(messages = []) {
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, messages),
}))
// alert(messages[0].text)
let self = this
self.state.messageId += 2
let m = {
_id: self.state.messageId,
text: '前端知识点总结(HTML)',
createdAt: new Date(),
user: {
_id: 2,
name: '',
avatar: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181101192529807-2132606645.jpg'
},
image: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181109115100292-977588541.png',
}
self.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, m),
}))
}
//气泡
renderBubble(props) {
return (
<Bubble
{...props}
wrapperStyle={{
left: {
backgroundColor: '#ffffff',
},
right: {
backgroundColor: '#1fb922',
}
}}
/>
);
}
//头像
renderAvatar(props) {
return (
<Avatar
{...props}
/>
);
}
/****************************聊天 end **************************************************/
render() {
let self = this;
return (
<TouchableOpacity
activeOpacity={1}
style={{ flex: 1,}}
onPress={() => { Keyboard.dismiss() }}
> {/* //聊天 */}
<GiftedChat
// onPressAvatar={()=>{alert('Keyboard.dismiss'); Keyboard.dismiss()}}
messages={this.state.messages}
onSend={messages => this.onSend(messages)}
renderBubble={this.renderBubble}//气泡
renderAvatar={this.renderAvatar}//头像
showUserAvatar={true}//是否显示自己的头像,默认不显示
//onLongPress={()=>{alert('onLongPress')}}//长按消息
// 输入组件
placeholder={'请输入内容'}//输入框占位符
// label={'发送'}
containerStyle={{ marginBottom: 2 }}//发送按钮
children={
<View
style={[
styles.buttonBoxBorder
]}
>
<Text
style={[
styles.buttonText,
]}
>发送</Text>
</View>
}
// textStyle={{ color: '#70b24e' }}//按钮字的颜色
timeFormat={'MM月DD日 HH:mm:ss'}//格式化日前
dateFormat={'YYYY年MM月DD日'}
// locale={'zh-cn'}
isAnimated={true}
// renderAvatarOnTop={true}
user={this.state.userData}//用户信息
/>
</TouchableOpacity>
)
} } const styles = StyleSheet.create({
buttonText: {
paddingHorizontal: 15,
paddingVertical: 5,
textAlign: 'center',
color: '#fff',
fontSize: 14
},
buttonBoxBorder: {
overflow: 'hidden',
borderRadius: 5,
borderWidth: 1,
backgroundColor: "#70b24e",
borderColor: "#70b24e",
marginRight: 12,
marginBottom: 6,
},
})
效果如下:

3.3,使用的方法

1,下拉加载更多

  {/* //聊天 */}
<GiftedChat
// onPressAvatar={()=>{alert('Keyboard.dismiss'); Keyboard.dismiss()}}
messages={this.state.messages}
onSend={messages => this.onSend(messages)}//发送消息 ... //加载更多消息
loadEarlier={self.state.isMore}//
isLoadingEarlier={self.state.isMore}//
renderLoadEarlier={() => {
return (
<Text
onPress={self.onLoadEarlier}
style={[
styles.LookMoreStyle
]}
>{self.state.moreData}</Text>
);
}} listViewProps={{
// //ListView/FlatView中标识是否可以加载更多(当现在获取到的数据已经是全部了,不能再继续获取数据了,则设为false,当还有数据可以获取则设为true)
canLoad: true,
//标识现在是否ListView/FlatView现在正在加载(根据这个值来决定是否显示"正在加载的cell")(loadMore()方法进去后设为true,fetch加载完数据后设为false)
isLoadding: false,
//是否显示下拉刷新的cell
ifShowRefresh: true,
//ListView/FlatList是否可以滚动
scrollEnabled: true,
//记录当前加载到了哪一页
page: 1,
onScroll:self._onScroll.bind(this)
}}
/>
   //加载更早的数据
onLoadEarlier = () => {
let self = this;
self.state.Currentpage += 1;
self.setState({
isMore: true,
moreData: '正在加载更多...'
})
self.getMessageData()
} //上拉加载//翻页
_onScroll(event) {
let self = this
let y = event.nativeEvent.contentOffset.y;
let height = event.nativeEvent.layoutMeasurement.height;
let contentHeight = event.nativeEvent.contentSize.height;
if (y + height >= contentHeight - 20 && y > 0 && this.state.contentHeight != contentHeight) {//上啦下一页
self.state.contentHeight=contentHeight
self.onLoadEarlier() }
else if (y < 0 || y == 0) {//下拉上一页ios }
}

2,在消息前后追加消息

 //prepend(),在父级最前面追加一个子元素
self.setState(previousState => ({
messages: GiftedChat.prepend(previousState.messages, ReceivedMessageData),
})) //append(),在父级最后追加一个子元素
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, messages),
}))

3,完整代码

  {/* //聊天 */}
<GiftedChat
// onPressAvatar={()=>{alert('Keyboard.dismiss'); Keyboard.dismiss()}}
messages={this.state.messages}
onSend={messages => this.onSend(messages)}//发送消息
renderBubble={this.renderBubble}//气泡
renderAvatar={this.renderAvatar}//头像
showUserAvatar={true}// 显示发送方的头像
showAvatarForEveryMessage={true}//每条消息都显示头像
//onLongPress={()=>{alert('onLongPress')}}
// 输入组件
placeholder={'请输入内容'}
// label={'发送'}
containerStyle={{ marginBottom: 2 }}
children={
<View
style={[
styles.buttonBoxBorder
]}
>
<Text
style={[
styles.buttonText,
]}
>发送</Text>
</View>
}//渲染发送按钮
// textStyle={{ color: '#70b24e' }}
timeFormat={'MM月DD日 HH:mm:ss'}
dateFormat={'YYYY年MM月DD日'}
// locale={'zh-cn'}
isAnimated={true}
// renderAvatarOnTop={true}
user={this.state.userData} // 系统消息样式
wrapperStyle={{ paddingLeft: 12, paddingRight: 12 }}
textStyle={{ lineHeight: 20 }}
//加载更多消息
loadEarlier={self.state.isMore}//
isLoadingEarlier={self.state.isMore}//
renderLoadEarlier={() => {
return (
<Text
onPress={self.onLoadEarlier}
style={[
styles.LookMoreStyle
]}
>{self.state.moreData}</Text>
);
}} listViewProps={{
// //ListView/FlatView中标识是否可以加载更多(当现在获取到的数据已经是全部了,不能再继续获取数据了,则设为false,当还有数据可以获取则设为true)
canLoad: true,
//标识现在是否ListView/FlatView现在正在加载(根据这个值来决定是否显示"正在加载的cell")(loadMore()方法进去后设为true,fetch加载完数据后设为false)
isLoadding: false,
//是否显示下拉刷新的cell
ifShowRefresh: true,
//ListView/FlatList是否可以滚动
scrollEnabled: true,
//记录当前加载到了哪一页
page: 1,
onScroll:self._onScroll.bind(this)
}}
/>

效果展示:

注意:

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 包:

  <dependency>
<groupId>javax.websocket</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>

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

  import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class WebSocketService { private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketService.class);
// ... }

3.完整代码实现

 ...

 public class WebSocketService {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketService.class); public static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>(); private HcAppchatService hcAppchatService = SpringContextHandler.getBean(HcAppchatService.class);
/**
* 建立连接后触发的回调
*/
@OnOpen
public void onOpen(@PathParam("userId") String userId, Session session) {
LOGGER.info("聊天打开onOpen:userId={}", userId);
if (sessionMap == null) {
sessionMap = new ConcurrentHashMap<String, Session>();
}
/**
* 断开连接后触发的回调
*/
@OnClose
public void OnClose(@PathParam("userId") String userId) {
LOGGER.info("聊天关闭OnClose:userId={}", userId);
sessionMap.remove(userId);
}
/**
* 收到消息时触发的回调
*/
@OnMessage
public void OnMessage(@PathParam("userId") String userId, Session session, String message) throws IOException{
LOGGER.info("发送消息:userId={}", userId);
LOGGER.info("发送消息:message={}", message);
HcAppchat hcAppchat = JSON.parseObject(message, HcAppchat.class);
sendMessageTo(hcAppchat);
//sendMessageAll(message);
}
/**
* 传输消息出错时触发的回调
*/
@OnError
public void error(Session session, Throwable t) {
LOGGER.error("socket通讯出现异常:", t.getMessage());
t.printStackTrace();
} public void sendMessageTo(HcAppchat hcAppchat) throws IOException {
Session se = sessionMap.get(String.valueOf(hcAppchat.getAcceptId()));
Date now = new Date();
hcAppchat.setCreateDate(now);
if(se != null){
WebMessage webms = new WebMessage();
hcAppchat.setStatus(1);
boolean result = hcAppchatService.insert(hcAppchat);
LOGGER.info("用户在线,直接发送消息:result={}", result);
webms.setId(hcAppchat.getId());
webms.setCreatedAt(DateUtil.dateStr(now, "yyyy-MM-dd HH:mm:ss"));
webms.setText(hcAppchat.getText());
User user = hcAppchatService.queryUserInfo(hcAppchat.getSendId());
webms.setUser(user);
LOGGER.info("发送消息给【" + user.getName() + "】, message={}", JSON.toJSONString(webms));
se.getAsyncRemote().sendText(JSON.toJSONString(webms));
}else{
hcAppchat.setStatus(0);
boolean result = hcAppchatService.insert(hcAppchat);
if(result){
LOGGER.info("接受消息用户不在线,将消息保存数据库成功!");
}else{
LOGGER.info("接受消息用户不在线,将消息保存数据库失败!");
}
}
}
se.getAsyncRemote().sendText(message);
}
}
}

4.2,nodeJS实现

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

下面以socket.io为例

 var IO = require('socket.io');
//var dbservice = require('./services/db_mssql.js');//链接数据库
//var settingConfig = require('./config/settingConfig.js');//解析存储过程 //var dbName = settingConfig.getValueByKey("dbName"); var socketFun = function (server) {
var socketIO = IO(server);
var userSockets = {};
socketIO.on('connection', function (socket) { //已建立链接 加入
socket.on('join', function (userId) {
socket.userId = userId;
userSockets[userId] = socket;
})
//发送通知
socket.on('notification', function (json) {
if (socket.userId == undefined) {
socket.emit('notification', {
"httpCode": 500,
"message": "请登录后再发送消息",
"data": {}
});
return;
}
//var spName = "存储过程的代称";
json.createPeopleId = socket.userId;
//支持多人接收消息
var receivePeopleIds = [];
if (json.receivePeopleId!=null)
receivePeopleIds = json.receivePeopleId.split(';');
for (var i = 0; i < receivePeopleIds.length; i++) { var json = {
"receivePeopleId": receivePeopleIds[i],
"content": json.content,
"url": json.url,
"creatPeopleId": json.creatPeopleId
};
console.log('-------json---------',json);
//dbservice.operateDatabase(dbName, spName, json, function (data) {//存进数据库
//console.log(data);
//});
var otherSocket = userSockets[json.receivePeopleId]
if (otherSocket != null) {
otherSocket.emit('notification', {
"httpCode": 200,
"message": "",
"data": json
});
}
}
});
//关闭链接
socket.on('disconnect', function () {
var userId = socket.userId;
delete userSockets[userId];
});
})
} module.exports = socketFun;

web端调用实例

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

   socket.on('connect', function () {//建立链接
socket.emit('join', userId);
console.log('1')
});
var json = {
"receivePeopleId": createId,
"content": content,
"url": TaskUrl,
"creatPeopleId": d.CreateUserId
};
socket.emit('notification', json);//发送通知

最新文章

  1. [LeetCode] Combine Two Tables 联合两表
  2. 手动使用sql*plus创建Oracle数据库
  3. 发现struct proc_dir_entry内核3.10.17移到internal中去了,倒
  4. 区别ie8和ie9的方法
  5. string.format
  6. C#异常类总结
  7. 百度HTTPS加密搜索有什么用?
  8. STM32实验非正式报告之DMA
  9. To change the sharepoint CA port
  10. poj 2441 Arrange the Bulls(状态压缩dp)
  11. 《Programming WPF》翻译 第5章 1.不使用样式
  12. k8s之scheduler
  13. oracle中的exists 和in
  14. java创建线程
  15. 使用Spring Aop自定义注解实现自动记录日志
  16. Linux(Ubuntu)使用日记(三)------git安装使用
  17. [leetcode]332. Reconstruct Itinerary
  18. 四、XML语言学习(3)
  19. cp 命令有坑
  20. _rate_charaters

热门文章

  1. Java集合类源码解析:ArrayList
  2. laravel框架使用中错误及解决办法总结
  3. Yapi部署说明
  4. SQL语句中不同的连接JOIN
  5. 【转载】关于generate用法的总结【Verilog】
  6. &quot;&#39;cl&#39; 不是内部或外部命令,也不是可运行的程序&quot;解决方案
  7. Python Docker 查看私有仓库镜像【转】
  8. Extending the Yahoo! Streaming Benchmark
  9. SQL LCASE() 函数
  10. JavaScript代码组织结构良好的5个特点