TCP和UDP C#代码实战

news/2024/5/18 14:00:50 标签: tcp/ip, udp, c#

网络传输的七层结构:
在这里插入图片描述

其中TCP和UDP协议在传输层。

TCP/IP协议

TCP/IP中包含了四层架构中的多个协议,取其中两个进行了命名:
在这里插入图片描述

TCP

TCP的特点
在这里插入图片描述

粘包问题处理

TCP一次性接收过多数据必然出现粘包,即不同时发送的数据黏连在一起成为一个包,这是TCP的打包机制决定的。
处理粘包问题的方法是发送时在每一个数据前加上固定长度的包头,例如两个字节,表示数据的长度,在接收时按照长度把包一一解出来。解包的过程中有可能最后一个包的数据长度比包头显示的长度短或包头不完整,这是因为拆分成了两个包发送,只需将最后一个包的数据缓存,然后拼到下一个包的头部即可。

代码
客户端
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)  // 加锁保证单例唯一
      {
        if (singleInstance == null)
        {
          singleInstance = new MyTcp();
        }
        return singleInstance;
      }
    }
  }

  public void ConnectServer(string _ip, Action<bool> _result)
  {
    //设定服务器IP地址  
      ac_connect = _result;
    IPAddress ip;
    bool _isRight = IPAddress.TryParse(_ip, out ip);

    if (!_isRight)
    {
      Debug.Log("无效地址......" + _ip);
      _result(false);
      return;
    }
    clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    IPEndPoint _endpoint = new IPEndPoint(ip, 13001);
    Debug.Log("开始连接tcp~");
    clientSocket.BeginConnect(_endpoint, requestConnectCallBack, clientSocket);
  }
  private void requestConnectCallBack(IAsyncResult iar)
  {
    try
    {
      //还原原始的TcpClient对象
      Socket client = (Socket)iar.AsyncState; 
      client.EndConnect(iar);

      Debug.Log("连接服务器成功:" + client.RemoteEndPoint.ToString());
      isRun = true;
      ac_connect(true);
    
    }
    catch (Exception e)
    {
      ac_connect(false);
    

      Debug.Log("tcp连接异常:" + e.Message);
    }
    finally
    {

    }
  }
    

  public void SendMessage(byte[] _mes)
  {
    if (isRun)
    {
      try
      {
        clientSocket.Send(_mes);
      }
      catch (Exception ex)
      {
          EndClient();
        Debug.Log("发送数据异常:" + ex.Message);
      }
    }
  }

  public void EndClient()
  {

    isRun = false;

    if (clientSocket != null)
    {
      try
      {
        clientSocket.Close();
        clientSocket = null;
        Debug.Log("关闭tcp连接");
      }
      catch (Exception ex)
      {
        Debug.Log("关闭tcp连接异常111:" + ex.Message);
      }
    }
  }
}

主程序

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("连接成功"); 


        string data = "aaaabbbbcccc";

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

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

      }
      else
      {
        Debug.Log("连接失败"); 
      }
    }); 
  }
  void OnApplicationQuit()
  { 
    MyTcp.Instance.EndClient(); 
  }

}

服务器
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;//包头长度 固定2
  byte[] saveBuffer = null;//不完整的数据包,即用户自定义缓冲区


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

  private ServerTcp()
  {
    
  }

  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));  //绑定IP地址:端口  
      serverSocket.Listen(1000);    //设定最多10个排队连接请求  
      Debug.Log("启动监听" + serverSocket.LocalEndPoint.ToString() + "成功");
      isRun = true;

      //通过Clientsoket发送数据  
      Thread myThread = new Thread(ListenClientConnect);  
      myThread.Start();  	

    } catch (Exception ex) {
      Debug.Log ("服务器启动失败:" + 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 ("监听失败:" + 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服务器关闭失败:" + 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 ("关闭客户端..." + 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 ("有客户端连接:" + _socketIp);

    dic_clientSocket[_socketIp] = myClientSocket;	

    bool _flag = true;

    byte[] resultData = new byte[1048];
    while (isRun && _flag)  
    {  
      try  
      {  
          Debug.Log("_socketName是否连接:" + myClientSocket.Connected);
        
        int _size = myClientSocket.Receive(resultData);   
        if (_size <= 0) {
          throw new Exception("客户端关闭了222~");
        }		

          OnReceive(0, resultData);
      }
      catch (Exception ex)  
      {  
        Debug.Log(_socketIp + "接收客户端数据异常: " + 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 ("发数据给异常:" + ex.Message);
      }	
    }

  }

  private bool OnReceive(int connId, byte[] bytes)
  {
    
    // 系统缓冲区长度
    int bytesRead = bytes.Length;
    if (bytesRead > 0)
    {
      if (saveBuffer == null)//第一次接收
        saveBuffer = bytes;//把系统缓冲区数据放在自定义缓冲区里面
      else
        saveBuffer = saveBuffer.Concat(bytes).ToArray();//拼接上次尾包
                              
      int haveRead = 0;         //已经完成读取的数据包长度
                    
      int totalLen = saveBuffer.Length; //这里totalLen的长度有可能大于缓冲区大小的(因为 这里的saveBuffer 是系统缓冲区+不完整的数据包)
      while (haveRead <= totalLen)
      {
        //如果在N次拆解后剩余的数据包 小于 包头的长度 
        //则剩下的是非完整的数据包
        if (totalLen - haveRead < headSize)
        {
          byte[] byteSub = new byte[totalLen - haveRead];
          //把剩下不够一个完整的数据包存起来
          Buffer.BlockCopy(saveBuffer, haveRead, byteSub, 0, totalLen - haveRead);
          saveBuffer = byteSub;
          totalLen = 0;
          break;
        }
        //如果够了一个完整包,则读取包头的数据
        byte[] headByte = new byte[headSize];
        Buffer.BlockCopy(saveBuffer, haveRead, headByte, 0, headSize);//从缓冲区里读取包头的字节
        int bodySize = BitConverter.ToInt16(headByte, 0);//从包头里面分析出包体的长度

        //这里的 haveRead=等于N个数据包的长度 从0开始;0,1,2,3....N
        //如果自定义缓冲区拆解N个包后的长度 大于 总长度,说最后一段数据不够一个完整的包了,拆出来保存
        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;
          }
          //挨个分解每个包,解析成实际文字 
          String strc = Encoding.UTF8.GetString(saveBuffer, haveRead + headSize, bodySize);
          Debug.Log("得到包"  + strc); 
          //依次累加当前的数据包的长度
          haveRead = haveRead + headSize + bodySize;
          if (headSize + bodySize == bytesRead)//如果当前接收的数据包长度正好等于缓冲区长度,则待拼接的不规则数据长度归0
          {
            saveBuffer = null;//设置空 回到原始状态
            totalLen = 0;//清0
          }
        }
      }
    }
    return true;
  }
}

UDP

UDP的特点
在这里插入图片描述

代码
客户端
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 ("客户端udp已经启动~");
  //			return;
  //		}
      
    if (isRun) {
      Debug.Log ("客户端udp已经启动~");
      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连接关闭异常:" + ex.Message);
    }

  }

  public void SendMessage(byte[] _mes){
    if (isRun) {
      try {
        sendClient.Send(_mes,_mes.Length); 
      } catch (Exception ex) {
        Debug.Log ("udp发送失败:" + 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("发送量:" + buf.Length.ToString() + "," + GameData.Instance().recvNum.ToString());
      } catch (Exception ex) {
        Debug.Log ("udpClient接收数据异常:" + ex.Message);
      }
    }
    Debug.Log ("udp接收线程退出~~~~~");
  }


  void OnDestroy()
  {
    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()
  {
    CreatUpd ();
  }

  public void Creat(){

  }

  void CreatUpd(){
    _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参数:" + _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) {
      CreatUpd ();
    }

    return _udpClient;
  }
    
}

主程序

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

public class Uclient : MonoBehaviour {

  private MyUdp _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);
    }

  }

}

服务器
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()
  {
    CreatUdp ();
  }

  public void Creat(){

  }

  void CreatUdp(){
    _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端口:" + _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) {
      CreatUdp ();
    }
    return _udpClient;
  }


}

主程序:

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 ("客户端udp已经启动~");
      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连接关闭异常:" + 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("发送量:" + _mes.Length.ToString() + "," + GameData.Instance().sendNum.ToString());
      } catch (Exception ex) {
        Debug.Log ("udp发送失败:" + ex.Message);
      }

    }
  }


  public void RecvClientReady(int _userUid){ 
    if (_userUid == userUid && sendEndPort == null) {
      CreatSendEndPort(sendPortNum);
    }
  }
    // 接收线程不断 监听客户端的消息
  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("接收客户端udp信息:" + endpoint.Port);
          sendPortNum = endpoint.Port;
        }
          string str = System.Text.Encoding.UTF8.GetString(buf);

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

        //byte packMessageId = buf[PackageConstant.PackMessageIdOffset];     //消息id (1个字节)
        //Int16 packlength = BitConverter.ToInt16(buf,PackageConstant.PacklengthOffset);  //消息包长度 (2个字节)
        //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);

        //是客户端,统计接收量
  //				GameData.Instance().recvNum+=buf.Length;
        //				Debug.Log("发送量:" + buf.Length.ToString() + "," + GameData.Instance().recvNum.ToString());
      } catch (Exception ex) {
        Debug.Log (endpoint.Address+ "udpClient接收数据异常:" + ex.Message);
      }
    }
    Debug.Log ("udp接收线程退出~~~~~");
  }
}


http://www.niftyadmin.cn/n/5179936.html

相关文章

Leetcode刷题详解——矩阵中的最长递增路径

1. 题目链接&#xff1a;329. 矩阵中的最长递增路径 2. 题目描述&#xff1a; 给定一个 m x n 整数矩阵 matrix &#xff0c;找出其中 最长递增路径 的长度。 对于每个单元格&#xff0c;你可以往上&#xff0c;下&#xff0c;左&#xff0c;右四个方向移动。 你 不能 在 对角…

《008.SpringBoot之教务系统》【界面简洁功能简单】

《008.SpringBoot之教务系统》【界面简洁功能简单】 项目简介 [1]本系统涉及到的技术主要如下&#xff1a; 推荐环境配置&#xff1a;DEA jdk1.8 Maven MySQL 前后端分离; 后台&#xff1a;SpringBootMybatis; 前台&#xff1a;JSPBootStrap; [2]功能模块展示&#xff1a; 管…

flume异常关闭文件修复方法

​ flume在从kafka采集数据后&#xff0c;会将数据写入到hdfs文件中。在写入过程中&#xff0c;由于集群负载、资源或者网络原因会导致文件没有正常关闭,即文件表现为tmp格式&#xff0c;这种格式的文件从hdfs往hive分区load数据时&#xff0c;会导致数据无法查询问题。 flume写…

STM32 HAL库多路PWM没有输出踩坑记录

之前只弄过单路的&#xff0c;这次想用4路PWM&#xff0c;CUBE里面一顿配置&#xff0c;生成&#xff0c;然后套用之前的代码&#xff1a; HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); 这算是开启第一路了&#xff0c;心想后面无非就复制几条&#xff0c;改下通道的事了&…

Kubernetes介绍以及Kubernetes快速部署

Kubernetes介绍以及Kubernetes快速部署 文章目录 Kubernetes介绍以及Kubernetes快速部署1.Kubernetes介绍&#xff1a;1.1.Kubernetes简介1.2. Kubernetes应用部署方式演变1.3.Kubernetes功能1.4.Kubernetes工作原理1.5.工作流程1.6.优缺点 2.Kubernetes环境部署2.1.环境说明2.…

技术架构 - 应用数据分离,应用服务集群架构

前言 上一篇文章介绍了单机架构&#xff0c;由于性能瓶颈&#xff0c;满足不了高访问量&#xff0c;所以演化出了数据分离架构。 这种架构也很简单只是将应用服务和数据库服务分离开来&#xff0c;避免单一架构的资源争夺的情况。 一、 应用数据分离架构 1. 简介 应用服务和…

记忆科技携手中国电信,一站式存储打造坚实数字底座

11月10日&#xff0c;以“数字科技 焕新启航”为主题的2023数字科技生态大会在广州盛大开幕&#xff0c;本次大会由中国电信、广东省人民政府联合举办&#xff0c;是一场数字科技领域的年度盛会。忆联母公司记忆科技作为中国电信的合作伙伴之一受邀参会&#xff0c;深度参与了大…

在windows上利用vmware17 搭建centos7 mini版本服务器

安装centos7mini 修改名称和安装路径 也可以点击自定义硬件&#xff0c;进行硬件配置修改 设置内存 设置处理器 点击下图按钮进行设置 点击done 点击开始安装 点击设置root密码 设置成功&#xff0c;点击done &#xff0c;root密码设置的简单的话需要按两次done 等待安装完成…