TCP and UDP C# code practice

Seven-layer structure of network transmission:

Among them, TCP and UDP protocols are at the transport layer.

TCP/IP protocol

TCP/IP contains multiple protocols in the four-layer architecture, two of which are named:

TCP

Characteristics of TCP

Handling of sticky bag problems

TCP receiving too much data at one time will inevitably cause sticky packets, that is, data not sent at the same time are stuck together into one packet. This is determined by TCP’s packaging mechanism.
The way to deal with the problem of sticky packets is to add a fixed-length header before each data when sending, such as two bytes, indicating the length of the data, and decode the packets one by one according to the length when receiving. During the unpacking process, it is possible that the data length of the last packet is shorter than the length displayed in the packet header or the packet header is incomplete. This is because it is split into two packets for sending. You only need to cache the data of the last packet and then put it into the next packet. Just the head of a package.

Code
Client
using UnityEngine;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

public class MyTcp
{<!-- -->
  private static MyTcp singleInstance;
  private static readonly object padlock = new object();

  private byte[] result = new byte[1024];
  private Socket clientSocket;

  public bool isRun = false;

  private Action<bool> ac_connect;
  public static MyTcp Instance
  {<!-- -->
    get
    {<!-- -->
      lock (padlock) // Lock to ensure singleton uniqueness
      {<!-- -->
        if (singleInstance == null)
        {<!-- -->
          singleInstance = new MyTcp();
        }
        return singleInstance;
      }
    }
  }

  public void ConnectServer(string _ip, Action<bool> _result)
  {<!-- -->
    //Set server IP address
      ac_connect = _result;
    IPAddress ip;
    bool _isRight = IPAddress.TryParse(_ip, out ip);

    if (!_isRight)
    {<!-- -->
      Debug.Log("Invalid address..." + _ip);
      _result(false);
      return;
    }
    clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    IPEndPoint _endpoint = new IPEndPoint(ip, 13001);
    Debug.Log("Start connecting tcp~");
    clientSocket.BeginConnect(_endpoint, requestConnectCallBack, clientSocket);
  }
  private void requestConnectCallBack(IAsyncResult iar)
  {<!-- -->
    try
    {<!-- -->
      //Restore the original TcpClient object
      Socket client = (Socket)iar.AsyncState;
      client.EndConnect(iar);

      Debug.Log("Connect to server successfully:" + client.RemoteEndPoint.ToString());
      isRun = true;
      ac_connect(true);
    
    }
    catch (Exception e)
    {<!-- -->
      ac_connect(false);
    

      Debug.Log("tcp connection exception:" + e.Message);
    }
    finally
    {<!-- -->

    }
  }
    

  public void SendMessage(byte[] _mes)
  {<!-- -->
    if(isRun)
    {<!-- -->
      try
      {<!-- -->
        clientSocket.Send(_mes);
      }
      catch (Exception ex)
      {<!-- -->
          EndClient();
        Debug.Log("Exception in sending data:" + ex.Message);
      }
    }
  }

  public void EndClient()
  {<!-- -->

    isRun = false;

    if (clientSocket != null)
    {<!-- -->
      try
      {<!-- -->
        clientSocket.Close();
        clientSocket = null;
        Debug.Log("Close tcp connection");
      }
      catch (Exception ex)
      {<!-- -->
        Debug.Log("Closing tcp connection exception 111:" + ex.Message);
      }
    }
  }
}

main program

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class Client : MonoBehaviour {<!-- -->

  // Use this for initialization
  void Start () {<!-- -->
    string _ip = "192.168.1.18";
    MyTcp.Instance.ConnectServer(_ip, (_result) => {<!-- -->
      if (_result)
      {<!-- -->
        Debug.Log("Connection successful");


        string data = "aaaabbbbcccc";

        byte[] packheadByte = BitConverter.GetBytes((short)data.Length);
        byte[] message = System.Text.Encoding.UTF8.GetBytes(data);
        List<byte> sendMessage = new List<byte>();
        header information
        sendMessage.AddRange(packheadByte);
        sendMessage.AddRange(message);

        for (int i = 0; i < 100; i + + )
        {<!-- -->
          MyTcp.Instance.SendMessage(sendMessage.ToArray());
        }

      }
      else
      {<!-- -->
        Debug.Log("Connection failed");
      }
    });
  }
  voidOnApplicationQuit()
  {<!-- -->
    MyTcp.Instance.EndClient();
  }

}

Server
using UnityEngine;
using System.Collections.Generic;
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Linq;

public class ServerTcp {<!-- -->

  static Socket serverSocket;

  private bool isRun = false;
  private Dictionary<string,Socket> dic_clientSocket = new Dictionary<string, Socket>();


  private static readonly object stLockObj = new object ();
  private static ServerTcp instance;

  int headSize = 2;//Header length is fixed at 2
  byte[] saveBuffer = null;//Incomplete data packet, i.e. user-defined buffer


  public static ServerTcp Instance
  {<!-- -->
    get{<!-- -->
      lock (stLockObj) {<!-- -->
        if (instance == null)
        {<!-- -->
          instance = new ServerTcp();
        }
      }
      return instance;
    }
  }

  privateServerTcp()
  {<!-- -->
    
  }

  public void Destory(){<!-- -->
    instance = null;
  }

  public void StartServer(){<!-- -->

    try {<!-- -->
      IPAddress ip = IPAddress.Parse("192.168.1.18");

      serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

      serverSocket.Bind(new IPEndPoint(ip, 13001)); //Bind IP address: port
      serverSocket.Listen(1000); //Set up to 10 queued connection requests
      Debug.Log("Start listening" + serverSocket.LocalEndPoint.ToString() + "Success");
      isRun = true;

      //Send data through Clientsoket
      Thread myThread = new Thread(ListenClientConnect);
      myThread.Start();

    } catch (Exception ex) {<!-- -->
      Debug.Log ("Server startup failed:" + ex.Message);
    }
  }



  private void ListenClientConnect()
  {<!-- -->
    while(isRun)
    {<!-- -->
      try {<!-- -->
        Socket clientSocket = serverSocket.Accept();


        Thread receiveThread = new Thread(ReceiveMessage);
        receiveThread.Start(clientSocket);
      } catch (Exception ex) {<!-- -->
        Debug.Log ("Listening failed:" + ex.Message);
      }
    }
  }

    // clientSocket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);

    public void EndServer(){<!-- -->

    if (!isRun) {<!-- -->
      return;
    }

    isRun = false;
    try {<!-- -->
      foreach (var item in dic_clientSocket) {<!-- -->
        item.Value.Close ();
      }

      dic_clientSocket.Clear();

      if (serverSocket != null) {<!-- -->
        serverSocket.Close();
        serverSocket = null;
      }
    } catch (Exception ex) {<!-- -->
      Debug.Log("tcp server shutdown failed:" + ex.Message);
    }

  }

  public void CloseClientTcp(string _socketIp){<!-- -->
    try {<!-- -->
      if (dic_clientSocket.ContainsKey(_socketIp)) {<!-- -->
        if (dic_clientSocket [_socketIp] != null) {<!-- -->
          dic_clientSocket [_socketIp].Close();
        }
        dic_clientSocket.Remove (_socketIp);
      }
    } catch (Exception ex) {<!-- -->
      Debug.Log ("Close the client..." + ex.Message);
    }

  }

  public int GetClientCount(){<!-- -->
    return dic_clientSocket.Count;
  }

  public List<string> GetAllClientIp(){<!-- -->
    return new List<string> (dic_clientSocket.Keys);
  }

      
  private void ReceiveMessage(object clientSocket)
  {<!-- -->
    Socket myClientSocket = (Socket)clientSocket;
      // Debug.Log(myClientSocket.RemoteEndPoint.ToString());
    string _socketIp = myClientSocket.RemoteEndPoint.ToString().Split(':')[0];

    Debug.Log ("Client connection:" + _socketIp);

    dic_clientSocket[_socketIp] = myClientSocket;

    bool _flag = true;

    byte[] resultData = new byte[1048];
    while (isRun & amp; & amp; _flag)
    {<!-- -->
      try
      {<!-- -->
          Debug.Log("Is _socketName connected:" + myClientSocket.Connected);
        
        int _size = myClientSocket.Receive(resultData);
        if (_size <= 0) {<!-- -->
          throw new Exception("Client closed 222~");
        }

          OnReceive(0, resultData);
      }
      catch (Exception ex)
      {<!-- -->
        Debug.Log(_socketIp + "Exception in receiving client data: " + ex.Message);

        _flag = false;
        break;
      }
    }
      
    CloseClientTcp (_socketIp);
  }


    
  public void SendMessage(string _socketName,byte[] _mes){<!-- -->
        Debug.Log("SendMessage aaa ----- _socketName " + _socketName);
        if (isRun) {<!-- -->
      try {<!-- -->
        dic_clientSocket [_socketName].Send (_mes);
      } catch (Exception ex) {<!-- -->
        Debug.Log ("Send data to exception:" + ex.Message);
      }
    }

  }

  private bool OnReceive(int connId, byte[] bytes)
  {<!-- -->
    
    // System buffer length
    int bytesRead = bytes.Length;
    if (bytesRead > 0)
    {<!-- -->
      if (saveBuffer == null)//First reception
        saveBuffer = bytes;//Put the system buffer data in the custom buffer
      else
        saveBuffer = saveBuffer.Concat(bytes).ToArray();//Splicing the last last packet
                              
      int haveRead = 0; //The length of the data packet that has been read
                    
      int totalLen = saveBuffer.Length; //The length of totalLen here may be greater than the buffer size (because the saveBuffer here is the system buffer + incomplete data packet)
      while (haveRead <= totalLen)
      {<!-- -->
        //If the remaining data packets after N disassembly are less than the length of the packet header
        //The rest is an incomplete data packet
        if (totalLen - haveRead < headSize)
        {<!-- -->
          byte[] byteSub = new byte[totalLen - haveRead];
          //Save the remaining data packets that are less than one complete
          Buffer.BlockCopy(saveBuffer, haveRead, byteSub, 0, totalLen - haveRead);
          saveBuffer = byteSub;
          totalLen = 0;
          break;
        }
        //If there is a complete package, read the data in the header
        byte[] headByte = new byte[headSize];
        Buffer.BlockCopy(saveBuffer, haveRead, headByte, 0, headSize);//Read the header bytes from the buffer
        int bodySize = BitConverter.ToInt16(headByte, 0);//Analyze the length of the package body from the header

        //Here haveRead=equal to the length of N data packets starting from 0; 0,1,2,3....N
        //If the length of the custom buffer after disassembling N packets is greater than the total length, it means that the last piece of data is not enough for a complete packet, so tear it out and save it.
        if (haveRead + headSize + bodySize > totalLen)
        {<!-- -->
          byte[] byteSub = new byte[totalLen - haveRead];
          Buffer.BlockCopy(saveBuffer, haveRead, byteSub, 0, totalLen - haveRead);
          saveBuffer = byteSub;
          break;
        }
        else
        {<!-- -->
          if (bodySize == 0)
          {<!-- -->
              saveBuffer = null;
              break;
          }
          //Decompose each package one by one and parse it into actual text
          String strc = Encoding.UTF8.GetString(saveBuffer, haveRead + headSize, bodySize);
          Debug.Log("Get package" + strc);
          //Accumulate the length of the current data packet in sequence
          haveRead = haveRead + headSize + bodySize;
          if (headSize + bodySize == bytesRead)//If the length of the currently received data packet is exactly equal to the buffer length, the length of the irregular data to be spliced is returned to 0
          {<!-- -->
            saveBuffer = null;//Set empty and return to original state
            totalLen = 0;//clear 0
          }
        }
      }
    }
    return true;
  }
}

UDP

Characteristics of UDP

Code
Client
using UnityEngine;
using System;
using System.Text;
using System.Threading;

using System.Net;
using System.Net.Sockets;
 
public class MyUdp {<!-- -->

  private UdpClient sendClient = null;
  // private IPEndPoint sendEndPort;
  private bool isRun;

  private bool isRecv;
  public void StartClientUdp(string _ip){<!-- -->

  // if (sendEndPort != null) {<!-- -->
  // Debug.Log ("Client udp has been started~");
  // return;
  // }
      
    if (isRun) {<!-- -->
      Debug.Log ("Client udp has been started~");
      return;
    }
    isRun = true;

    sendClient = UdpManager.Instance.GetClient();
  // sendEndPort = new IPEndPoint(IPAddress.Parse(_ip), NetConfig.UdpSendPort);

  // StartRecvMessage ();
  }
    
  private void StartRecvMessage(){<!-- -->
    Thread t = new Thread(new ThreadStart(RecvThread));
    t.Start();
  }

  public void StopRecvMessage(){<!-- -->
    isRecv = false;
  }

  public void EndClientUdp(){<!-- -->
    try {<!-- -->
      isRun = false;
      isRecv = false;
  // if (sendEndPort != null) {<!-- -->
  // UdpManager.Instance.CloseUdpClient();
  // sendClient = null;
  // sendEndPort = null;
  // }
      UdpManager.Instance.CloseUdpClient();
      sendClient = null;
    } catch (Exception ex) {<!-- -->
      Debug.Log ("udp connection closing exception:" + ex.Message);
    }

  }

  public void SendMessage(byte[] _mes){<!-- -->
    if (isRun) {<!-- -->
      try {<!-- -->
        sendClient.Send(_mes,_mes.Length);
      } catch (Exception ex) {<!-- -->
        Debug.Log ("udp sending failed:" + ex.Message);
      }
    } }


  private void RecvThread()
  {<!-- -->
    isRecv = true;
    IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("192.169.1.18"), UdpManager.Instance.localPort);
    while(isRecv)
    {<!-- -->
      try {<!-- -->
        byte[] buf = sendClient.Receive(ref endpoint);

      
  // Debug.Log("Amount sent:" + buf.Length.ToString() + "," + GameData.Instance().recvNum.ToString());
      } catch (Exception ex) {<!-- -->
        Debug.Log ("udpClient receiving data exception:" + ex.Message);
      }
    }
    Debug.Log ("udp receiving thread exited~~~~~");
  }


  voidOnDestroy()
  {<!-- -->
    EndClientUdp();
  }
}
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;

public class UdpManager {<!-- -->
  private static UdpManager singleInstance;
  private static readonly object padlock = new object();

  public UdpClient _udpClient = null;

  public int localPort;
  public static UdpManager Instance
  {<!-- -->
    get
    {<!-- -->
      lock (padlock)
      {<!-- -->
        if (singleInstance==null)
        {<!-- -->
          singleInstance = new UdpManager();
        }
        return singleInstance;
      }
    }
  }

  private UdpManager()
  {<!-- -->
    CreateUpd();
  }

  public void Create(){<!-- -->

  }

  void CreateUpd(){<!-- -->
    _udpClient = new UdpClient ();
    Debug.Log("CreatUpd " + localPort);
    IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("192.168.1.18"), 10011);
    _udpClient.Connect(endpoint);
    IPEndPoint _localEnd = (IPEndPoint)_udpClient.Client.LocalEndPoint;
    localPort = _localEnd.Port;
    Debug.Log ("udp parameters:" + _localEnd.Address + "," + _localEnd.Port);
  }

  public void Destory(){<!-- -->

    CloseUdpClient();
    singleInstance = null;
  }

  public void CloseUdpClient(){<!-- -->
    if (_udpClient != null) {<!-- -->
      _udpClient.Close();
      _udpClient = null;
    }
  }

  public UdpClient GetClient(){<!-- -->
    if (_udpClient == null) {<!-- -->
      CreateUpd();
    }

    return _udpClient;
  }
    
}

main program

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Uclient : MonoBehaviour {<!-- -->

  privateMyUdp_upd;
  // Use this for initialization
  void Start () {<!-- -->
    StartClientUdp();
  }

  public void StartClientUdp()
  {<!-- -->
      
    _upd = new MyUdp();
    _upd.StartClientUdp("192.168.1.18");
    string data = "aaaabbbbcccc";
      
    byte[] message = System.Text.Encoding.UTF8.GetBytes(data);
        for (int i =0; i<20;i + + )
        {<!-- -->
      _upd.SendMessage(message);
    }

  }

}

Server
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System;
public class UdpManager {<!-- -->
  private static UdpManager singleInstance;
  private static readonly object padlock = new object();

  public UdpClient _udpClient = null;
  public int recvPort;
  public static UdpManager Instance
  {<!-- -->
    get
    {<!-- -->
      lock (padlock)
      {<!-- -->
        if (singleInstance==null)
        {<!-- -->
          singleInstance = new UdpManager();
        }
        return singleInstance;
      }
    }
  }

  private UdpManager()
  {<!-- -->
    CreateUdp();
  }

  public void Create(){<!-- -->

  }

  void CreateUdp(){<!-- -->
    _udpClient = new UdpClient (10011);
    // uint IOC_IN = 0x80000000;
    // uint IOC_VENDOR = 0x18000000;
    // uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
    //byte[] optionOutValue = new byte[4];
    //byte[] optionInValue = { Convert.ToByte(false) };
    //_udpClient.Client.IOControl((int)SIO_UDP_CONNRESET, optionInValue, optionOutValue);


    IPEndPoint _localip = (IPEndPoint)_udpClient.Client.LocalEndPoint;
    Debug.Log ("udp port:" + _localip.Port);
    recvPort = _localip.Port;
  }

  public void Destory(){<!-- -->

    CloseUdpClient();
    singleInstance = null;
  }

  public void CloseUdpClient(){<!-- -->
    if (_udpClient != null) {<!-- -->
      Debug.Log("CloseUdpClient **************** ");
      _udpClient.Close();
      _udpClient = null;
    }
  }

  public UdpClient GetClient(){<!-- -->
    if (_udpClient == null) {<!-- -->
      CreateUdp();
    }
    return _udpClient;
  }


}

Main program:

using UnityEngine;
using System;
using System.Text;
using System.Threading;

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

public class ClientUdp {<!-- -->
  public int userUid;
  private int sendPortNum;
  private UdpClient sendClient = null;
  private IPEndPoint sendEndPort;
  private bool isRun;
  private string serverIp;

  public void StartClientUdp(string _ip,int _uid){<!-- -->

    if (sendEndPort != null) {<!-- -->
      Debug.Log ("Client udp has been started~");
      return;
    }

    userUid = _uid;
    serverIp = _ip;
    isRun = true;

    sendClient = UdpManager.Instance.GetClient();
  // sendClient = new UdpClient(NormalData.recvPort);
  // sendEndPort = new IPEndPoint(IPAddress.Parse(_ip), ServerConfig.udpRecvPort);

    Thread t = new Thread(new ThreadStart(RecvThread));
    t.Start();


  }

  public void EndClientUdp(){<!-- -->
    try {<!-- -->
      isRun = false;
      
        UdpManager.Instance.CloseUdpClient();
        sendClient = null;
        sendEndPort = null;
        
    } catch (Exception ex) {<!-- -->
      Debug.Log ("udp connection closing exception:" + ex.Message);
    }

  }

  private void CreatSendEndPort(int _port){<!-- -->
    sendEndPort = new IPEndPoint(IPAddress.Parse(serverIp), _port);
  }

  public void SendMessage(byte[] _mes){<!-- -->
    if (isRun) {<!-- -->
      try {<!-- -->
        sendClient.Send (_mes,_mes.Length,sendEndPort);
  // GameData.Instance().sendNum + =_mes.Length;
        // Debug.Log("Send amount:" + _mes.Length.ToString() + "," + GameData.Instance().sendNum.ToString());
      } catch (Exception ex) {<!-- -->
        Debug.Log ("udp sending failed:" + ex.Message);
      }

    }
  }


  public void RecvClientReady(int _userUid){<!-- -->
    if (_userUid == userUid & amp; & amp; sendEndPort == null) {<!-- -->
      CreateSendEndPort(sendPortNum);
    }
  }
    // The receiving thread continuously monitors the client's messages
  private void RecvThread()
  {<!-- -->

    IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse(serverIp), UdpManager.Instance.recvPort);
    while(isRun)
    {<!-- -->
      try {<!-- -->
        byte[] buf = sendClient.Receive(ref endpoint);

        if (sendEndPort == null) {<!-- -->
          //Debug.Log("Receive client udp information:" + endpoint.Port);
          sendPortNum = endpoint.Port;
        }
          string str = System.Text.Encoding.UTF8.GetString(buf);

        Debug.Log("str *** " + str);

        //byte packMessageId = buf[PackageConstant.PackMessageIdOffset]; //Message id (1 byte)
        //Int16 packlength = BitConverter.ToInt16(buf,PackageConstant.PacklengthOffset); //Message packet length (2 bytes)
        //int bodyDataLenth = packlength - PackageConstant.PacketHeadLength;
        //byte[] bodyData = new byte[bodyDataLenth];
        //Array.Copy(buf, PackageConstant.PacketHeadLength, bodyData, 0, bodyDataLenth);

        //delegate_analyze_message((PBCommon.CSID)packMessageId,bodyData);

        //It is the client, counting the reception volume
  // GameData.Instance().recvNum + =buf.Length;
        // Debug.Log("Amount sent:" + buf.Length.ToString() + "," + GameData.Instance().recvNum.ToString());
      } catch (Exception ex) {<!-- -->
        Debug.Log (endpoint.Address + "udpClient receiving data exception:" + ex.Message);
      }
    }
    Debug.Log ("udp receiving thread exited~~~~~");
  }
}