一、通信時(shí)會(huì)傳輸哪些內(nèi)容
-
1、字符串?dāng)?shù)據(jù)
簡(jiǎn)單的字符串:比如登錄請(qǐng)求信息,登錄結(jié)果返回的信息。
用json系列化的字符串:比如上傳一個(gè)表到服務(wù)器,讓它寫入到數(shù)據(jù)庫中。
讀取文件的時(shí)候,讀取的是string內(nèi)容。 -
2、二進(jìn)制數(shù)據(jù)
比如傳輸?shù)氖俏募豪鏼yword.doc,myexcel.xls或者是assetboundle文件。
比如上傳實(shí)驗(yàn)報(bào)告,生成實(shí)驗(yàn)報(bào)告并下載等。
讀取文件的時(shí)候,直接讀取字節(jié)碼。
二、Unity(NativeWebSocket)和服務(wù)端(Fleck)怎么約定要傳輸?shù)臄?shù)據(jù)格式
1、Unity(NativeWebSocket)發(fā)送和接收信息的API有哪些?
- 發(fā)送字符串
SendText(string message) - 發(fā)送二進(jìn)制
Send(byte[] bytes) - 接收字符串【無】
OnMessage(string : msg) - 接收二進(jìn)制
OnMessage(bytes : byte[])
2、服務(wù)器發(fā)送string和byte[]給Unity時(shí),Unity如何處理?
- Fleck發(fā)送string,使用Send(message:string)
- Fleck發(fā)送二進(jìn)制,使用Send(bytes:byte[])
- Unity客戶端統(tǒng)一使用OnMessage(bytes : byte[])進(jìn)行數(shù)據(jù)的接收
Unity需要識(shí)別這個(gè)包是什么內(nèi)容:字符串指令,系列化的json字符串,不同種類的二進(jìn)制文件…等等!
3、傳輸?shù)臄?shù)據(jù)格式
4、服務(wù)器和客戶端信息來往說明
5、接收和發(fā)送的關(guān)鍵部分
(1)、客服端發(fā)送信息
- 發(fā)送json文件到服務(wù)器
var jsonString = JsonUtility.ToJson(myScores); //myScores 預(yù)先定義的得分信息
websocket.SendText($"JSONSCOR{jsonString}");
- 發(fā)送字符串指令到服務(wù)器
/// <summary>
/// 命令字符串
/// </summary>
/// <param name="text">命令字符串</param>
/// <returns></returns>
public async UniTask SendCommand(string cmdText)
{
//傳輸時(shí)自動(dòng)在前面添加[COMD]
var msg = $"COMD{cmdText}";
await websocket.SendText(msg);
}
- 發(fā)送二進(jìn)制文件到服務(wù)器
/// <summary>
/// 發(fā)送文件
/// </summary>
/// <param name="file">file.extend</param>
/// <param name="data">byte[]數(shù)據(jù)</param>
/// <returns>成功與否</returns>
public async UniTask<bool> SendBinary(string fileName,byte[] data)
{
Debug.Log($"即將發(fā)送數(shù)據(jù):{fileName} {data.Length}");
var rtn = false;
if (fileName.Length > 30)
{
Debug.Log("文件名不能超過30個(gè)字符");
rtn = false;
}
else
{
var head = Encoding.UTF8.GetBytes("BINA");
var fileNameBytes = Encoding.UTF8.GetBytes(fileName.PadRight(30));
var allData = new List<byte>();
allData.AddRange(head); //頭
allData.AddRange(fileNameBytes); //文件名稱
allData.AddRange(data); //文件數(shù)據(jù)
await websocket.Send(allData.ToArray());
rtn = true;
}
return true;
}
(2)、客戶端向服務(wù)器發(fā)送下載請(qǐng)求,并等待數(shù)據(jù)到達(dá)
- 發(fā)送【COMDdown#shiYanBaoGao.docx】
- 等待二進(jìn)制數(shù)據(jù)下載
- 保存文件到本地
//下載文件
downLoadBtn.onClick.AddListener(async () =>
{
//【1】文件名
var file = "shiYanBaoGao.docx";
Debug.Log("從服務(wù)器下載文件");
//COMD + down + # + shiYanBaoGao.docx
//【2】請(qǐng)求下載實(shí)驗(yàn)報(bào)告
var cmdText = $"COMDdown#{file}"; //[COMD][down][#][shiYanBaoGao.docx]
await Connection.instance.websocket.SendText(cmdText);
//【3】等待接收實(shí)驗(yàn)報(bào)告
var data = await Connection.instance.ReceiveBinaryAsync(file);
//【4】保存文件
Debug.Log($"收到文件{file},大小為{data.Length}");
FileSave.instance.SaveFileBytes(file,data);
});
(3)、客戶端從服務(wù)器下載時(shí)的異步等待實(shí)現(xiàn)
客戶端申請(qǐng)下載某個(gè)文件的時(shí)候邏輯:
- 客戶端創(chuàng)建下載任務(wù)
- 客戶端收到下載數(shù)據(jù)時(shí)判斷是否是某個(gè)任務(wù)的下載需求,是則把收到的數(shù)據(jù)寫入該任務(wù)緩沖里面
- 下載完成后讀取收到的數(shù)據(jù),并從列表中刪除該任務(wù)
任務(wù)列表的定義:
/// <summary>
/// 下載任務(wù)列表: 客戶端申請(qǐng)下載某個(gè)文件的時(shí)候使用。
/// private List<DownFileTask> DownFileTasks = new List<DownFileTask>();
/// </summary>
[Serializable]
public class DownFileTask
{
/// <summary>
/// 任務(wù)ID
/// </summary>
public int taskID;
/// <summary>
/// 要下載的文件的名字:
/// </summary>
public string fileName;
/// <summary>
/// 下載狀態(tài):false-未下載,true-下載成功
/// </summary>
public bool state;
/// <summary>
/// 文件的數(shù)據(jù):二進(jìn)制信息
/// </summary>
public List<byte> data;
}
異步等待下載文件:文章來源:http://www.zghlxwxcb.cn/news/detail-402820.html
/// <summary>
/// 等待接收文件
/// </summary>
/// <param name="fileName">文件名</param>
/// <returns></returns>
public async UniTask<byte[]> ReceiveBinaryAsync(string fileName)
{
//【1】創(chuàng)建下載任務(wù)
var taskId = CreatTask(fileName, ref DownFileTasks);
//【2】OnMessage(bytes[])+ ()=>{}中更新,收到文件,寫入數(shù)據(jù)
//在ParseBytes()里
Debug.Log(DownFileTasks.Where(x => x.taskID == taskId).All(x => x.state));
Debug.Log("開始等待下載......");
//【3】等待下載
await UniTask.WaitUntil
(
() => DownFileTasks.Where(x => x.taskID == taskId).All(x => x.state) == true
);
//【4】提取數(shù)據(jù)
Debug.Log("提取數(shù)據(jù)......");
var data = DownFileTasks.First(x => x.taskID == taskId).data;
//【5】刪除下載任務(wù)
Debug.Log("刪除下載任務(wù)......");
DeleteTask(taskId, ref DownFileTasks);
//【6】返回?cái)?shù)據(jù)
return data.ToArray();
}
6、客戶端socket的主要事件綁定
- 連接打開時(shí)…
- 連接出錯(cuò)時(shí)…
- 連接關(guān)閉時(shí)…
- 收到二進(jìn)制數(shù)據(jù)時(shí)…
/// <summary>
/// 連接服務(wù)器
/// </summary>
/// <param name="ip">服務(wù)器ip</param>
/// <param name="port">服務(wù)器端口號(hào)</param>
/// <returns></returns>
async UniTask<bool> ConnectServer(string ip, int port)
{
bool rtn = false;
try
{
//websocket = new WebSocket("ws://192.168.0.137:8081");
Debug.Log($"ws://{ip}:{port}");
websocket = new WebSocket($"ws://{ip}:{port}");
//連接打開時(shí)...
websocket.OnOpen += () =>
{
hasConnected = true;
userID = iptfdUsername.text;
Debug.Log("Connection open!");
textLog.text = $"Connection open! {Time.realtimeSinceStartup} {Environment.NewLine} {textLog.text}";
websocket.SendText($"COMDname#{userID}"); //發(fā)送用戶id
};
//連接出錯(cuò)時(shí)...
websocket.OnError += (e) =>
{
Debug.Log("Error! " + e);
textLog.text = $"Error:{e} {Time.realtimeSinceStartup} {Environment.NewLine} {textLog.text}";
};
//連接關(guān)閉時(shí)...
websocket.OnClose += (e) =>
{
hasConnected = false;
Debug.Log("連接被關(guān)閉了!");
textLog.text = $"連接被關(guān)閉了! {Time.realtimeSinceStartup} {Environment.NewLine} {textLog.text}";
};
//收到二進(jìn)制數(shù)據(jù)時(shí)...
websocket.OnMessage += (bytes) =>
{
//解析數(shù)據(jù)
ParseBytes(bytes);
};
//開始連接
await websocket.Connect();
rtn = true;
}
catch (Exception e)
{
Debug.Log($"連接服務(wù)器出錯(cuò):{e.Message}");
textLog.text = $"連接服務(wù)器出錯(cuò):{e.Message}";
rtn = false;
}
return rtn;
}
7、服務(wù)器端socket的主要事件綁定
- 連上時(shí)…
- 斷開時(shí)…
- 收到字符串?dāng)?shù)據(jù)時(shí)…
- 收到二進(jìn)制數(shù)據(jù)時(shí)…
//var server = new WebSocketServer("ws://192.168.0.137:8081");
var server = new WebSocketServer($"ws://{ip}:{port}");
server.Start(socket =>
{
//連上ss
socket.OnOpen = () =>
{
this.Invoke(new Action(() =>
{
//textBoxLog.Text += $"有新用戶連入:{socket.ConnectionInfo.ClientIpAddress} {Environment.NewLine} {textBoxLog.Text}";
AddText($"有新用戶連入:{socket.ConnectionInfo.ClientIpAddress}");
}));
//報(bào)錯(cuò)的寫法
//textBoxLog.Text = $"有新用戶連入:{socket.ConnectionInfo.ClientIpAddress} \n {textBoxLog.Text}";
//SetTextLog($"有新用戶連入:{socket.ConnectionInfo.ClientIpAddress}");
Debug.WriteLine($"有新用戶連入:{socket.ConnectionInfo.ClientIpAddress}");
//回復(fù)一個(gè)信息
//SendCommand(socket);
};
//斷開
socket.OnClose = () =>
{
Debug.WriteLine($"用戶斷開連接:{socket.ConnectionInfo.ClientIpAddress}");
this.Invoke(new Action(() =>
{
AddText($"用戶斷開連接:{socket.ConnectionInfo.ClientIpAddress}");
}));
UserSockets.First(x => x.socket.ConnectionInfo.Id == socket.ConnectionInfo.Id).connected = false;
};
//收到string信息
socket.OnMessage = message =>
{
Debug.WriteLine($"收到一條消息,來自:{socket.ConnectionInfo.ClientIpAddress}");
Debug.WriteLine(message);
this.Invoke(new Action(() =>
{
AddText($"收到一條消息,來自:{socket.ConnectionInfo.ClientIpAddress}");
AddText($"{message}");
}));
//解析客戶端字符串命令
ParseString(message,socket);
};
//收到二進(jìn)制信息
socket.OnBinary = bytes =>
{
var userName = UserSockets.First(x => x.socket.ConnectionInfo.Id == socket.ConnectionInfo.Id)
.userID;
Debug.WriteLine($"收到二進(jìn)制數(shù)據(jù),長(zhǎng)度為{bytes.Length}Bytes,來自ip:{socket.ConnectionInfo.ClientIpAddress},userID ={userName}");
this.Invoke(new Action(() =>
{
AddText($"收到二進(jìn)制數(shù)據(jù),長(zhǎng)度為{bytes.Length}Bytes,來自ip:{socket.ConnectionInfo.ClientIpAddress},userID ={userName}");
}));
var head = Encoding.UTF8.GetString(bytes.Take(4).ToArray());
switch (head)
{
case "BINA":
//收到二進(jìn)制文件
ParseBinaryFile(bytes,socket);
break;
case "other":
//
break;
default:
//
break;
}
};
});
三、todo
實(shí)時(shí)傳輸語音和視頻文章來源地址http://www.zghlxwxcb.cn/news/detail-402820.html
到了這里,關(guān)于Unity使用webSocket與服務(wù)器通信(三)——C#服務(wù)端(Fleck)與Unity客戶端( NativeWebSocket)傳輸多種數(shù)據(jù)數(shù)據(jù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!