close

2019年10月19日─大幅度修改了程式碼,使用起來更直覺了

新的程式在此

 

 

======================================================================================================

2016年6月24日  00:51,修改了程式碼,刪掉多餘的部分和BUG

 

C#的Socket連線要使用非同步的方式設計!不然在等待連線和資料的時候就會卡著不動了...

至於非同步的方式可以和這影片所用的方式一樣,或者和我一樣使用Thread的方式編寫。

有幾篇文章建議先看一下

Unity C# Thread 注意事項

Thread類別

多執行續程序的參數和回傳值

Socket

 

或許有人在想為什麼要用Socket呢?Unity不是有內建連線功能了?

Unity 區域網路(LAN)連線───使用Network

但這只能用在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內的一個元素分配給該人

arrow
arrow
    全站熱搜

    Yang 發表在 痞客邦 留言(8) 人氣()