前言
UDP通信分為單播 廣播 組播,基礎(chǔ)庫2.7.0之后,小程序開始支持UDP通信,目前小程序只支持單播。
小程序API
小程序UDP通信這一塊可以說是很簡單了就一個UDPSocket實(shí)例。然后bind()方法綁定端口,send()方法發(fā)送數(shù)據(jù),close()方法關(guān)閉通信,onMessage()方法監(jiān)聽消息等等,具體可以去看文檔
相關(guān)技術(shù)難點(diǎn)
使用UDP通信時,如果是用于局域網(wǎng)內(nèi)通信,這里有個難點(diǎn)就是,不知道本機(jī)及對應(yīng)要通信設(shè)備在當(dāng)前局域網(wǎng)內(nèi)的ip及綁定的端口。如果是用于和服務(wù)器之間的數(shù)據(jù)傳輸?shù)脑?,沒想出具體應(yīng)用場景...
咨詢了一下做嵌入式的朋友,他說硬件部分可以綁定一個固定的端口號。(這樣的話可以通過mDNS和固定端口號把需要通信對象的ip/port拼出來。)
通信效果展示
由于沒有2臺手機(jī),所以用node.js代碼充當(dāng)了其中一臺客戶端,先看看客戶端間通信截圖。

client1.png

client2.png
Node.js端代碼
通過dgram框架進(jìn)行操作,這里由于演示,所以每次都簡單地返回相同的數(shù)據(jù)。
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('close',()=>{
console.log('socket已關(guān)閉');
});
server.on('error',(err)=>{
console.log(err);
});
server.on('listening',()=>{
console.log('socket正在監(jiān)聽中...');
});
server.on('message',(msg,rinfo)=>{
console.log(`receive message from ${rinfo.address}:${rinfo.port}`);
console.log(`message=${msg}`)
server.send('收到你的信息了',rinfo.port,rinfo.address)
});
server.bind('8060');
小程序端代碼
代碼比較簡單,唯一需要注意的是,接收到udp消息時,數(shù)據(jù)是ArrayBuffter格式的,需要轉(zhuǎn)成string顯示。(這部分代碼在js代碼最下方)
// js代碼
// pages/welcome/welcome.js
let util = require('../../utils/util.js')
Page({
udpSocket:null,
/**
* 頁面的初始數(shù)據(jù)
*/
data: {
messageList:[]
},
mydata:{
message:'',
remoteUrl: {
ip: '',
port: -1
},
isSend:false
},
addMessage(event){
console.log(event)
this.mydata.message = event.detail.value
},
sendMessage(){
if (this.mydata.isSend){
return ;
}
this.mydata.isSend = true
let ip = this.mydata.remoteUrl.ip
let port = this.mydata.remoteUrl.port
let message = this.mydata.message
if(message.trim() === ''){
wx.showToast({
title: '請輸入內(nèi)容',
})
return ;
}
udpSocket.send({
address: ip,
port: port,
message: message
})
this.mydata.isSend = false
let list = this.data.messageList
let obj = {
text: message,
from : 2
}
list.push(obj)
this.setData({
messageList : list
})
},
/**
* 生命周期函數(shù)--監(jiān)聽頁面加載
*/
onLoad: function (options) {
this.initUdpSocket()
},
initUdpSocket(){
udpSocket = wx.createUDPSocket();
if(udpSocket === null){
console.log('暫不支持')
return ;
}
const locationPort = udpSocket.bind()
this.setData({
'locationUrl.port': locationPort
})
udpSocket.onListening(function(res){
console.log('監(jiān)聽中...')
console.log(res)
})
let that = this
udpSocket.onMessage(function (res) {
console.log(res)
let str = util.newAb2Str(res.message)
console.log('str==='+str)
let list = that.data.messageList
let obj = {
text: str,
from: 1
}
list.push(obj)
that.setData({
messageList: list
})
})
},
/**
* 輸入ip/端口號
*/
addIp: function(event){
this.mydata.remoteUrl.ip = event.detail.value
},
addPort: function (event){
this.mydata.remoteUrl.port = event.detail.value
},
/**
* 生命周期函數(shù)--監(jiān)聽頁面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函數(shù)--監(jiān)聽頁面顯示
*/
onShow: function () {
}
}
// util.newAb2Str代碼
function newAb2Str(arrayBuffer){
let unit8Arr = new Uint8Array(arrayBuffer);
let encodedString = String.fromCharCode.apply(null, unit8Arr),
decodedString = decodeURIComponent(escape((encodedString)));//沒有這一步中文會亂碼
return decodedString;
}
相應(yīng)的WXML和WXSS代碼
<!-- wxml -->
<!--pages/welcome/welcome.wxml-->
<view>
<view class='header'>
<view>
<text>本機(jī)端口:{{locationUrl.port}}</text>
</view>
<view class='headerItem'>
<text>IP地址:</text>
<input type='text' bindinput='addIp'></input>
</view>
<view class='headerItem'>
<text>端口:</text>
<input type='number' bindinput='addPort'></input>
</view>
</view>
<view class='content'>
<block wx:for="{{messageList}}" wx:key="{{index}}">
<view wx:if="{{item.from === 1}}" class='fromLeft'>
<text>{{item.text}}</text>
</view>
<view wx:else class='fromRight'>
<text>{{item.text}}</text>
</view>
</block>
</view>
<view class='footer'>
<input type='text' bindinput='addMessage'></input>
<button bindtap='sendMessage'>發(fā)送</button>
</view>
</view>
// wxss
/* pages/welcome/welcome.wxss */
.header{
padding: 20rpx;
border-bottom: solid 1px #dfdfdf;
}
.headerItem{
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
margin-top: 10rpx;
height: 50rpx;
}
.headerItem > text{
color: #333;
width: 150rpx;
font-size: 30rpx;
text-align: right;
}
.headerItem > input{
border: solid 1px #dfdfdf;
}
.content{
padding: 0 20rpx;
}
.content > view{
margin-top: 10rpx;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
}
.content > .fromLeft > text{
padding: 10rpx;
width: 690rpx;
text-align: left;
background-color: orangered;
color: white;
}
.content > .fromRight > text{
padding: 10rpx;
width: 690rpx;
text-align: right;
background-color: #dfdfdf;
color: white;
}
.footer{
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
padding: 0 20rpx;
margin-top: 20rpx;
}
.footer > input{
border: solid 2rpx #dfdfdf;
border-radius: 4rpx;
width: 500rpx;
height: 70rpx;
line-height: 70rpx;
margin-right: 10rpx;
}
.footer > button{
width: 200rpx;
height: 70rpx;
line-height: 70rpx;
}