2010年8月11日 星期三

Unity 3D Client/Server Socket Connection Sample Code


版權歸 UnityBuster.blogspot.com 所有。轉載請註明出處!
Welcome to link to this blog when referencing the article!

I'v been looking everywhere to find a socket connection sample code for Unity 3D. Although there are many sites/blogs pointed out some feature, but not a sample in whole can be found. Finally, I put some resources that I found and came out this sample. Hope those people who are looking for the answer can get one step further via this article.

You can use .net sockets to connect between Unity and socket-based applications. Furthermore, you can use most of the .net 2.0 API. If you happen to have a Unity Pro, you can write your own C++ Dlls, be noted, for the security reason, C++ Dlls can't be used with Web Player.

.NET Sockets is kind of TCP/IP Sockets. Existing network class of .NET, ex: TcpClient(client side) and TcpListener(server side), they include socket other feature support, and are easier then using Socket directly.

Below is a simple implementation. There are 2 parts: server side and client side. Server side is the Server side TcpListener code listed below. Just compile it and run it to wait for the connection. The Server was a sample code from MSDN, it can handle one Client connection at a time. As an example to make the socket connection work is enough. I'll keep on working on a simple server code that can handle multiple clients. Wish I can get it in the near future.

To do the client in the Unity, you can create a new Project, then create a JavaScript and rename to GUI_Script, copy and past the code in 'GUI_Scripts.js' listed below. When you finish this step, press left key to grab "CUI_Script" inside Unity/Project sub-window and drop on the "Main Camera" object inside Hierarchy sub-window, the script can then be attached to the Camera, you can then see the GUI when running the game. We then create a folder in the Unity/Project sub-window and rename to "Standard Assets", create a C# Script, rename it to "s_TCP", copy and past the code in the 's_TCP.cs' below. Now, run the unity game, you can try the result now.

Client side C# TCP script is attach to a GUI JavaScript. The GUI JavaScript contains a "Connect" button, a "Level 1" button, and a "Level 2" button; a text field and a "Disconnect" button. In the beginning of the game running, there is only one "Connect" button display on the screen; after the player press the button, the Unity game client will connect to the Server and establish a socket connection. The "Connect" button disappear, and the "Level 1", "Level 2" button, text filed, and "Disconnect" button show up. Whenever press the "Level 1" or "Level 2" button, the text field will use WriteSocket() function in the GUI JavaScript to send the preset string to the server, the server will then convert the lower case text to upper case text and send the result string to the client. The GUI JavaScript will then use the ReadSocket() function to read the return string and display in the text field. If you press the "Disconnect" button, the connection will be closed and the screen will change back to display "Connect" button only.



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

Code listed below is for Server side TcpListener:

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

class MyTcpListener
{
  public static void Main()
  { 
    TcpListener server=null;   
    try
    {
      // Set the TcpListener on port 13000.
      Int32 port = 13000;
      IPAddress localAddr = IPAddress.Parse("127.0.0.1");
      
      // TcpListener server = new TcpListener(port);
      server = new TcpListener(localAddr, port);

      // Start listening for client requests.
      server.Start();
         
      // Buffer for reading data
      Byte[] bytes = new Byte[256];
      String data = null;
      int counter = 0;


      // Enter the listening loop.
      while(true) 
      {
        Console.Write("Waiting for a connection... ");
        
        // Perform a blocking call to accept requests.
        // You could also user server.AcceptSocket() here.
        TcpClient client = server.AcceptTcpClient();
        counter++;
        Console.WriteLine("#"+counter+" Connected!");

        data = null;

        // Get a stream object for reading and writing
        NetworkStream stream = client.GetStream();

        int i;

        // Loop to receive all the data sent by the client.
        while((i = stream.Read(bytes, 0, bytes.Length))!=0) 
        {   
          // Translate data bytes to a ASCII string.
          data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
          Console.WriteLine("Received: {0}", data + counter);
       
          // Process the data sent by the client.
          data = data.ToUpper();

          byte[] msg = System.Text.Encoding.ASCII.GetBytes(data+"Client counter:"+counter);

          // Send back a response.
          stream.Write(msg, 0, msg.Length);
          Console.WriteLine("Sent: {0}", data + "Client counter:"+counter);            
        }
         
        // Shutdown and end connection
        client.Close();
      }
    }
    catch(SocketException e)
    {
      Console.WriteLine("SocketException: {0}", e);
    }
    finally
    {
       // Stop listening for new clients.
       server.Stop();
    }

      
    Console.WriteLine("\nHit enter to continue...");
    Console.Read();
  }   
}


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

Code listed below is a C# TCP program 's_TCP.cs', which was graped from Unity Forum and made some modification:

using UnityEngine;
using System.Collections;
using System;
using System.IO;
using System.Net.Sockets;

public class s_TCP : 
MonoBehaviour {
    bool socketReady = false;

    TcpClient mySocket;
    NetworkStream theStream;
    StreamWriter theWriter;
    StreamReader theReader;
    String Host = "localhost";
    Int32 Port = 13000; 

    // Use this for initialization
    void Start() {

    }

    // Update is called once per frame
    void Update() {

    }

    public void setupSocket() {
        try {
            mySocket = new TcpClient(Host, Port);
            theStream = mySocket.GetStream();
            theWriter = new StreamWriter(theStream);
            theReader = new StreamReader(theStream);
            socketReady = true;
        }
        catch (Exception e) {
            Debug.Log("Socket error:" + e);
        }
    }

    public void writeSocket(string theLine) {
        if (!socketReady)
            return;
  String tmpString = theLine + "\r\n";
        theWriter.Write(tmpString);
        theWriter.Flush();
    }

    public String readSocket() {
        if (!socketReady)
            return "";
        if (theStream.DataAvailable)
            return theReader.ReadLine();
        return "";
    }

    public void closeSocket() {
        if (!socketReady)
            return;
        theWriter.Close();
        theReader.Close();
        mySocket.Close();
        socketReady = false;
    }
 
    public void maintainConnection(){
 if(!theStream.CanRead) {
  setupSocket();
 }
    }
} // end class s_TCP

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

This is a JavaScript 'GUI_Scripts.js':

private var textFieldString = "Socket Testing String";
private var myTCP; 

function Awake() {
 myTCP = gameObject.AddComponent(s_TCP);
}

function OnGUI () {
 if(myTCP.socketReady==false) {
  if (GUI.Button (Rect (20,10,80,20),"Connect")) {
   myTCP.setupSocket();
  }
 }
 else {
  myTCP.maintainConnection();

  if (GUI.Button (Rect (20,40,80,20), "Level 1")) {
   myTCP.writeSocket(" The is from Level 1 Button");
   textFieldString=myTCP.readSocket();
  }
  if (GUI.Button (Rect (20,70,80,20), "Level 2")) {
   myTCP.writeSocket(" The is from Level 2 Button");
   textFieldString=myTCP.readSocket();
  }
  
  textFieldString = GUI.TextField (Rect (25, 100, 300, 30), textFieldString);
  
  if (GUI.Button (Rect (20,140,80,20),"Disconnect")) {
   myTCP.closeSocket();
   textFieldString = "Socket Disconnected...";
  }
 }
}

版權歸 UnityBuster.blogspot.com 所有。轉載請註明出處!
Welcome to link to this blog when reference the article!

2010年8月8日 星期日

Unity3D的Socket連線實作範例


版權歸 UnityBuster.blogspot.com 所有。轉載請註明出處!

你可以使用標準的 .net sockets 來連接Unity和其他的socket-based應用軟體. 另外, 你也可以使用大多數的.net 2.0 API. 如果你有Unity Pro, 你還可以自己撰寫 C++ Dlls來使用, 需注意的是, 為了安全的理由, C++ Dlls不可以用在Web Player之中.

.NET Sockets 基本來說就是 TCP/IP Sockets. .NET 現有的network classe例如:TcpClient(client端)和 TcpListener(伺服器端), 它們包括了Socket及其他的支援功能, 這比直接使用Socket來得容易一些.

以下為一個簡單的試作.  若是想要實際在Unity中驗證,  可以開啟一個新的Project, 先create一個JavaScript並更名為GUI_Script後將以下'GUI_Scripts.js'的內容貼入. 完成後, 在Unity/Project子視窗中左鍵按住此一物件並移動到Hierarchy子視窗中的"Main Camera"物件上放開左鍵, 此一腳本即可和Camera連接上, 執行時就可以看到GUI在畫面上顯示.  接下來在Unity/Project視窗中create一個folder並更名為"Standard Assets", 然後在此一folder中create一個C Sharp Script並更名為s_TCP後將以下的's_TCP.cs'內容貼入.  將最上端的server程式編譯後執行, 然後執行Unity的client即可看到結果.

Client端的C# TCP腳本程式是附加在一個JavaScript GUI腳本上的.這個GUI腳本包含一個"Connect"按鈕, 一個"Level 1"按鈕和一個"Level 2"按鈕, 一個文字框和一個 "Disconnect"按鈕. 開始時, 畫面只會出現一個"Connect"按鈕, 按下按鈕後會把Unity的TcpClient連接到Server端建立Socket連線. 之後"Connect"按鈕會消失, 變而出現"Level 1", "Level 2"按鈕, 文字框, 和"Disconnect"按鈕. 當按下"Level 1"或"Level 2"按鈕, 文字框內利用GUI腳本的 WriteSocket()功能將預設的文字傳送給server, 而server則將收到的文字轉換成大寫傳回. 這個GUI腳本同時也包含了一個 ReadSocket()功能用來讀取server傳回的資訊並顯示在文字框中. 最後按"Disconnect"按鈕時則關閉連線回到"Connect"按鈕的初始畫面

Server端是取自MSDN的一個很簡單的程式碼, 一次只能處理一個Client的連結. 但足以用來驗證連線功能, 日後若想要強化, 則可依個人需求來修改了.

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

以下為Server端 TcpListener 程式碼範例:

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

class MyTcpListener
{
  public static void Main()
  { 
    TcpListener server=null;   
    try
    {
      // Set the TcpListener on port 13000.
      Int32 port = 13000;
      IPAddress localAddr = IPAddress.Parse("127.0.0.1");
      
      // TcpListener server = new TcpListener(port);
      server = new TcpListener(localAddr, port);

      // Start listening for client requests.
      server.Start();
         
      // Buffer for reading data
      Byte[] bytes = new Byte[256];
      String data = null;
      int counter = 0;


      // Enter the listening loop.
      while(true) 
      {
        Console.Write("Waiting for a connection... ");
        
        // Perform a blocking call to accept requests.
        // You could also user server.AcceptSocket() here.
        TcpClient client = server.AcceptTcpClient();
        counter++;
        Console.WriteLine("#"+counter+" Connected!");

        data = null;

        // Get a stream object for reading and writing
        NetworkStream stream = client.GetStream();

        int i;

        // Loop to receive all the data sent by the client.
        while((i = stream.Read(bytes, 0, bytes.Length))!=0) 
        {   
          // Translate data bytes to a ASCII string.
          data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
          Console.WriteLine("Received: {0}", data + counter);
       
          // Process the data sent by the client.
          data = data.ToUpper();

          byte[] msg = System.Text.Encoding.ASCII.GetBytes(data+"Client counter:"+counter);

          // Send back a response.
          stream.Write(msg, 0, msg.Length);
          Console.WriteLine("Sent: {0}", data + "Client counter:"+counter);            
        }
         
        // Shutdown and end connection
        client.Close();
      }
    }
    catch(SocketException e)
    {
      Console.WriteLine("SocketException: {0}", e);
    }
    finally
    {
       // Stop listening for new clients.
       server.Stop();
    }

      
    Console.WriteLine("\nHit enter to continue...");
    Console.Read();
  }   
}


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

以下為一個C# TCP 程式碼 's_TCP.cs', 本程式取自Unity Forum中並加以簡單的修改:

using UnityEngine;
using System.Collections;
using System;
using System.IO;
using System.Net.Sockets;

public class s_TCP : 
MonoBehaviour {
    bool socketReady = false;

    TcpClient mySocket;
    NetworkStream theStream;
    StreamWriter theWriter;
    StreamReader theReader;
    String Host = "localhost";
    Int32 Port = 13000; 

    // Use this for initialization
    void Start() {

    }

    // Update is called once per frame
    void Update() {

    }

    public void setupSocket() {
        try {
            mySocket = new TcpClient(Host, Port);
            theStream = mySocket.GetStream();
            theWriter = new StreamWriter(theStream);
            theReader = new StreamReader(theStream);
            socketReady = true;
        }
        catch (Exception e) {
            Debug.Log("Socket error:" + e);
        }
    }

    public void writeSocket(string theLine) {
        if (!socketReady)
            return;
  String tmpString = theLine + "\r\n";
        theWriter.Write(tmpString);
        theWriter.Flush();
    }

    public String readSocket() {
        if (!socketReady)
            return "";
        if (theStream.DataAvailable)
            return theReader.ReadLine();
        return "";
    }

    public void closeSocket() {
        if (!socketReady)
            return;
        theWriter.Close();
        theReader.Close();
        mySocket.Close();
        socketReady = false;
    }
 
    public void maintainConnection(){
 if(!theStream.CanRead) {
  setupSocket();
 }
    }
} // end class s_TCP

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

以下為一個JavaScript的GUI腳本 'GUI_Scripts.js':

private var textFieldString = "Socket Testing String";
private var myTCP; 

function Awake() {
 myTCP = gameObject.AddComponent(s_TCP);
}

function OnGUI () {
 if(myTCP.socketReady==false) {
  if (GUI.Button (Rect (20,10,80,20),"Connect")) {
   myTCP.setupSocket();
  }
 }
 else {
  myTCP.maintainConnection();

  if (GUI.Button (Rect (20,40,80,20), "Level 1")) {
   myTCP.writeSocket(" The is from Level 1 Button");
   textFieldString=myTCP.readSocket();
  }
  if (GUI.Button (Rect (20,70,80,20), "Level 2")) {
   myTCP.writeSocket(" The is from Level 2 Button");
   textFieldString=myTCP.readSocket();
  }
  
  textFieldString = GUI.TextField (Rect (25, 100, 300, 30), textFieldString);
  
  if (GUI.Button (Rect (20,140,80,20),"Disconnect")) {
   myTCP.closeSocket();
   textFieldString = "Socket Disconnected...";
  }
 }
}
版權歸 UnityBuster.blogspot.com 所有。轉載請註明出處!