Socket程式設計之TcpClient、TcpListener在Unity3D中的應用
經過一段時間的學習,對於TCP/IP協議有了一定的認識,大概知道了TCP協議通訊的原理,接下來主要實現用TcpClient、TcpListener兩個封裝類和stream來實現一個小的demo:用vs建一個工程作為伺服器,然後用Unity引擎作為客戶端,實現客戶端和伺服器,客戶端和客戶端之間的通訊。
由於入坑試驗多次,這裡強調一下通訊時候的資料轉換,當在客戶端傳送資訊的時候,會將使用者輸入的字串轉為byte型陣列(要通過NetworkStream stream這個物件寫入)
程式碼塊一:
void SendMessage(string myWrite) {
byte[] data = Encoding.UTF8.GetBytes(myWrite);
stream.Write(data, 0, data.Length);
}
伺服器端通過流stream來讀取資料,並且將資料重新轉換為字串:
程式碼塊二:
byte[] data = new byte[1024];
int length = stream.Read(data, 0, data.Length);
string message = Encoding.UTF8.GetString(data, 0, length);
轉換為字串後就可以在伺服器端列印輸出了,然後需要再次將字串資料轉為byte[]陣列,重新傳回客戶端
程式碼塊三:(注意!!!在伺服器端一定要將客戶端讀取的byte陣列資料先轉為字串,再將字串重新轉為byte[]陣列寫入到客戶端,至於原因,前面踩過坑,發現,如果直接將客戶端讀取的byte陣列寫入到客戶端,在Unity的UGUI的文字框顯示會出現問題,UGUI只會顯示使用者輸入的第一條資料。總之,這一過程必不可少
public void SendMessage(string message) {
byte[] data = Encoding.UTF8.GetBytes(message);
stream.Write(data, 0, data.Length);
}
然後在客戶端讀取伺服器寫入過來的資料,將其用byte陣列存放,並轉為string字串
程式碼塊四:
byte[] data = new byte[1024];
int length = stream.Read(data, 0, data.Length);
message = Encoding.UTF8.GetString(data, 0, length);
最後將字串通過Unity引擎的UGUI和基本程式碼顯示到Unity場景中
完整程式碼如下
伺服器端(用vs建立一個工程)由一個主方法和自定義類組成:
額外新增名稱空間:using System.Net; using System.Net.Sockets; using System.Threading;
主方法如下:
class Program
{
public static List<ClientStream> ls = new List<ClientStream>();
public static void BroadcastMessage(string message)
{
List<ClientStream> nols = new List<ClientStream>();
foreach (var client in ls)
{
if (client.client.Connected)
{
client.SendMessage(message);
}
else
{
nols.Add(client);
}
}
foreach (var i in nols)
{
ls.Remove(i);
}
}
static void Main(string[] args)
{
//1.建立一個TCPListener物件,例項化綁定了IP和埠號
TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Parse("192.168.1.101"), 7788));
//2.開始偵聽
listener.Start();
Console.WriteLine("已開啟伺服器...");
TcpClient client;
NetworkStream stream;
byte[] data = new byte[1024]; //data作為一個容器用來接收
//讀取流裡面的資料,存放到data的0索引位置,每次最大讀取1024個位元組
while (true)
{
//3.等待客戶端連線
client = listener.AcceptTcpClient();
Console.WriteLine("一個客戶端已連線");
//4.取得客戶端傳送過來的資料
stream = client.GetStream();
ClientStream clientStream = new ClientStream(stream, client);
ls.Add(clientStream);
}
stream.Close();
client.Close();
listener.Stop();
Console.ReadKey();
}
}
自定義類如下:
class ClientStream
{
private Thread t;
private NetworkStream stream;
public TcpClient client;
public ClientStream(NetworkStream stream, TcpClient client) {
this.stream = stream;
this.client = client;
t = new Thread(ReceiveMessage);
t.Start();
}
private void ReceiveMessage() {
while (true)
{
if (client.Connected == false)
{
Console.WriteLine("客戶端斷開連線");
stream.Close();
break; //跳出迴圈,終止執行緒的執行
}
byte[] data = new byte[1024];
int length = stream.Read(data, 0, data.Length);
string message = Encoding.UTF8.GetString(data, 0, length);
Console.WriteLine("收到了訊息:" + message);
Program.BroadcastMessage(message);
}
}
public void SendMessage(string message) {
byte[] data = Encoding.UTF8.GetBytes(message);
stream.Write(data, 0, data.Length);
}
}
客戶端指令碼(建立一個Unity專案,需額外新增名稱空間using System.Net; using System.Net.Sockets; using System.Text;
using System.Threading;):
public class ClientTest : MonoBehaviour {
public Text textContainer;
public InputField text;
private string address = "192.168.1.101";
private int port = 7788;
private TcpClient client;
private NetworkStream stream;
private Thread t;
private string message;
// Use this for initialization
void Start () {
ConnectToServer();
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.Return))
{
OnSendButtonClick();
}
if (message != "" && message != null)
{
textContainer.text += "\n" + message;
Debug.Log("message2:" + message);
Debug.Log("文字框2:" + textContainer.text);
message = "";
}
}
//客戶端的連線
void ConnectToServer() {
client = new TcpClient(address, port);
Debug.Log("client.Available:" + client.Available);
//if (client.Available == 0)
// Debug.Log("伺服器不存在");
stream = client.GetStream();
Debug.Log("客戶端連線");
t = new Thread(ReceiveMessage);
t.Start();
}
//客戶端傳送訊息
void SendMessage(string myWrite) {
byte[] data = Encoding.UTF8.GetBytes(myWrite);
stream.Write(data, 0, data.Length);
}
//客戶端接收訊息
void ReceiveMessage() {
while (true)
{
if (client.Connected == false)
{
break;
}
byte[] data = new byte[1024];
int length = stream.Read(data, 0, data.Length);
message = Encoding.UTF8.GetString(data, 0, length);
Debug.Log("從伺服器受到了:"+message);
}
}
//按鈕偵聽函式
public void OnSendButtonClick() {
if (text.text != null && text.text != "" && client.Connected) {
string myWrite= text.text;
SendMessage(myWrite);
text.text = "";
}
}
void OnDestroy() {
stream.Close();
client.Close();
}
}
最後先編譯vs這邊的程式,即啟動伺服器,
然後執行Unity,即啟動客戶端,
也可以將Unity專案打包釋出PC端,這樣就可以有多個客戶端了
以上只是一個小的demo,望各位大佬不吝賜教!!!