WEB通知和React Native之即時通訊(iOS Android)


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 實例。執行如下語句之后,客戶端就會與服務器進行連接。

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 實現有以下三種。

下面以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);//發送通知

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM