Unity中Socket的使用

Socket

命名空間:System.Net.Sockets

服務(wù)器端:

    static void Main(string[] args)
    {
      // socket
      Socket listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
      // bind
      IPAddress ipAdr = IPAddress.Parse("127.0.0.1");
      IPEndPoint ipEP = new IPEndPoint(ipAdr, 1234);
      listenfd.Bind(ipEP);
      // listen
      listenfd.Listen(0);
      // 
      while (true)
      {
        // accept
        Socket connfd = listenfd.Accept();
        // receive
        byte[] readBuff = new byte[1024];
        int count = connfd.Receive(readBuff);
        string str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);
        Console.WriteLine("[服務(wù)器接收:]" + str);
        // send
        byte[] bytes = System.Text.Encoding.Default.GetBytes("serv echo " + str);
        connfd.Send(bytes);
      }
    }

AddressFamily:地址簇,指明是使用IPv4還是IPv6
SocketType:套接字類(lèi)型,Stream支持可靠、雙向、基于連接的字節(jié)流,既不重復(fù)數(shù)據(jù),也不保留邊界
ProtocolType:協(xié)議
Socket.Bind(IPEndPoint):給Socket綁定IP和端口
Socket.Listen(int backlog):開(kāi)啟監(jiān)聽(tīng),等待客戶(hù)端連接,參數(shù)指定隊(duì)列中最多可容納等待接受的連接數(shù),0表示不限制
Socket.Accept:開(kāi)啟監(jiān)聽(tīng)后,服務(wù)器調(diào)用Accept接受客戶(hù)端連接
Receive:接收客戶(hù)端數(shù)據(jù),receive帶有一個(gè)byte[]類(lèi)型參數(shù),它將存儲(chǔ)接收到的數(shù)據(jù),返回值指明接收到的數(shù)據(jù)的長(zhǎng)度
Send:發(fā)送數(shù)據(jù),接受一個(gè)byte[]類(lèi)型的參數(shù),指明要發(fā)送的內(nèi)容

客戶(hù)端:

Socket socket;
public InputField hostText;
public InputField portText;
public Text ipText;
public Text contentText;
byte[] readBuff = new byte[1024];
public void Connection()
  {    
    // socket
    socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    // connect
    string host = hostText.text;
    int port = int.Parse(portText.text);
    socket.Connect(host, port);
    ipText.text = socket.LocalEndPoint.ToString();
    // send
    string str = "hello world";
    byte[] bytes = System.Text.Encoding.Default.GetBytes(str);
    socket.Send(bytes);
    // receive
    int count = socket.Receive(readBuff);
    str = System.Text.Encoding.UTF8.GetString(readBuff, 0, count);
    contentText.text = str;
    socket.Close();
  }

粘包分包處理:

處理粘包分包的一種方法是在每個(gè)數(shù)據(jù)包前面加上長(zhǎng)度字節(jié)。每次接受到數(shù)據(jù)后,先讀取長(zhǎng)度字節(jié),如果緩沖區(qū)的數(shù)據(jù)長(zhǎng)度大于要提取的字節(jié)數(shù),則取出相應(yīng)的字節(jié),負(fù)責(zé)等待下一次數(shù)據(jù)接收。

  • Connect連接類(lèi):
public class Connect
  {
    public const int BUFFER_SIZE = 1024;
    public Socket socket;
    public bool isuse = false;
    public byte[] readBuff = new byte[BUFFER_SIZE];
    public int bufferCount = 0;
    public byte[] lenBytes = new byte[sizeof(int)];
    public int msgLength = 0;
    
    public Connect()
    {
      readBuff = new byte[BUFFER_SIZE];
    }
    public void Init(Socket socket)
    {
      this.socket = socket;
      isuse = true;
      bufferCount = 0;
    }
    public int BuffRemain()
    {
      return BUFFER_SIZE - bufferCount;
    }
    public string GetAdress()
    {
      if (!isuse)
        return "無(wú)法獲取地址";
      return socket.RemoteEndPoint.ToString();
    }
    public void Close()
    {
      if (!isuse)
        return;
      Console.WriteLine("[斷開(kāi)鏈接]" + GetAdress());
      socket.Shutdown(SocketShutdown.Both);
      socket.Close();
      isuse = false;
    }
  }
  • ReceiveCb的粘包分包處理:ReceiveCb是接收數(shù)據(jù)的回調(diào),它通過(guò)socket.EndReceive獲取接收到數(shù)據(jù)的字節(jié)數(shù)count。connect.buffCount指向緩沖區(qū)的數(shù)據(jù)長(zhǎng)度,接收數(shù)據(jù)緩沖區(qū)數(shù)據(jù)增加時(shí),需要給buffCount加上count。
    ReceiveCb的實(shí)現(xiàn)如下:
public void ReceiveCb(IAsyncResult ar)
{
  Connect connect = (Connect)ar.AsyncState;
  try
  {
    int count = connect.socket.EndReceive(ar);
    if (count <= 0)
    {
      Console.WriteLine(connect.GetAdress()+"斷開(kāi)連接");
      connect.Close();
      return;
    }
    connect.bufferCount += count;
    ProcessData(connect);
        
    connect.socket.BeginReceive(connect.readBuff, connect.bufferCount, connect.BuffRemain(), SocketFlags.None, ReceiveCb, connect);
  }
  catch(Exception e)
  {
    Console.WriteLine(connect.GetAdress() + "斷開(kāi)連接");
    connect.Close();
  }
}
void ProcessData(Connect connect)
{
  if (connect.bufferCount < sizeof(int))
    return;
  // 消息長(zhǎng)度
  Array.Copy(connect.readBuff, connect.lenBytes, sizeof(int));
  connect.msgLength = BitConverter.ToInt32(connect.lenBytes, 0);
  if (connect.bufferCount < connect.msgLength + sizeof(int))
    return;
  // 處理消息
  string str = System.Text.Encoding.UTF8.GetString(connect.readBuff, sizeof(int), connect.msgLength);
  Console.WriteLine("收到消息" + str);
  int count = connect.bufferCount - sizeof(int) - connect.msgLength;
  // 消除已處理的消息
  Array.Copy(connect.readBuff, sizeof(int) + connect.msgLength, connect.readBuff, 0, count);
  connect.bufferCount = count;
  if (connect.bufferCount > 0)
  {
    ProcessData(connect);
  }
}
  • 發(fā)送消息:需要組裝消息長(zhǎng)度和消息內(nèi)容,然后一起發(fā)送
void Send(Connect connect,string str)
{
  byte[] bytes = System.Text.Encoding.UTF8.GetBytes(str);
  byte[] length = BitConverter.GetBytes(bytes.Length);
  byte[] sendbuff = length.Concat(bytes).ToArray();
  try
  {
    connect.socket.BeginSend(sendbuff, 0, sendbuff.Length, SocketFlags.None, null, null);
  }
  catch(Exception e)
  {
    Console.WriteLine("發(fā)送消息:" + e.Message);
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容