2019年10月19日─大幅度修改了程式碼,使用起來更直覺了
新的程式在此
======================================================================================================
2016年6月24日 00:51,修改了程式碼,刪掉多餘的部分和BUG
C#的Socket連線要使用非同步的方式設計!不然在等待連線和資料的時候就會卡著不動了...
至於非同步的方式可以和這影片所用的方式一樣,或者和我一樣使用Thread的方式編寫。
有幾篇文章建議先看一下
或許有人在想為什麼要用Socket呢?Unity不是有內建連線功能了?
但這只能用在Unity之間的連線,若是要不同語言甚至是不同IDE做出的程式連線(e.g.使用VS做的程式和Unity之間連線),就要使用到Socket
先創兩個Project,分別命名為Server和Client
(當然,想在同個Project內也是可以,不過要分成兩個Scene,然後分別將這兩個Scene輸出)
在Server儲存一個場景和一些C# script,內容如下
Client也是一樣的步驟
都完成後就開始撰寫程式碼了~~
首先編寫ServerThread.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
class ServerThread
{
//結構,儲存IP和Port
private struct Struct_Internet
{
public string ip;
public int port;
}
private Socket serverSocket;//伺服器本身的Socket
private Socket clientSocket;//連線使用的Socket
private Struct_Internet internet;//宣告結構物件
public string receiveMessage;
private string sendMessage;
private Thread threadConnect;//連線的Thread
private Thread threadReceive;//接收資料的Thread
public ServerThread(AddressFamily family, SocketType socketType, ProtocolType protocolType, string ip, int port)
{
serverSocket = new Socket(family, socketType, protocolType);//new server socket object
internet.ip = ip;//儲存IP
internet.port = port;//儲存Port
receiveMessage = null;//初始化接受的資料
}
//開始傾聽連線需求
public void Listen()
{
//伺服器本身的IP和Port
serverSocket.Bind(new IPEndPoint(IPAddress.Parse(internet.ip), internet.port));
serverSocket.Listen(1);//最多一次接受多少人連線
}
//開始連線
public void StartConnect()
{
//由於連線成功之前程式都會停下,所以必須使用Thread
threadConnect = new Thread(Accept);
threadConnect.IsBackground = true;//設定為背景執行續,當程式關閉時會自動結束
threadConnect.Start();
}
//停止連線
public void StopConnect()
{
try
{
clientSocket.Close();
}
catch (Exception)
{
}
}
//寄送訊息
public void Send(string message)
{
if (message == null)
throw new NullReferenceException("message不可為Null");
else
sendMessage = message;
SendMessage();//由於資料傳遞速度很快,沒必要使用Thread
}
public void Receive()
{
//先判斷原先的threadReceive若還在執行接收檔案的工作,則直接結束
if (threadReceive != null && threadReceive.IsAlive == true)
return;
//由於在接收到所有資料前都會停下,所以必須使用Thread
threadReceive = new Thread(ReceiveMessage);
threadReceive.IsBackground = true;//設定為背景執行續,當程式關閉時會自動結束
threadReceive.Start();
}
private void Accept()
{
try
{
clientSocket = serverSocket.Accept();//等到連線成功後才會往下執行
//連線成功後,若是不想再接受其他連線,可以關閉serverSocket
//serverSocket.Close();
}
catch (Exception)
{
}
}
private void SendMessage()
{
try
{
if (clientSocket.Connected == true)//若成功連線才傳遞資料
{
//將資料進行編碼並轉為Byte後傳遞
clientSocket.Send(Encoding.ASCII.GetBytes(sendMessage));
}
}
catch (Exception)
{
}
}
private void ReceiveMessage()
{
if (clientSocket.Connected == true)
{
byte[] bytes = new byte[256];//用來儲存傳遞過來的資料
long dataLength = clientSocket.Receive(bytes);//資料接收完畢之前都會停在這邊
//dataLength為傳遞過來的"資料長度"
receiveMessage = Encoding.ASCII.GetString(bytes);//將傳過來的資料解碼並儲存
}
}
}
再來是Server.cs
using UnityEngine;
using System.Net.Sockets;
using System.Collections;
public class Server : MonoBehaviour
{
private ServerThread st;
private bool isSend;//儲存是否發送訊息完畢
private void Start()
{
//開始連線,設定使用網路、串流、TCP
st = new ServerThread(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp, "127.0.0.1", 8000);
st.Listen();//讓Server socket開始監聽連線
st.StartConnect();//開啟Server socket
isSend = true;
}
private void Update()
{
if (st.receiveMessage != null)
{
Debug.Log("Client:" + st.receiveMessage);
st.receiveMessage = null;
}
if (isSend == true)
StartCoroutine(delaySend());//延遲發送訊息
st.Receive();
}
private IEnumerator delaySend()
{
isSend = false;
yield return new WaitForSeconds(1);//延遲1秒後才發送
st.Send("Hello~ My name is Server");
isSend = true;
}
private void OnApplicationQuit()//應用程式結束時自動關閉連線
{
st.StopConnect();
}
}
再來是ClientThread.cs,和ServerThread.cs類似
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
class ClientThread
{
public struct Struct_Internet
{
public string ip;
public int port;
}
private Socket clientSocket;//連線使用的Socket
private Struct_Internet internet;
public string receiveMessage;
private string sendMessage;
private Thread threadReceive;
private Thread threadConnect;
public ClientThread(AddressFamily family, SocketType socketType, ProtocolType protocolType, string ip, int port)
{
clientSocket = new Socket(family, socketType, protocolType);
internet.ip = ip;
internet.port = port;
receiveMessage = null;
}
public void StartConnect()
{
threadConnect = new Thread(Accept);
threadConnect.Start();
}
public void StopConnect()
{
try
{
clientSocket.Close();
}
catch(Exception)
{
}
}
public void Send(string message)
{
if (message == null)
throw new NullReferenceException("message不可為Null");
else
sendMessage = message;
SendMessage();
}
public void Receive()
{
if (threadReceive != null && threadReceive.IsAlive == true)
return;
threadReceive = new Thread(ReceiveMessage);
threadReceive.IsBackground = true;
threadReceive.Start();
}
private void Accept()
{
try
{
clientSocket.Connect(IPAddress.Parse(internet.ip), internet.port);//等待連線,若未連線則會停在這行
}
catch (Exception)
{
}
}
private void SendMessage()
{
try
{
if (clientSocket.Connected == true)
{
clientSocket.Send(Encoding.ASCII.GetBytes(sendMessage));
}
}
catch (Exception)
{
}
}
private void ReceiveMessage()
{
if (clientSocket.Connected == true)
{
byte[] bytes = new byte[256];
long dataLength = clientSocket.Receive(bytes);
receiveMessage = Encoding.ASCII.GetString(bytes);
}
}
}
再來是Client.cs,和Server.cs類似
using UnityEngine;
using System.Collections;
using System.Net.Sockets;
public class Client : MonoBehaviour
{
private ClientThread ct;
private bool isSend;
private bool isReceive;
private void Start()
{
ct = new ClientThread(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp, "127.0.0.1", 8000);
ct.StartConnect();
isSend = true;
}
private void Update()
{
if (ct.receiveMessage != null)
{
Debug.Log("Server:" + ct.receiveMessage);
ct.receiveMessage = null;
}
if (isSend == true)
StartCoroutine(delaySend());
ct.Receive();
}
private IEnumerator delaySend()
{
isSend = false;
yield return new WaitForSeconds(1);
ct.Send("Hello~ My name is Client");
isSend = true;
}
private void OnApplicationQuit()
{
ct.StopConnect();
}
}
都好之後就可以直接Run了~~
結果如下
ServerThread.cs
ClientThread.cs
這兩個程式碼可以直接搬到VS使用(當然只能給C#,不過若是打包成dll也可以給其他語言使用)
由於當前程式碼只能1對1連線,若要1對多則ServerThread內的clientSocket要改為陣列或List,當有人連線後就將該clientSocket內的一個元素分配給該人
留言列表