TCP和UDP C#代码实战

news2025/1/16 16:11:22

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

其中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.coloradmin.cn/o/1191956.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OV5640的参数与配置方法

分辨率和速率&#xff08;FPS&#xff09; 寄存器配置 I/O 板的驱动能力和方向控制 system clock control OV5640 PLL 允许输入时钟频率范围为 6~27 MHz&#xff0c;最大 VCO 频率为 800 MHz。 MipiClk 用于 MIPI&#xff0c;SysClk 用于图像信号处理 (ISP) 模块的内部时钟。 …

网络营销利器:海外IP代理如何助力你的网络营销?如何选择?

在当今数字化的时代&#xff0c;网络营销已经成为企业营销策略的重要组成部分。而对于进去海外市场的跨境玩家来说&#xff0c;海外的推广营销是重中之重。然而&#xff0c;在开展网络营销的过程中&#xff0c;我们常常会遇到各种挑战&#xff0c;如地域限制、访问速度慢等。 …

力扣每日一道系列 --- LeetCode 88. 合并两个有序数组

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构探索 ✅LeetCode每日一道 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 文章目录 思路1&#xff1a;暴力求解思路2&#xff1a;原地合并 LeetCode 88. 合并两个有序数组…

Docker - 镜像

Docker - 镜像 镜像是什么 镜像是一种轻量级&#xff0c;可执行的独立软件包&#xff0c;用来打包软件运行环境和基于运行环境开发的软件&#xff0c;它包含运行某个软件所需的所有内容&#xff0c;包括代码&#xff0c;运行时&#xff0c;库&#xff0c;环境变量和配置文件。…

数据结构与算法(一)数据结构基础

目录 一、绪论1.1 什么是程序 二、算法2.1 定义2.2 特性2.3 算法时间效率2.4 时间复杂度1&#xff09;大 O 阶推导法&#xff1a;2&#xff09;举个例子&#xff1a;3&#xff09;常见的时间复杂度 2.5 空间复杂度1&#xff09;计算方法2&#xff09;存储空间 2.6 常见算法的时…

Yolov5 + 界面PyQt5 +.exe文件部署运行

介绍 Yolov5是一种基于深度学习的目标检测算法&#xff0c;PyQt5是一个Python编写的GUI框架&#xff0c;用于创建交互式界面。在部署和运行Yolov5模型时&#xff0c;结合PyQt5可以方便地创建一个用户友好的界面&#xff0c;并将代码打包为.exe文件以供其他人使用。 下面是一个…

虚拟化服务器+华为防火墙+kiwi_syslog访问留痕

一、适用场景 1、大中型企业需要对接入用户的访问进行记录时&#xff0c;以前用3CDaemon时&#xff0c;只能用于小型网络当中&#xff0c;记录的数据量太大时&#xff0c;本例采用破解版的kiwi_syslog。 2、当网监、公安查到有非法访问时&#xff0c;可提供基于五元组的外网访…

在全志XR806上移植st7789屏幕驱动

前言 很高兴有机会参加本次极术社区举办的“「免费试用」搭载安谋科技STAR-MC1的全志XR806 Wi-FiBLE 开发板试用活动”。 去年就对全志的mcu芯片感兴趣了&#xff0c;一直没有机会接触&#xff0c;看到本次极术社区提供的全志wifi BLE开发板试用&#xff0c;就马上参加了。板…

WebGL智慧城市软件项目

WebGL开发智慧城市项目时&#xff0c;需要考虑多个方面&#xff0c;包括技术、隐私、安全和可持续性。以下是一些需要注意的关键问题&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.隐私和数据安全…

6.3二叉树的层序遍历(LC102,LC107-M)

二叉树的层序遍历&#xff08;LC102&#xff09;&#xff1a; 算法&#xff08;长度法&#xff09;&#xff1a; 需要借用一个辅助数据结构即队列来实现&#xff0c;队列先进先出&#xff0c;符合一层一层遍历的逻辑&#xff0c;而用栈先进后出适合模拟深度优先遍历也就是递归…

【外部服务对接】对接Firebase支持谷歌、Facebook、苹果等第三方平台用户注册登录

【外部服务对接】对接Firebase支持谷歌、Facebook、苹果等第三方平台登录 背景 因主要做国外尼日的市场&#xff0c;相关的应用的全是国外用户使用&#xff0c;为了方便用户的注册和登录&#xff0c;接入国外的统一平台Firebase,集成使用很方便。 主要步骤 1.注册登录Fireb…

如何提高小红书笔记的互动率

相信有很多新手在运营小红书的时候&#xff0c;可能都会遇到过以下这样的情况&#xff1a; 笔记点赞、收藏数据明明还可以&#xff0c;但评论区却没有人留言&#xff1f;为何大家只给点赞、收藏&#xff0c;却不关注账号&#xff1f; 其实&#xff0c;这背后有很多运营技巧&a…

​做好研发管理的三个条件​

1.制造鼓励创新的环境 要做好研发管理&#xff0c;首先要制造一个鼓励创新、适合研发的环境&#xff0c;必须采取弹性而目标化的管理&#xff0c;不以死板的制度限制员工的创意&#xff0c;必须要求实质的成果。 2.融入行销观念 将行销的观念融入研发中&#xff1a;为使有限的…

xlua游戏热更新(lua访问C#)

CS.UnityEngine静态方法访问unity虚拟机 创建游戏物体 CS.UnityEngine.GameObject(new by lua);静态属性 CS.UnityEngine.GameObject(new by lua); -- 创建 local camera CS.UnityEngine.GameObject.Find(Main Camera); --查找 camera.name Renamed by Lua;访问组件 loca…

通配符匹配

题目链接 通配符匹配 题目描述 注意点 s 仅由小写英文字母组成p 仅由小写英文字母、‘?’ 或 ‘*’ 组成‘?’ 可以匹配任何单个字符‘*’ 可以匹配任意字符序列&#xff08;包括空字符序列&#xff09; 解答思路 最初想到的是dfs 剪枝&#xff0c;但是用例超时了参照题…

小型企业如何数字化转型?ZohoCRM助力小企业转型

小型企业数字化之路倍加艰难&#xff0c;其组织规模有限、资源有限&#xff0c;数字化布局或转型&#xff0c;也存在与数字平台匹配度的问题。其实小型企业可以通过CRM客户管理系统实现高效的客户关系管理&#xff0c;进一步提高市场竞争力。 建立高效易用的客户关系管理系统 …

OpenAI重磅推出GPTs,无需编码人人可以自定ChatGPT!

原创 | 文 BFT机器人 在11月7日深夜2点&#xff08;北京时间&#xff09;&#xff0c;美国旧金山举办了首届开发者大会&#xff0c;该活动由AI领域的知名公司OpenAI主办。尽管这是该公司的首届大会&#xff0c;但其盛大的规模和影响力已将其誉为“AI春晚”。在会议上&#xff…

关于echarts封装组件以及多次更新数据信息加载问题

项目中经常使用到echarts插件&#xff0c;使用时会遇到封装组件的问题&#xff0c;一个组件到底怎么封装才是完善的&#xff1f;仁者见仁智者见智思路不同封装的方式就是不同的。废话不多直接上封装的代码&#xff1a; <template><div :id"id" :style"…

OpenGL_Learn08(坐标系统与3D空间)

目录 1. 概述 2. 局部空间 3. 世界空间 4. 观察空间 5. 剪裁空间 6. 初入3D 7. 3D旋转 8. 多个正方体 9. 观察视角 1. 概述 OpenGL希望在每次顶点着色器运行后&#xff0c;我们可见的所有顶点都为标准化设备坐标(Normalized Device Coordinate, NDC)。也就是说&#x…

两台Linux服务器之间传送文件

两台Linux服务器之间传送文件 将U盘上的数据传送到服务器上 本地U盘 远程服务器地址&#xff1a; 192.168.30.125 传送到data文件夹的下面 scp -r coco2017 s192.168.30.125:/data传送 两台Linux服务器之间传送文件的4种方法