我們知道兩個進程如果需要進行通訊最基本的一個前提是有唯一的標示一個進程,在本地進程通訊中我們可以使用PID來唯一標示一個進程,但PID只在本地唯一,網絡中的兩個進程PID衝突機率很大,這時候我們需要另闢它徑了,我們知道IP層的ip地址可以唯一標示主機,而TCP層協議和端口號可以唯一標示主機的一個進程,這樣我們可以利用ip地址+協議+端口號唯一標示網絡中的一個進程。
能夠唯一標示網絡中的進程後,它們就可以利用socket進行通信了,什麼是socket呢?我們經常把socket翻譯爲套接字,socket是在應用層和傳輸層之間的一個抽象層,它把TCP/IP層複雜的操作抽象爲幾個簡單的接口供應用層調用已實現進程在網絡中通信。
三次握手:
第一次握手:客戶端嘗試連接服務器,向服務器發送syn包,syn=j,客戶端進入SYN_SEND狀態等待服務器確認
第二次握手:服務器接收客戶端syn包並確認(ack=j+1),同時向客戶端發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態
第三次握手:第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手
服務器socket與客戶端socket建立連接的部分其實就是大名鼎鼎的三次握手
Socket
using UnityEngine;
using System.Collections;
using System.Net;//網絡
using System.Net.Sockets;//套接字
using System.Text;//文本
//我的Socket委託
public delegate void AlbertReceiveCallBack(string content);
public class AlbertSocketManager
{
#region 服務器端
//聲明一個服務器端的套接字
Socket serverSocket;
//聲明一個服務器端的委託
AlbertReceiveCallBack serverCallBack;
//聲明一個byte緩存
byte[] serverBuffer = new byte[1024];
public void InitServer(AlbertReceiveCallBack rcb)
{
// 傳入委託對象
serverCallBack = rcb;
//初始化服務器端的套接字(1)
serverSocket = new Socket(AddressFamily.InterNetwork,/*IPV4協議*/
SocketType.Stream, /*雙向讀寫流**/
ProtocolType.Tcp/*TCP協議*/);
//實例一個網絡端點,傳入地址和端口
IPEndPoint serverEP = new IPEndPoint(IPAddress.Any, 23456);
//綁定網絡端點(2)
serverSocket.Bind(serverEP);
//設置監聽的最大數量(3)
serverSocket.Listen(10);
//異步接受客戶端的連接(CallBack)(4)
serverSocket.BeginAccept(new System.AsyncCallback(ServerAccept), serverSocket);
//發送一個消息表示服務器已經創建
serverCallBack("Server Has Init");
}
private void ServerAccept(System.IAsyncResult ar)
{
//接收結果狀態(5接收數據)
serverSocket = ar.AsyncState as Socket;
//接收結果
Socket workingSocket = serverSocket.EndAccept(ar);
//開始異步接收消息
workingSocket.BeginReceive(serverBuffer/*消息緩存*/,
0/*接收消息的偏移量*/,
serverBuffer.Length/*設置接收字節數*/,
SocketFlags.None/*Socket標誌位*/,
new System.AsyncCallback(ServerRecive)/*接收回調*/,
workingSocket/*最後的狀態*/);
//繼續接收客戶端的請求
workingSocket.BeginAccept(new System.AsyncCallback(ServerAccept), workingSocket);
}
private void ServerRecive(System.IAsyncResult ar)
{
//獲取正在工作的Socket對象(用來接收數據)
Socket workingSocket = ar.AsyncState as Socket;
//接收到的數據字節數
int byteCount = 0;
//接收到的數據字符串
string content = "";
try
{
//嘗試結束異步接收的消息
byteCount = workingSocket.EndReceive(ar);
}
catch (SocketException ex)
{
//如果接收失敗,返回詳細異常
serverCallBack(ex.ToString());
}
if (byteCount > 0)
{
//轉換byte數組爲字符串(支持中文)
content = UTF8Encoding.UTF8.GetString(serverBuffer);//字符編碼
}
//發送接收到的消息
serverCallBack(content);
//繼續接收消息
workingSocket.BeginReceive(serverBuffer/*消息緩存*/,
0/*接收消息的偏移量*/,
serverBuffer.Length/*設置接收字節數*/,
SocketFlags.None/*Socket標誌位*/,
new System.AsyncCallback(ServerRecive)/*接收回調*/,
workingSocket/*最後的狀態*/);
}
#endregion
</pre><pre name="code" class="csharp"> #region 客戶端
//聲明客戶端的套接字
Socket clientSocket;
//聲明客戶端的委託對象
AlbertReceiveCallBack clientReceiveCallBack;
//聲明客戶端的緩存1KB
byte[] clientBuffer = new byte[1024];
/// <summary>
/// 實例化客戶端方法
/// </summary>
/// <param name="ip">IP地址</param>
/// <param name="port">端口</param>
/// <param name="rcb">委託對象</param>
public void InitClient(string ip, int port, AlbertReceiveCallBack rcb)
{
//接收客戶端的委託對象
clientReceiveCallBack = rcb;
//實例客戶端的Socket
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//實例化一個客戶端的網絡端點(把IP和端口傳進來)
IPEndPoint clientEP = new IPEndPoint(IPAddress.Parse(ip), port);
//連接服務器
clientSocket.Connect(clientEP);
clientSocket.BeginReceive(clientBuffer,
0,
clientBuffer.Length,
SocketFlags.None,
new System.AsyncCallback(ClientReceive),
clientSocket);
}
private void ClientReceive(System.IAsyncResult ar)
{
//獲取一個客戶端正在接收數據的Socket對象
Socket workingSocket = ar.AsyncState as Socket;
int byteCount = 0;
string content = "";
try
{
//結束接收數據,完成存儲
byteCount = workingSocket.EndReceive(ar);
}
catch (SocketException ex)
{
//如果接收消息異常,發送異常消息
clientReceiveCallBack(ex.ToString());
}
if (byteCount > 0)
{
//轉換已經接收到的Byte數據爲字符串
content = UTF8Encoding.UTF8.GetString(clientBuffer);
}
//發送數據
clientReceiveCallBack(content);
//接收下一波數據
workingSocket.BeginReceive(clientBuffer,
0,
clientBuffer.Length,
SocketFlags.None,
new System.AsyncCallback(ClientReceive),
clientSocket);
}
/// <summary>
/// 客戶端發送數據方法
/// </summary>
/// <param name="msg">消息內容</param>
public void ClientSendMessage(string msg)
{
if (msg != "")
{
//將要發送的字符串消息轉換成byte數組
clientBuffer = UTF8Encoding.UTF8.GetBytes(msg);
}
clientSocket.BeginSend(clientBuffer,
0,
clientBuffer.Length,
SocketFlags.None,
new System.AsyncCallback(SendMsg),
clientSocket);
}
//客戶端發送消息的回調函數
private void SendMsg(System.IAsyncResult ar)
{
Socket workingSocket = ar.AsyncState as Socket;
workingSocket.EndSend(ar);
}
#endregion
}
服務器端:
using UnityEngine;
using System.Collections;
public class ServerSocketDemo : MonoBehaviour {
private AlbertSocketManager albertSocketManager;
string serverContent;
void Awake()
{
albertSocketManager = new AlbertSocketManager();
//執行初始化服務器方法,傳入委託函數
albertSocketManager.InitServer(ShowMsg);
}
/// <summary>
/// Shows the message委託函數
/// </summary>
/// <param name="msg">Message</param>
void ShowMsg(string msg)
{
serverContent = msg;
}
void OnGUI()
{
GUILayout.Label(serverContent);
}
}
客戶端:
using UnityEngine;
using System.Collections;
public class ClientSocketDemo : MonoBehaviour
{
private AlbertSocketManager albertSocketManager;
private string clientContent="";
private string needSendText="";
void Awake()
{
albertSocketManager = new AlbertSocketManager();
albertSocketManager.InitClient("127.0.0.1", 23456, (string msg) =>
{
clientContent = msg;
});
}
void OnGUI()
{
needSendText = GUILayout.TextField(needSendText);
if (GUILayout.Button("點擊發送消息"))
{
if (needSendText != "")
{
albertSocketManager.ClientSendMessage(needSendText);
}
}
}
}