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

WEB通知和React Native之即时通讯(iOS Android)的更多相关文章

  1. React Native之微信分享(iOS Android)

    React Native之微信分享(iOS Android) 在使用React Native开发项目的时候,基本都会使用到微信好友或者微信朋友圈分享功能吧,那么今天我就带大家实现以下RN微信好友以及朋 ...

  2. React Native 真机调试(iOS / Android)

    React Native 真机调试(iOS / Android) https://reactnative.dev/docs/running-on-device https://developer.ap ...

  3. 30天React Native从零到IOS/Android双平台发布总结

    前言 本人有近十年的技术背景,除了APP开发之外对后端.前端等都比较熟悉,近期做一个APP项目需要IOS.Android两个平台都需要,只能硬着头皮上.其实很早就想开发APP也很早就接触Android ...

  4. React Native之通知栏消息提示(android)

    React Native之通知栏消息提示(android) 一,需求分析与概述 1.1,推送作为手机应用的基本功能,是手机应用的重要部分,如果自己实现一套推送系统费时费力,所以大部分的应用都会选择使用 ...

  5. 教程视频、项目源码、全部干货【微信小程序、React Native、Java、iOS、数据结构】

    把我收藏多年的教学视频.项目源码分享给大家,大神就可以忽略了,很多东西都是基础性的,都是期初学习阶段收集的东西. 微信小程序(入门级,有web前端基础的人群): 链接: https://pan.bai ...

  6. React Native分析(index.ios.js)

    定义创建组件MyComponent(index.ios.js): 'use strict' var React = require('react-native'); var { AppRegistry ...

  7. 【REACT NATIVE 系列教程之十二】REACT NATIVE(JS/ES)与IOS(OBJECT-C)交互通信

    http://blog.csdn.net/xiaominghimi/article/details/51586492 一用到跨平台的引擎必然要有引擎与各平台原生进行交互通信的需要.那么Himi先讲解R ...

  8. react native报错处理com.android.build.api.transform.TransformException: com.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process

    背景:最近准备在使用react-native开发的app中接入友盟,来进行用户行为统计,分享,授权登录等操作. 在使用的过程中,遇到了一些错误信息,在此记录一下. 在修改android目录下的buil ...

  9. react native 5.54 出ios版本遇到的坑(应该是在xcode10下才会有的吧)记录。。。。。。 据说5.7已经修复了

    1. config.h找不到 rm -r ~/.rncache/cd node_modules/react-native/third-party/glog-0.3.4/./configure --ho ...

随机推荐

  1. 【转载】C#将图片转换为二进制流调用

    在C#中可以使用MemoryStream类.BinaryFormatter类等来操作图片,将图片读取到二进制数据流中,最终转成二进制数据流进行调用,详细的实现如下方法所示. private byte[ ...

  2. Java Scanner nextLine方法跳过

    问题描述 Scanner使用了nextInt方法的时候,如果接下来要使用nextLine,会获取不到内容 原因 因为Scanner读取用户输入数据,是先判断缓冲区是否含有数据,没有则接收用户输入的数据 ...

  3. Java基础:一个100%会发生死锁的程序

        多线程是Java工程师进阶所必须掌握的一项技能,也是面试中绕不过的一个环节,而死锁又是多线程同步失败的经典案例,对于复杂的系统,死锁是很难通过代码层面来做静态检测和排查的,所以有的面试官会从反 ...

  4. 5-Redis 的持久化之 RDB

    2016-12-22 13:58:48 该系列文章链接NoSQL 数据库简介Redis的安装及及一些杂项基础知识Redis 的常用五大数据类型(key,string,hash,list,set,zse ...

  5. html标签种类很多,为什么不都用div?

    why not divs? 所有html页面标签都可以用div解决,为什么还会存在各种不同的标签呢? 代码是写给机器阅读的,初始化标签更利于快速编程,毕竟很多标签有了自定义属性,无需编码控制,可维护性 ...

  6. arcgis api 3.x for js 共享干货系列之一自写算法实现地图量算工具(附源码下载)

    0.内容概览 Geometry 地图服务方式实现地图距离以及面积的量算,简单描述 arcgis api 提供的接口类 geometryEngine 实现地图距离以及面积的量算,简单描述 自定义距离以及 ...

  7. MAC终端常用语法

    这篇文章的重点不在于说是对终端语法的讲解,而是方便大家做语法备忘. 方便查找对应终端语法.所以使用了表格形式对常用终端语法进行了汇总, 但是并没有很多的讲解部分. 当然了这里记录的也都是十分基础的语法 ...

  8. Android为TV端助力 eclipse build project 出现major.minor version 52.0的问题

    那些网上说的JDK什么的的问题,我求你们不要误人子弟好吗? 出现在这个的原因就是ADT也就是你的SDK manager 的Tools版本跟你的SDK版本不兼容,如果你的是SDK 23.0.2那你的To ...

  9. Several ports (8005, 8080, 8009) required by Tomcat v9.0 Server at localhost

    Several ports (8005, 8080, 8009) required by Tomcat v9.0 Server at localhost 问题:Tomcat服务器的端口被占用 解决: ...

  10. Session session = connection.createSession(paramA,paramB);参数解析

    Session session = connection.createSession(paramA,paramB); paramA是设置事务,paramB是设置acknowledgment mode ...