Unity3D —— Socket通訊(C#)
前言:
在開始編寫程式碼之前,我們首先需要明確:聯網方式、聯網步驟、資料收發以及協議資料格式
當然在設計時也應該減低程式碼的耦合性,儘量使得網路層可以在其他地方進行復用,這就需要我們進行介面式的開發。我們這裡使用的通訊模式是Socket強連線的通訊方式,並且使用C#作為程式語言,其實與.NET的Socket通訊是一致的。
一、設計思想:
為了方便測試,我直接使用C#寫的一個控制檯應用,作為伺服器,等待客戶端的連線,然後使用Unity建立Socket客戶端去連線伺服器,進行簡單的資料通訊。這麼設計的原因是都基於.net進行開發,也方便理解。
二、實現步驟:
對於網路通訊有所瞭解的都應該知道,資料在網路傳輸的格式必須以位元組流的形式進行,那麼免不了要對位元組流進行寫入和讀出操作,為了方便後面的操作,我們有必要封裝一個讀寫位元組流的操作類,在這裡我定義了一個位元組流的操作類ByteBuffer類,用於將各個型別資料寫入流中,也可從位元組流中讀取各種型別的資料:
using System.IO; using System.Text; using System; namespace Net { public class ByteBuffer { MemoryStream stream = null; BinaryWriter writer = null; BinaryReader reader = null; public ByteBuffer() { stream = new MemoryStream(); writer = new BinaryWriter(stream); } public ByteBuffer(byte[] data) { if (data != null) { stream = new MemoryStream(data); reader = new BinaryReader(stream); } else { stream = new MemoryStream(); writer = new BinaryWriter(stream); } } public void Close() { if (writer != null) writer.Close(); if (reader != null) reader.Close(); stream.Close(); writer = null; reader = null; stream = null; } public void WriteByte(byte v) { writer.Write(v); } public void WriteInt(int v) { writer.Write((int)v); } public void WriteShort(ushort v) { writer.Write((ushort)v); } public void WriteLong(long v) { writer.Write((long)v); } public void WriteFloat(float v) { byte[] temp = BitConverter.GetBytes(v); Array.Reverse(temp); writer.Write(BitConverter.ToSingle(temp, 0)); } public void WriteDouble(double v) { byte[] temp = BitConverter.GetBytes(v); Array.Reverse(temp); writer.Write(BitConverter.ToDouble(temp, 0)); } public void WriteString(string v) { byte[] bytes = Encoding.UTF8.GetBytes(v); writer.Write((ushort)bytes.Length); writer.Write(bytes); } public void WriteBytes(byte[] v) { writer.Write((int)v.Length); writer.Write(v); } public byte ReadByte() { return reader.ReadByte(); } public int ReadInt() { return (int)reader.ReadInt32(); } public ushort ReadShort() { return (ushort)reader.ReadInt16(); } public long ReadLong() { return (long)reader.ReadInt64(); } public float ReadFloat() { byte[] temp = BitConverter.GetBytes(reader.ReadSingle()); Array.Reverse(temp); return BitConverter.ToSingle(temp, 0); } public double ReadDouble() { byte[] temp = BitConverter.GetBytes(reader.ReadDouble()); Array.Reverse(temp); return BitConverter.ToDouble(temp, 0); } public string ReadString() { ushort len = ReadShort(); byte[] buffer = new byte[len]; buffer = reader.ReadBytes(len); return Encoding.UTF8.GetString(buffer); } public byte[] ReadBytes() { int len = ReadInt(); return reader.ReadBytes(len); } public byte[] ToBytes() { writer.Flush(); return stream.ToArray(); } public void Flush() { writer.Flush(); } } }
使用此操作類進行讀寫資料的操作範例如下:
- 讀取資料:
//result是位元組陣列byte[],從中讀取兩個int型別的資料
ByteBuffer buff = new ByteBuffer(result);
int len = buff.ReadShort();
int protoId = buff.ReadShort();
- 寫入資料:
//result是位元組陣列byte[],從寫入兩個不同型別的資料 ByteBuffer buff = new ByteBuffer(); int protoId = ProtoDic.GetProtoIdByProtoType(0); buff.WriteShort((ushort)protoId); buff.WriteBytes(ms.ToArray()); byte[] result = buff.ToBytes();
1.伺服器建立:
在VS中新建一個C#控制檯應用,新建專案完成後將上面定義的ByteBuffer.cs類匯入工程中,然後開始在入口類Program中開始建立Socket伺服器的邏輯。
先引入必要的名稱空間:
using System.Net;
using System.Net.Sockets;
using System.Threading;
基本的步驟如下:
- 建立一個伺服器Socket物件,並繫結伺服器IP地址和埠號;
private const int port = 8088;
private static string IpStr = "127.0.0.1";
private static Socket serverSocket;
static void Main(string[] args)
{
IPAddress ip = IPAddress.Parse(IpStr);
IPEndPoint ip_end_point = new IPEndPoint(ip, port);
//建立伺服器Socket物件,並設定相關屬性
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//繫結ip和埠
serverSocket.Bind(ip_end_point);
//設定最長的連線請求佇列長度
serverSocket.Listen(10);
Console.WriteLine("啟動監聽{0}成功", serverSocket.LocalEndPoint.ToString());
}
完成上述程式碼之後,已經能正常啟動一個伺服器Socket,但是還沒有處理連線監聽邏輯和資料接收,所以執行應用會出現一閃就關掉的情況。- 啟動一個執行緒,並在執行緒中監聽客戶端的連線,為每個連線建立一個Socket物件;
- 建立接受資料和傳送資料的方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Threading;
using Net;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
private static byte[] result = new byte[1024];
private const int port = 8088;
private static string IpStr = "127.0.0.1";
private static Socket serverSocket;
static void Main(string[] args)
{
IPAddress ip = IPAddress.Parse(IpStr);
IPEndPoint ip_end_point = new IPEndPoint(ip, port);
//建立伺服器Socket物件,並設定相關屬性
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//繫結ip和埠
serverSocket.Bind(ip_end_point);
//設定最長的連線請求佇列長度
serverSocket.Listen(10);
Console.WriteLine("啟動監聽{0}成功", serverSocket.LocalEndPoint.ToString());
//在新執行緒中監聽客戶端的連線
Thread thread = new Thread(ClientConnectListen);
thread.Start();
Console.ReadLine();
}
/// <summary>
/// 客戶端連線請求監聽
/// </summary>
private static void ClientConnectListen()
{
while (true)
{
//為新的客戶端連線建立一個Socket物件
Socket clientSocket = serverSocket.Accept();
Console.WriteLine("客戶端{0}成功連線", clientSocket.RemoteEndPoint.ToString());
//向連線的客戶端傳送連線成功的資料
ByteBuffer buffer = new ByteBuffer();
buffer.WriteString("Connected Server");
clientSocket.Send(WriteMessage(buffer.ToBytes()));
//每個客戶端連線建立一個執行緒來接受該客戶端傳送的訊息
Thread thread = new Thread(RecieveMessage);
thread.Start(clientSocket);
}
}
/// <summary>
/// 資料轉換,網路傳送需要兩部分資料,一是資料長度,二是主體資料
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
private static byte[] WriteMessage(byte[] message)
{
MemoryStream ms = null;
using (ms = new MemoryStream())
{
ms.Position = 0;
BinaryWriter writer = new BinaryWriter(ms);
ushort msglen = (ushort)message.Length;
writer.Write(msglen);
writer.Write(message);
writer.Flush();
return ms.ToArray();
}
}
/// <summary>
/// 接收指定客戶端Socket的訊息
/// </summary>
/// <param name="clientSocket"></param>
private static void RecieveMessage(object clientSocket)
{
Socket mClientSocket = (Socket)clientSocket;
while (true)
{
try
{
int receiveNumber = mClientSocket.Receive(result);
Console.WriteLine("接收客戶端{0}訊息, 長度為{1}", mClientSocket.RemoteEndPoint.ToString(), receiveNumber);
ByteBuffer buff = new ByteBuffer(result);
//資料長度
int len = buff.ReadShort();
//資料內容
string data = buff.ReadString();
Console.WriteLine("資料內容:{0}", data);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
mClientSocket.Shutdown(SocketShutdown.Both);
mClientSocket.Close();
break;
}
}
}
}
}
2.客戶端建立:
客戶端連線伺服器的邏輯相對簡單一些,跟伺服器一樣,先把ByteBuffer類匯入到工程中,基本步驟如下:
- 建立一個Socket物件,這個物件在客戶端是唯一的,可以理解為單例模式;
- 使用上面建立Socket連線指定伺服器IP和埠號;
- 接收伺服器資料和傳送資料給伺服器。
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace Net
{
public class ClientSocket
{
private static byte[] result = new byte[1024];
private static Socket clientSocket;
//是否已連線的標識
public bool IsConnected = false;
public ClientSocket(){
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
/// <summary>
/// 連線指定IP和埠的伺服器
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
public void ConnectServer(string ip,int port)
{
IPAddress mIp = IPAddress.Parse(ip);
IPEndPoint ip_end_point = new IPEndPoint(mIp, port);
try {
clientSocket.Connect(ip_end_point);
IsConnected = true;
Debug.Log("連線伺服器成功");
}
catch
{
IsConnected = false;
Debug.Log("連線伺服器失敗");
return;
}
//伺服器下發資料長度
int receiveLength = clientSocket.Receive(result);
ByteBuffer buffer = new ByteBuffer(result);
int len = buffer.ReadShort();
string data = buffer.ReadString();
Debug.Log("伺服器返回資料:" + data);
}
/// <summary>
/// 傳送資料給伺服器
/// </summary>
public void SendMessage(string data)
{
if (IsConnected == false)
return;
try
{
ByteBuffer buffer = new ByteBuffer();
buffer.WriteString(data);
clientSocket.Send(WriteMessage(buffer.ToBytes()));
}
catch
{
IsConnected = false;
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
}
}
/// <summary>
/// 資料轉換,網路傳送需要兩部分資料,一是資料長度,二是主體資料
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
private static byte[] WriteMessage(byte[] message)
{
MemoryStream ms = null;
using (ms = new MemoryStream())
{
ms.Position = 0;
BinaryWriter writer = new BinaryWriter(ms);
ushort msglen = (ushort)message.Length;
writer.Write(msglen);
writer.Write(message);
writer.Flush();
return ms.ToArray();
}
}
}
}
三、樣例測試:
1.客戶端測試:
在Unity中寫一個測試指令碼TestSocket.cs,並將此指令碼綁到當前場景的相機上:
using UnityEngine;
using System.Collections;
using Net;
public class TestSocket : MonoBehaviour {
// Use this for initialization
void Start () {
ClientSocket mSocket = new ClientSocket();
mSocket.ConnectServer("127.0.0.1", 8088);
mSocket.SendMessage("伺服器傻逼!");
}
// Update is called once per frame
void Update () {
}
}
2.啟動伺服器:
在Visual Studio中點選執行按鈕,啟動伺服器:
啟動正常的話,會彈出一個視窗如下圖所示:
3.開始連線:
在Unity中運行當前場景,檢視輸出日誌,假如連線成功,輸出如下:
檢視伺服器視窗,發現雙向通訊都正常:
四、總結:
這裡測試案例其實很簡單,協議沒有進行如何優化,單純地傳送字串資料而已,假如針對複雜的資料的話,需要建立完整打包和解包協議資料的機制,而且必要時還需要對資料進行加密操作。
相關推薦
Unity3D —— Socket通訊(C#)
前言: 在開始編寫程式碼之前,我們首先需要明確:聯網方式、聯網步驟、資料收發以及協議資料格式 當然在設計時也應該減低程式碼的耦合性,儘量使得網路層可以在其他地方進行復用,這就需要我們進行介面式的開發。我們這裡使用的通訊模式是Socket強連線
Socket通訊——C++伺服器端和Java客戶端
//更新 這件事可以用現有的序列化框架來做 比如 protobuf 一句話來說就是,C++和Java 通過socket進行通訊、資料傳輸,通過傳送“位元組流”即可。 位元組對於C++和java來說是通用的,但是傳輸的過程有許多問題需要注意,我為了弄清楚這個過程,查了一些資料
java與c語言之間的socket通訊—c客戶端java伺服器端
寫在前面的宣告:程式例子均執行在ubuntu(是一個以桌面應用為主的Linux作業系統)上。當然你也可以把java執行在其它系統上,這裡只是為了方便。 上一篇文章已經說明了關於socket的一些知識,but這是遠遠不夠的,我相信只要你感興趣,學習它並不是什麼難事。 好吧,我
基於Socket通訊(C#)和WebSocket協議(net)編寫的兩種聊天功能(文末附源碼下載地址)
消息 客戶端和服務器端 win 屬性 比較 com 端口 caption .html 轉載:https://www.cnblogs.com/xiongze520/p/10338802.html 今天我們來盤一盤Socket通訊和WebSocket協議在即時通訊的小應
Socket通訊 C#寫服務商 Delphi客戶端
摘要: 最近在做Wince開發,搞一個超市賣場採用手持機盤點的現場作業模組。通訊部分的實現有兩種,其一是通過USB線把資料拷到PDA 上,掃描條碼後,查詢本地的商品資料庫(用SQLite做本地庫),盤點完成後再通過USB把盤點結果匯入伺服器(還是通過讀取SQLi
C#socket通訊服務器(連接狀態監控)
del endpoint etc acc ipa ack ipaddress ava listening class SocketServerManager { public delegate void ConnectStateEventHandler
C#Socket通訊基礎(非同步Socket通訊TCP)伺服器與客戶端
一、效果圖 二、伺服器端程式碼(原始碼下載地址:https://download.csdn.net/download/xiaochenxihua/10748789) using System; using System.Collections.Generic; using System
C#Socket通訊基礎知識(非同步Socket通訊TCP)
一、Socket通訊基礎 《1》TCP/IP層次模型 這裡只討論重要的四層 01,應用層(Application):應用層是個很廣泛的概念,有一些基本相同的系統級TCP/IP應用以及應用協議,也有許多的企業應用和網際網路應用。http
C#Socket通訊基礎(非同步Socket通訊UDP)
一、通訊原理參考https://blog.csdn.net/xiaochenXIHUA/article/details/83446031。 非同步通訊解決同步通訊的缺點可以雙向傳送和接收資訊;非同步通訊思路是引入多執行緒機制,在一個程序中使用兩個執行緒,分別處理接收執行緒與傳送執行緒,這種機制稱
C#Socket通訊基礎(同步Socket通訊UDP)
一、UDP通訊原理 UDP協議使用無連線的套接字,因此不需要再網路裝置之間傳送連線資訊,但是必須用Bind方法繫結到一個本地地址/埠上。 ①建立套接字 ②繫結IP地址和埠作為伺服器端 ③直接使用SendTo/ReceiveFrom來執行操作 注意:同步Socket(UDP)通訊存
C# FrameworkAPI之Socket通訊
服務端: 1:建立一個socket的物件 Socket socketserver=new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 第一個引數是指定socket物件使用的定址方案,
[Visual Studio C++][Socket程式設計] Socket通訊原理詳細講解
(本文參考:https://www.cnblogs.com/wangcq/p/3520400.html 在原文的基礎上進行了擴充。) 對TCP/IP、UDP、Socket程式設計這些詞你不會很陌生吧?隨著網路技術的發展,這些詞充斥著我們的耳朵。那麼我想問
C#一個伺服器端多個客戶端Socket通訊
原理: 啟動服務端後,服務端通過持續監聽客戶端發來的請求,一旦監聽到客戶端傳來的資訊後,兩端便可以互發資訊了。伺服器端需要繫結一個IP和埠號,用於客戶端在網路中尋找並建立連線。資訊傳送原理:將手動輸入字串資訊轉換成機器可以識別的位元組陣列,然後呼叫套接字的Send()方法將位元組陣列傳送出去
Untiy中用C#實現TCP通訊(Socket通訊)服務端與客戶端皆可
簡而言之,TCP通訊原理大家可以從各種網路文獻上找到,這裡不做贅述。 只提出C#實現TCP通訊的一般方法和常用程式碼工具供第一次接觸TCP通訊的玩家參考,老玩家繞道。。。 為了方便大家理解我的程式碼,會適當提及通訊遠離。 1、建立服務端,TCP連線的基本: using U
C#實現Socket通訊(同時監聽多客戶端)
//建立socket物件 //第一個引數:設定網路定址的協議、第二引數設定資料傳輸的方式、第三個引數設定通訊協議 Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketT
Socket通訊總結(附C++實現)
文章目錄[隱藏] 因為專案需要,服務端需要一個SOCKET來接收客戶端的請求,好吧,沒辦法度娘哇,結果很多都是linux的例子,功夫不負有心人啊,終於找到個demo,並且客戶端程式碼詳盡,記錄之,以便以後檢視。 一、Socket是什麼 Socket
Socket通訊中粘包分包問題的介紹和解決(C#)
最近在做Unity區域網時,用到了Socket通訊基於TCP協議,然後使用非同步方式,主要用到了BeginAccept和BeginReceive方法 然而就可以實現非同步通訊,然而還是要解決粘包和分包問題 這裡我先說明一下什麼是分包和粘包,TCP提供面向連線的、可靠的資料流傳輸,所以當我們傳
C++:實現socket通訊(TCP/IP)例項
首先宣告,博主之前從來沒有寫過通訊方面的東西,這次之所以寫這個是因為專案需要,因此本文主要介紹一個使用C++語言及Socket來實現TCP/IP通訊的例項,希望可以幫助入門者。 一、什麼是TCP/IP? TCP提供基於IP環境下的資料可靠性傳
c# socket通訊實現簡單的視窗資訊互相傳送 (聊天室的deom)
這次使用socket來實現簡單的視窗資訊互相傳送 首先我們建立一個伺服器端 services (winfrom檔案) 這邊注意。你的ip地址和埠號可以 命令建+r 開啟cmd 輸入ipconfig找到自己的ip地址 每個人的ip地址都不一樣 程式碼部
.net平臺下C#socket通訊(上)
在開始介紹socket前先補充補充基礎知識,在此基礎上理解網路通訊才會順理成章,當然有基礎的可以跳過去了。都是廢話,進入正題。 TCP/IP:Transmission Control Protocol/Internet Protocol,傳輸控制協議/因特網互聯協議,又