基於無線區網群播之 Ad Hoc 群組通訊
Ad Hoc Group Communication Based on Multicast in Wireless LAN
即運作於區網間不需要對外網路的分散式通訊軟體,
使用者將裝置連至同一 WiFi 即可通訊。
系統內以群組作為通訊單位,
其中包含 LOBBY (公開群組)可與網路內所有使用者形成 Ad Hoc 群組並通訊,
以及使用者自行創建的私人群組。
私人群組內之訊息傳遞皆經過加密處理,
使用者於群組內可取得由群組資訊與加密金鑰組成之 QR Code,
其他使用者透過掃描 QR Code 方可加入群組。
Logo designed by ihtiht.
- 支援 Android、iOS 雙系統
- 離線聊天,不須對外網路
- 分散式,無中央伺服器
- Ad Hoc 隨意群組
- 訊息加密
- 訊息形式支援純文字、圖片、檔案 (UTF8 文字檔)、票選活動、緊急訊息
- 一鍵發送緊急訊息
https://facebook.github.io/react-native/docs/getting-started.html Building Projects with Native Code
- Android
- react-native-cli
- Java JDK
- Android Studio
- iOS
- react-native-cli
- watchman
- Xcode
git clone https://github.com/x3388638/LANChat.git
cd LANChat
npm install
https://facebook.github.io/react-native/docs/running-on-device.html
Android:
adb devices
react-native run-android
iOS: 使用 Xcode 開啟
MacOS:
MacOS: 10.13.4
Xcode: 9.3
Java JDK: 1.8.0
nodejs: 6.11.3
npm: 6.0.1
React: 16.3.1
React Native: 0.55.3
react-native-cli: 2.0.1
iOS device(s): MGAA2TA/A (11.3.1)
Android device(s): SM-T700 (5.0.2)、ASUS_Z00AD (5.0)、SGP312 (5.0.2)、C5502 (5.0.2)
Ubuntu:
Ubuntu: 16.04
Java JDK: 1.8.0
nodejs: 6.11.3
npm: 3.10.10
React: 16.3.1
React Native: 0.55.3
react-native-cli: 2.0.1
Android device(s): ASUS_Z00AD (5.0)
{
type: 'alive',
paylaod: {
ip: 'string'
}
}
{
type: 'userData',
payload: {
uid: 'string',
data: {
username: 'string',
selfIntro: 'string',
uid: 'string'
},
joinedGroups: {
[group ID]: 'encrypted groupID string'
}
}
}
encrypted
{
type: 'msg',
payload: {
groupID: 'string',
encryptedID: 'encrypted groupID string',
data: Encrypt('group key string', JSON.stringify({
key: 'msg id string',
sender: 'user id string',
timestamp: 'ISO 8601 string',
type: 'text" || 'poll' || 'vote' || 'img' || 'file',
[type]: 'string' || 'img base64 string' || 'file string'
}))
}
}
plain text
{
type: 'msg',
payload: {
groupID: 'LOBBY',
data: {
key: 'msg id string',
sender: 'user id string',
timestamp: 'ISO 8601 string',
type: 'text' || 'emergency' || 'poll' || 'vote' || 'img' || 'file',
[type]: 'string' || 'img base64 string' || 'file string'
}
}
}
type: text
text: 'string'
type: emergency
emergency: 'string'
type: img
img: 'base64 string'
type: poll
poll: {
pollID: 'uuid',
title: 'string',
desc: 'string',
options: [
{
id: 'option id string',
text: 'string'
}
]
}
type: vote
vote: {
voteID: 'vote id string',
pollID: 'poll id string',
optionID: 'option id string'
}
type: file
file: {
fileID: 'string',
fileName: 'string',
}
{
type: 'msgSync',
payload: {
[groupID]: {
encryptID: 'encrypted groupID string',
messages: Encrypt('group key string', JSON.stringify([
{
key: 'msg id string',
sender: 'user id string',
timestamp: 'ISO 8601 string',
type: 'text' || 'poll' || 'vote' || 'img' || 'file',
[type]: 'string' || 'img base64 string' || 'file string'
}
]))
},
'LOBBY': {
messages: JSON.stringify([
{
key: 'msg id string',
sender: 'user id string',
timestamp: 'ISO 8601 string',
type: 'text' || 'poll' || 'vote' || 'img' || 'file',
[type]: 'string' || 'img base64 string' || 'file string'
}
])
}
}
}
encrypted
{
type: 'fileReq',
payload: {
bssid: {{ group's bssid }}
groupID: 'group id',
data: Encrypt('group key string', JSON.stringify({
fileID: 'file id string',
reqID: 'file request id string'
})
}
}
plain text
{
type: 'fileReq',
payload: {
groupID: 'LOBBY',
data: JSON.stringify({
fileID: 'file id string',
reqID: 'file request id string'
})
}
}
encrypted
{
type: 'fileRes',
payload: {
groupID: 'group id',
data: Encrypted('group key string', JSON.stringify({
reqID: 'file request id plain text',
error: null || 'error msg',
fileName: 'string',
file: 'utf8 string'
})
}
}
plain text
{
type: 'fileRes',
payload: {
groupID: 'group id',
data: JSON.stringify({
reqID: 'file request id plain text',
error: null || 'error msg',
fileName: 'string',
file: 'utf8 string'
})
}
}
{
normal: {
username: 'string',
selfIntro: '+string',
uid: 'hash(deviceID)'
},
emergency: {
name: '+string',
birth: '+string',
phone: '+string',
gender: '+string ("F" || "M")',
bloodType: '+string ("A" || "B" || "AB" || "O")',
address: '+string',
memo: '+string'
}
}
{
[bssid]: {
[groupID]: {
groupID: 'string',
groupName: 'string',
groupDesc: 'string',
key: 'string',
net: {
ssid: 'string',
bssid: 'string'
},
createdTime: 'ISO 8601 string'
}
}
}
{
[bssid]: {
[groipID]: {
[msgID]: {
key: 'msg id string',
read: 'boolean',
sender: 'user id string',
timestamp: 'ISO 8601 string',
type: 'text' || 'img' || 'file',
[type]: 'string' || 'img base64 string' || file Buffer
}
}
}
}
{
[uid]: {
uid: 'string',
username: 'string',
selfIntro: 'string',
lastSeen: 'ISO 8601 string',
joinedGroups: ['group ID string']
}
}
{
[bssid]: {
[uid]: 1
}
}
{
[pollID]: {
bssid: 'string',
groupID: 'group id string',
creater: 'user id string',
title: 'string',
desc: 'string',
timestamp: 'ISO 8601 string',
options: [
{
id: 'option id string'
text: 'string'
}
]
}
}
{
[voteID]: {
bssid: 'bssid',
groupID: 'group id string',
pollID: 'poll id string',
optionID: 'option id string',
voter: 'user id string',
timestamp: 'ISO 8601 string'
}
}
{
[bssid]: {
[groupID]: {
[fileID]: {
fileID: 'string',
fileName: 'string',
filePath: 'string'
}
}
}
}
{
[ip]: {
uid: 'user id string',
username: 'string',
selfIntro: 'string',
lastSeen: 'ISO 8601 string',
ip: 'string',
tcpSocket: 'net socket'
}
}