Unity高級-Socket

傳輸層協(xié)議

TCP協(xié)議UDP協(xié)議 屬于傳輸層協(xié)議

protocols.jpg

TCP(Transmission Control Protocol 傳輸控制協(xié)議)

“面向連接”就是在正式通信前必須要與對方建立起連接。比如你給別人打電話,必須等線路接通了、對方拿起話筒才能相互通話。
TCP是基于連接的協(xié)議,也就是說,在正式收發(fā)數(shù)據(jù)前,必須和對方建立可靠的連接。一個TCP連接必須要經(jīng)過三次“對話”才能建立起來,其中的過程非常復(fù)雜,我們這里只做簡單、形象的介紹,你只要做到能夠理解這個過程即可。我們來看看這三次對話的簡單過程:[主機]A向主機B發(fā)出連接請求數(shù)據(jù)包:“我想給你發(fā)數(shù)據(jù),可以嗎?”,這是第一次對話;主機B向主機A發(fā)送同意連接和要求同步(同步就是兩臺主機一個在發(fā)送,一個在接收,協(xié)調(diào)工作)的數(shù)據(jù)包:“可以,你什么時候發(fā)?”,這是第二次對話;主機A再發(fā)出一個數(shù)據(jù)包確認主機B的要求同步:“我現(xiàn)在就發(fā),你接著吧!”,這是第三次對話。三次“對話”的目的是使數(shù)據(jù)包的發(fā)送和接收同步,經(jīng)過三次“對話”之后,[主機]A才向主機B正式發(fā)送數(shù)據(jù)。

UDP(User Data Protocol 用戶數(shù)據(jù)報協(xié)議 )

UDP是與TCP相對應(yīng)的協(xié)議。它是[面向非連接]的協(xié)議,在正式通信前不必與對方先建立連接,不管對方狀態(tài)就直接發(fā)送。與手機短信非常相似:你在發(fā)短信的時候,只需要輸入對方手機號就OK了。它不與對方建立連接,而是直接就把[數(shù)據(jù)包]發(fā)送過去!
UDP適用于一次只傳送少量數(shù)據(jù)、對可靠性要求不高的應(yīng)用環(huán)境。比如,我們經(jīng)常使用“ping”命令來測試兩臺[主機]之間TCP/IP通信是否正常,其實“ping”命令的原理就是向?qū)Ψ街鳈C發(fā)送ICMP數(shù)據(jù)包,然后對方主機確認收到數(shù)據(jù)包,如果數(shù)據(jù)包是否到達的消息及時反饋回來,那么網(wǎng)絡(luò)就是通的。例如,在默認狀態(tài)下,一次“ping”操作發(fā)送4個數(shù)據(jù)包(如圖所示)。大家可以看到,發(fā)送的數(shù)據(jù)包數(shù)量是4包,收到的也是4包(因為對方主機收到后會發(fā)回一個確認收到的數(shù)據(jù)包)。這充分說明了UDP協(xié)議是[面向非連接]的協(xié)議,沒有建立連接的過程。正因為UDP協(xié)議沒有連接的過程,所以它的通信效率高;但也正因為如此,它的可靠性不如TCP協(xié)議高。QQ就使用UDP發(fā)消息,因此有時會出現(xiàn)收不到消息的情況。


Socket(套接字)

建立網(wǎng)絡(luò)通信連接至少要一對端口號(socket)。socket本質(zhì)是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網(wǎng)絡(luò)開發(fā)所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數(shù)據(jù)的具體形式;Socket是發(fā)動機,提供了網(wǎng)絡(luò)通信的能力。

應(yīng)用層通過傳輸層進行數(shù)據(jù)通信時,TCP會遇到同時為多個應(yīng)用程序進程提供并發(fā)服務(wù)的問題。多個TCP連接或多個應(yīng)用程序進程可能需要通過同一個 TCP協(xié)議端口傳輸數(shù)據(jù)。為了區(qū)別不同的應(yīng)用程序進程和連接,許多計算機操作系統(tǒng)為應(yīng)用程序與TCP/IP協(xié)議交互提供了套接字(Socket)接口。應(yīng)用層可以和傳輸層通過Socket接口,區(qū)分來自不同應(yīng)用程序進程或網(wǎng)絡(luò)連接的通信,實現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務(wù)。

套接字(socket)是通信的基石,是支持TCP/IP協(xié)議的網(wǎng)絡(luò)通信的基本操作單元。它是網(wǎng)絡(luò)通信過程中端點的抽象表示,包含進行網(wǎng)絡(luò)通信必須的五種信息:連接使用的協(xié)議,本地主機的IP地址,本地進程的協(xié)議端口,遠地主機的IP地址,遠地進程的協(xié)議端口。

建立socket連接

建立Socket連接至少需要一對套接字,其中一個運行于客戶端,稱為ClientSocket ,另一個運行于服務(wù)器端,稱為ServerSocket 。

套接字之間的連接過程分為三個步驟:服務(wù)器監(jiān)聽,客戶端請求,連接確認。

服務(wù)器監(jiān)聽:服務(wù)器端套接字并不定位具體的客戶端套接字,而是處于等待連接的狀態(tài),實時監(jiān)控網(wǎng)絡(luò)狀態(tài),等待客戶端的連接請求。

客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是服務(wù)器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務(wù)器的套接字,指出服務(wù)器端套接字的地址和端口號,然后就向服務(wù)器端套接字提出連接請求。

連接確認:當(dāng)服務(wù)器端套接字監(jiān)聽到或者說接收到客戶端套接字的連接請求時,就響應(yīng)客戶端套接字的請求,建立一個新的線程,把服務(wù)器端套接字的描述發(fā)給客戶端,一旦客戶端確認了此描述,雙方就正式建立連接。而服務(wù)器端套接字繼續(xù)處于監(jiān)聽狀態(tài),繼續(xù)接收其他客戶端套接字的連接請求。


TCP示例:

服務(wù)器

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

class Program
{
    static void Main(string[] args)
    {
        IPAddress address = IPAddress.Parse("192.168.11.3");
        int port = 8500;
        TcpListener listener = new TcpListener(address, port);

        listener.Start();
        Console.WriteLine("開始偵聽");

        while (true)
        {
            TcpClient client = listener.AcceptTcpClient();
            //接收到客戶端后,交給RemoteCient去處理
            RemoteClient remote = new RemoteClient(client);
        }
    }
}
class RemoteClient
{
    public const int BuffseSize = 8192;

    TcpClient tcpClient;
    NetworkStream stream;
    byte[] buffer;

    public RemoteClient(TcpClient client)
    {
        this.tcpClient = client;
        Console.WriteLine(client.Client.RemoteEndPoint + " -> 連接成功");

        stream = client.GetStream();
        buffer = new byte[BuffseSize];

        //開啟異步讀取消息
        stream.BeginRead(buffer, 0, BuffseSize, ReadAsync, null);
    }

    void ReadAsync(IAsyncResult result)
    {
        try
        {
            int readCount = stream.EndRead(result);
            if (readCount == 0) throw new Exception("讀取到0字節(jié)");

            string msg = Encoding.UTF8.GetString(buffer, 0, readCount);
            Console.WriteLine("接收到消息 -> " + msg);

            lock (stream) //再次開啟讀取
            {
                Array.Clear(buffer, 0, buffer.Length);
                stream.BeginRead(buffer, 0, BuffseSize, ReadAsync, null);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }

    //發(fā)送給客戶端消息
    void SendMessage(string msg)
    {
        byte[] temp = Encoding.UTF8.GetBytes(msg);
        stream.Write(temp, 0, temp.Length);
        Console.WriteLine("回復(fù)客戶端 -> " + msg);
    }
}

客戶端

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

class Program
{
    static void Main(string[] args)
    {
        SocketClient client = new SocketClient();

        //發(fā)送消息
        while (true)
        {
            string msg = Console.ReadLine();
            client.SendMessage(msg);
        }
    }
}

public class SocketClient
{
    string ip = "192.168.11.3";
    int port = 8500;

    NetworkStream stream;
    byte[] buffer;
    int BuffseSize = 8192;

    public SocketClient()
    {
        TcpClient client = new TcpClient();
        client.Connect(ip, port);

        Console.WriteLine("連接服務(wù)器 -> " + client.Client.RemoteEndPoint);
        stream = client.GetStream();

        buffer = new byte[8192];
        stream.BeginRead(buffer,0,buffer.Length, ReadAsync,null);
    }

    void ReadAsync(IAsyncResult result)
    {
        try
        {
            int readCount = stream.EndRead(result);
            if (readCount == 0) throw new Exception("讀取到0字節(jié)");

            string msg = Encoding.UTF8.GetString(buffer, 0, readCount);
            Console.WriteLine("接收到消息 -> " + msg);

            lock (stream) //再次開啟讀取
            {
                Array.Clear(buffer, 0, buffer.Length);
                stream.BeginRead(buffer, 0, BuffseSize, ReadAsync, null);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }

    //發(fā)送消息到服務(wù)器
    public void SendMessage(string msg)
    {
        byte[] temp = Encoding.UTF8.GetBytes(msg);

        stream.Write(temp, 0, temp.Length);
        Console.WriteLine("發(fā)送消息 -> " + msg);
    }
}


UDP示例

服務(wù)器

using System;
class Program
{
    static void Main(string[] args)
    {
        SocketClient client = new SocketClient();

        while (true)
        {
            string input = Console.ReadLine();
            client.Send(input);
        }
    }
}

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

class SocketClient
{
    private UdpClient udpClient;
    private const int localPort = 5500;

    private const string remoteIP = "127.0.0.1";
    private const int remotePort = 6600;
    private IPEndPoint remotePoint;

    public SocketClient()
    {
        remotePoint = new IPEndPoint(IPAddress.Parse(remoteIP), remotePort);

        udpClient = new UdpClient(localPort);
        udpClient.Connect(IPAddress.Parse(remoteIP), remotePort);
        udpClient.BeginReceive(OnReceive, null);
    }

    void OnReceive(IAsyncResult result)
    {
        try
        {
            byte[] buffer = udpClient.EndReceive(result, ref remotePoint);
            OnMessage(buffer);

            lock (udpClient)
            {
                udpClient.BeginReceive(OnReceive, null);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    void OnMessage(byte[] buffer)
    {
        string message = Encoding.Default.GetString(buffer);
        Console.WriteLine("Receive Message:{0}", message);
    }

    public void Send(string message)
    {
        byte[] sendData = Encoding.Default.GetBytes(message);
        udpClient.Send(sendData, sendData.Length);
        Console.WriteLine("Send Message:{0}", message);
    }
}

客戶端

using System;
class Program
{
    static void Main(string[] args)
    {
        SocketClient client = new SocketClient();

        while (true)
        {
            string input = Console.ReadLine();
            client.Send(input);
        }
    }
}

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class SocketClient
{
    private UdpClient udpClient;
    private const int localPort = 6600;

    private const string remoteIP = "127.0.0.1";
    private const int remotePort = 5500;
    private IPEndPoint remotePoint;

    public SocketClient()
    {
        remotePoint = new IPEndPoint(IPAddress.Parse(remoteIP), remotePort);

        udpClient = new UdpClient(localPort);
        udpClient.Connect(IPAddress.Parse(remoteIP), remotePort);
        udpClient.BeginReceive(OnReceive, null);
    }

    void OnReceive(IAsyncResult result)
    {
        try
        {
            byte[] buffer = udpClient.EndReceive(result, ref remotePoint);
            OnMessage(buffer);

            lock (udpClient)
            {
                udpClient.BeginReceive(OnReceive, null);
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

    void OnMessage(byte[] buffer)
    {
        string message = Encoding.Default.GetString(buffer);
        Console.WriteLine("Receive Message:{0}", message);
    }

    public void Send(string message)
    {
        byte[] sendData = Encoding.Default.GetBytes(message);
        udpClient.Send(sendData, sendData.Length);
        Console.WriteLine("Send Message:{0}", message);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 參考:http://www.2cto.com/net/201611/569006.html TCP HTTP UD...
    F麥子閱讀 3,069評論 0 14
  • 轉(zhuǎn)自 TCP/IP,Http,Socket,XMPP的區(qū)別網(wǎng)絡(luò)由下往上分為 物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層、傳輸層、會...
    ZMJun閱讀 1,420評論 1 10
  • 一: 網(wǎng)絡(luò)各個協(xié)議:TCP/IP、SOCKET、HTTP 網(wǎng)絡(luò)七層由下往上分別為物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層、傳輸層...
    iYeso閱讀 1,510評論 0 13
  • 網(wǎng)絡(luò)由下往上分為物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層、傳輸層、會話層、表示層和應(yīng)用層。通過初步的了解,我知道IP協(xié)議對應(yīng)于網(wǎng)...
    黑馬飲清泉閱讀 1,532評論 0 6
  • 記得周天和張森,常在,小董,沐風(fēng)他們一起玩的時候,提起過木棉酒吧,說是里面有人教手鼓,也常常聚會,還有的時候會教教...
    豆豆在成長閱讀 376評論 1 0

友情鏈接更多精彩內(nèi)容