C# Socket非同步實現消息發送--附帶源碼

来源:https://www.cnblogs.com/psjinfo/archive/2019/03/06/10485224.html
-Advertisement-
Play Games

前言 看了一百遍,不如動手寫一遍。 Socket這塊使用不是特別熟悉,之前實現是公司有對應源碼改改能用。 但是不理解實現的過程和步驟,然後最近有時間自己寫個demo實現看看,熟悉熟悉Socket。 網上也有好的文章,結合別人的理接和自己實踐總算寫完了。。。 參考: https://www.cnblo ...


前言

看了一百遍,不如動手寫一遍。

Socket這塊使用不是特別熟悉,之前實現是公司有對應源碼改改能用。

但是不理解實現的過程和步驟,然後最近有時間自己寫個demo實現看看,熟悉熟悉Socket。

網上也有好的文章,結合別人的理接和自己實踐總算寫完了。。。

參考: https://www.cnblogs.com/sunev/ 實現

參考:https://blog.csdn.net/woshiyuanlei/article/details/47684221

          https://www.cnblogs.com/dotnet261010/p/6211900.html

理解握手過程,關閉時的握手過程(關閉的過程弄了半天,總算弄懂了意思)。

實現過程

總體包含:開啟,關閉,斷線重連(客戶端),內容發送。

說明:服務端和客戶端代碼基本一直,客戶端需要實時監聽服務端狀態斷開時需要重連。

頁面效果圖:

服務端實現(實現不包含UI部分 最後面放所有代碼和下載地址)

給出一個指定的地址IP+Port,套接字類型初始化Socket

使用Bind方法進行關聯,Listen(1) 註釋: 掛起連接隊列的最大長度。我的通俗理接:你有一個女朋友這是你不能找別的,但是如果分手了就可以找別的,

BeginAccept包含一個非同步的回調,獲取請求的Socket

var s_socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
s_socket.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
s_socket.Listen(10);//同時連接的最大連接數
s_socket.BeginAccept(new AsyncCallback(Accept), s_socket);//獲取連接
View Code

ClientState,自己聲明的一個類用於接收數據(對應ReadCallback)和內部處理

Accept非同步請求回調,當有Socket請求時會進去該回調,

EndAccept,我的理接為給當前Socket創建一個新的鏈接釋放掉 Listen

BeginReceive,包含一個消息回調(ReadCallback),只需給出指定長度數組接收Socket上傳的消息數據

BeginAccept,再次執行等待方法等待後續Socket連接

        /// <summary>
        /// 非同步連接回調 獲取請求Socket 添加信息到控制項
        /// </summary>
        /// <param name="ar"></param>
        private void Accept(IAsyncResult ar)
        {
            try
            {
                //獲取連接Socket 創建新的連接
                Socket myServer = ar.AsyncState as Socket;
                Socket service = myServer.EndAccept(ar);

                ClientState obj = new ClientState();
                obj.clientSocket = service;
                //接收連接Socket數據
                service.BeginReceive(obj.buffer, 0, ClientState.bufsize, SocketFlags.None, new AsyncCallback(ReadCallback), obj);
                myServer.BeginAccept(new AsyncCallback(Accept), myServer);//等待下一個連接
            }
            catch (Exception ex)
            {
                Console.WriteLine("服務端關閉"+ex.Message+" "+ex.StackTrace);
            }
        }
View Code

EndReceive,通俗理接買東西的時候老闆已經打包好了,付錢走人。

BeginReceive,當數據處理完成之後,和Socket連接一樣需再次執行獲取下次數據

         /// <summary>
        /// 數據接收
        /// </summary>
        /// <param name="ar">請求的Socket</param>
        private void ReadCallback(IAsyncResult ar)
        {
            //獲取並保存
            ClientState obj = ar.AsyncState as ClientState;
            Socket c_socket = obj.clientSocket;
            int bytes = c_socket.EndReceive(ar);                    
            //接收完成 重新給出buffer接收
            obj.buffer = new byte[ClientState.bufsize];
            c_socket.BeginReceive(obj.buffer, 0, ClientState.bufsize, 0, new AsyncCallback(ReadCallback), obj);
         }
View Code

Send,消息發送比較簡單,將發送的數組轉成數組的形式進行發送

        /// <summary>
        /// 發送消息
        /// </summary>
        /// <param name="s_socket">指定客戶端socket</param>
        /// <param name="message">發送消息</param>
        /// <param name="Shake">發送消息</param>
        private void Send(Socket c_socket, byte[] by)
        {
               //發送
                c_socket.BeginSend(by, 0, by.Length, SocketFlags.None, asyncResult =>
                {
                    try
                    {
                        //完成消息發送
                        int len = c_socket.EndSend(asyncResult);
                    }
                    catch (Exception ex)
                    {
                        if (c_socket != null)
                        {
                            c_socket.Close();
                            c_socket = null;
                        }
                        Console.WriteLine("error ex=" + ex.Message + " " + ex.StackTrace);
                    }
                }, null);
        }
View Code

 

客戶端實現(實現不包含UI部分 最後面放所有代碼和下載地址)

相對於服務端差別不大,只是需要在鏈接之後增加一個監聽機制,進行斷線重連,我寫的這個比較粗糙。

BeginConnect,使用指定的地址ip+port進新一個非同步的鏈接

Connect,鏈接回調

ConnectionResult,初始值為-2 在消息接收(可檢測服務端斷開),鏈接(鏈接失敗)重新賦值然後判斷是否為錯誤碼然後重連

        /// <summary>
        /// 連接Socket
        /// </summary>
        public void Start()
        {
            try
            {
                var endPoint = new IPEndPoint(IPAddress.Parse(ip), port);
                c_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                c_socket.BeginConnect(endPoint, new AsyncCallback(Connect), c_socket);//鏈接服務端
                th_socket = new Thread(MonitorSocker);//監聽線程
                th_socket.IsBackground = true;
                th_socket.Start();           
           }
           catch (SocketException ex)
           {
                Console.WriteLine("error ex=" + ex.Message + " " + ex.StackTrace);
            }
        }

        //監聽Socket
        void MonitorSocker()
        {
            while (true)
            {
                if (ConnectionResult != 0 && ConnectionResult != -2)//通過錯誤碼判斷
                {
                    Start();
                }
                Thread.Sleep(1000);
            }
        }
View Code

 

Connec鏈接部分

/// <summary>
        /// 連接服務端
        /// </summary>
        /// <param name="ar"></param>
        private void Connect(IAsyncResult ar)
        {
            try
            {
                ServiceState obj = new ServiceState();
                Socket client = ar.AsyncState as Socket;
                obj.serviceSocket = client;
                //獲取服務端信息
                client.EndConnect(ar);
                //接收連接Socket數據 
                client.BeginReceive(obj.buffer, 0, ServiceState.bufsize, SocketFlags.None, new AsyncCallback(ReadCallback), obj);
catch (SocketException ex)
            {
                ConnectionResult = ex.ErrorCode;
                Console.WriteLine(ex.Message + " " + ex.StackTrace);
            }
        }
View Code

 

整體數據發送和實現部分

聲明一個指定的類,給出指定的標識,方便數據處理,

在發送文字消息,圖片消息,震動時需要對應的判斷其實最簡便的方法是直接發送一個XML報文過去直接解析,

也可以採用在頭部信息裡面直接給出傳送類型。

數組中給出了一個0-15的信息頭

0-3 標識碼,確認身份

4-7 總長度,總體長度可用於接收時判斷所需長度

8-11 內容長度,判斷內容是否接收完成

12-15 補0,1111(震動) 2222(圖片數據,圖片這塊為了接收圖片名稱所以採用XML報文形式發送)

16開始為內容

/// <summary>
    /// 0-3 標識碼 4-7 總長度 8-11 內容長度 12-15補0 16開始為內容
    /// </summary>
    public class SendSocket
    {
        /// <summary>
        /// 頭 標識8888
        /// </summary>
        byte[] Header = new byte[4] { 0x08, 0x08, 0x08, 0x08 };

        /// <summary>
        /// 文本消息
        /// </summary>
        public string Message;

        /// <summary>
        /// 是否發送震動
        /// </summary>
        public bool SendShake = false;

        /// <summary>
        /// 是否發送圖片
        /// </summary>
        public bool SendImg = false;

        /// <summary>
        /// 圖片名稱
        /// </summary>
        public string ImgName;

        /// <summary>
        /// 圖片數據
        /// </summary>
        public string ImgBase64;
        /// <summary>
        /// 組成特定格式的byte數據
        /// 12-15 為指定發送內容 1111(震動) 2222(圖片數據)
        /// </summary>
        /// <param name="mes">文本消息</param>
        /// <param name="Shake">震動</param>
        /// <param name="Img">圖片</param>
        /// <returns>特定格式的byte</returns>
        public byte[] ToArray()
        {
            if (SendImg)//是否發送圖片
            {
                //組成XML接收 可以接收相關圖片數據
                StringBuilder xmlResult = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
                xmlResult.Append("<ImgMessage>");
                xmlResult.AppendFormat("<ImgName>{0}</ImgName>", ImgName);
                xmlResult.AppendFormat("<ImgBase64>{0}</ImgBase64>", ImgBase64);
                xmlResult.Append("</ImgMessage>");
                Message = xmlResult.ToString();
            }

            byte[] byteData = Encoding.UTF8.GetBytes(Message);//內容
            int count = 16 + byteData.Length;//總長度
            byte[] SendBy = new byte[count];
            Array.Copy(Header, 0, SendBy, 0, Header.Length);//添加頭

            byte[] CountBy = BitConverter.GetBytes(count);
            Array.Copy(CountBy, 0, SendBy, 4, CountBy.Length);//總長度

            byte[] ContentBy = BitConverter.GetBytes(byteData.Length);
            Array.Copy(ContentBy, 0, SendBy, 8, ContentBy.Length);//內容長度

            if (SendShake)//發動震動
            {
                var shakeBy = new byte[4] { 1, 1, 1, 1 };
                Array.Copy(shakeBy, 0, SendBy, 12, shakeBy.Length);//震動
            }

            if (SendImg)//發送圖片
            {
                var imgBy = new byte[4] { 2, 2, 2, 2 };
                Array.Copy(imgBy, 0, SendBy, 12, imgBy.Length);//圖片
            }

            Array.Copy(byteData, 0, SendBy, 16, byteData.Length);//內容
            return SendBy;
        }
    }
View Code

 

代碼:

服務端:

窗體(UI)代碼:

namespace SocketService
{
    partial class Form1
    {
        /// <summary>
        /// 必需的設計器變數。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的資源。
        /// </summary>
        /// <param name="disposing">如果應釋放托管資源,為 true;否則為 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗體設計器生成的代碼

        /// <summary>
        /// 設計器支持所需的方法 - 不要修改
        /// 使用代碼編輯器修改此方法的內容。
        /// </summary>
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.txt_ip = new System.Windows.Forms.TextBox();
            this.txt_port = new System.Windows.Forms.TextBox();
            this.label2 = new System.Windows.Forms.Label();
            this.btn_StartSocket = new System.Windows.Forms.Button();
            this.txt_Monitor = new System.Windows.Forms.TextBox();
            this.label3 = new System.Windows.Forms.Label();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.btn_SendImg = new System.Windows.Forms.Button();
            this.btn_SendShake = new System.Windows.Forms.Button();
            this.btn_SendMes = new System.Windows.Forms.Button();
            this.txt_Mes = new System.Windows.Forms.TextBox();
            this.label4 = new System.Windows.Forms.Label();
            this.dataGridView1 = new System.Windows.Forms.DataGridView();
            this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
            this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
            this.groupBox2 = new System.Windows.Forms.GroupBox();
            this.lab_ImgName = new System.Windows.Forms.Label();
            this.pictureBox1 = new System.Windows.Forms.PictureBox();
            this.listBox_Mes = new System.Windows.Forms.ListBox();
            this.listBox_attribute = new System.Windows.Forms.ListBox();
            this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog();
            this.btn_Stop = new System.Windows.Forms.Button();
            this.label5 = new System.Windows.Forms.Label();
            this.label6 = new System.Windows.Forms.Label();
            this.label7 = new System.Windows.Forms.Label();
            this.groupBox1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
            this.groupBox2.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(13, 13);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(17, 12);
            this.label1.TabIndex = 0;
            this.label1.Text = "IP";
            // 
            // txt_ip
            // 
            this.txt_ip.Location = new System.Drawing.Point(36, 10);
            this.txt_ip.Name = "txt_ip";
            this.txt_ip.Size = new System.Drawing.Size(100, 21);
            this.txt_ip.TabIndex = 1;
            this.txt_ip.Text = "127.0.0.1";
            // 
            // txt_port
            // 
            this.txt_port.Location = new System.Drawing.Point(183, 10);
            this.txt_port.Name = "txt_port";
            this.txt_port.Size = new System.Drawing.Size(100, 21);
            this.txt_port.TabIndex = 3;
            this.txt_port.Text = "9999";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(148, 15);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(29, 12);
            this.label2.TabIndex = 2;
            this.label2.Text = "";
            // 
            // btn_StartSocket
            // 
            this.btn_StartSocket.Location = new System.Drawing.Point(307, 8);
            this.btn_StartSocket.Name = "btn_StartSocket";
            this.btn_StartSocket.Size = new System.Drawing.Size(75, 23);
            this.btn_StartSocket.TabIndex = 4;
            this.btn_StartSocket.Text = "開始監聽";
            this.btn_StartSocket.UseVisualStyleBackColor = true;
            this.btn_StartSocket.Click += new System.EventHandler(this.btn_StartSocket_Click);
            // 
            // txt_Monitor
            // 
            this.txt_Monitor.Location = new System.Drawing.Point(460, 8);
            this.txt_Monitor.Name = "txt_Monitor";
            this.txt_Monitor.ReadOnly = true;
            this.txt_Monitor.Size = new System.Drawing.Size(155, 21);
            this.txt_Monitor.TabIndex = 6;
            // 
            // label3
            // 
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(401, 11);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(53, 12);
            this.label3.TabIndex = 5;
            this.label3.Text = "正在監聽";
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.btn_SendImg);
            this.groupBox1.Controls.Add(this.btn_SendShake);
            this.groupBox1.Controls.Add(this.btn_SendMes);
            this.groupBox1.Controls.Add(this.txt_Mes);
            this.groupBox1.Controls.Add(this.label4);
            this.groupBox1.Controls.Add(this.dataGridView1);
            this.groupBox1.Location = new System.Drawing.Point(13, 68);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(681, 275);
            this.groupBox1.TabIndex = 8;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "客戶端連接列表";
            // 
            // btn_SendImg
            // 
            this.btn_SendImg.Location = new System.Drawing.Point(466, 246);
            this.btn_SendImg.Name = "btn_SendImg";
            this.btn_SendImg.Size = new System.Drawing.Size(75, 23);
            this.btn_SendImg.TabIndex = 11;
            this.btn_SendImg.Text = "發送圖片";
            this.btn_SendImg.UseVisualStyleBackColor = true;
            this.btn_SendImg.Click += new System.EventHandler(this.btn_SendImg_Click);
            // 
            // btn_SendShake
            // 
            this.btn_SendShake.Location = new System.Drawing.Point(380, 246);
            this.btn_SendShake.Name = "btn_SendShake";
            this.btn_SendShake.Size = new System.Drawing.Size(75, 23);
            this.btn_SendShake.TabIndex = 10;
            this.btn_SendShake.Text = "發送震動";
            this.btn_SendShake.UseVisualStyleBackColor = true;
            this.btn_SendShake.Click += new System.EventHandler(this.btn_SendShake_Click);
            // 
            // btn_SendMes
            // 
            this.btn_SendMes.Location = new System.Drawing.Point(294, 246);
            this.btn_SendMes.Name = "btn_SendMes";
            this.btn_SendMes.Size = new System.Drawing.Size(75, 23);
            this.btn_SendMes.TabIndex = 9;
            this.btn_SendMes.Text = "發送";
            this.btn_SendMes.UseVisualStyleBackColor = true;
            this.btn_SendMes.Click += new System.EventHandler(this.btn_SendMes_Click);
            // 
            // txt_Mes
            // 
            this.txt_Mes.Location = new System.Drawing.Point(294, 50);
            this.txt_Mes.Multiline = true;
            this.txt_Mes.Name = "txt_Mes";
            this.txt_Mes.Size = new System.Drawing.Size(368, 190);
            this.txt_Mes.TabIndex = 2;
            // 
            // label4
            // 
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(292, 35);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(53, 12);
            this.label4.TabIndex = 1;
            this.label4.Text = "消息輸入";
            // 
            // dataGridView1
            // 
            this.dataGridView1.AllowUserToOrderColumns = true;
            this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
            this.Column1,
            this.Column2});
            this.dataGridView1.Location = new System.Drawing.Point(7, 35);
            this.dataGridView1.Name = "dataGridView1";
            this.dataGridView1.RowTemplate.Height = 23;
            this.dataGridView1.Size = new System.Drawing.Size(249, 234);
            this.dataGridView1.TabIndex = 0;
            // 
            // Column1
            // 
            this.Column1.HeaderText = "IP";
            this.Column1.Name = "Column1";
            // 
            // Column2
            // 
            this.Column2.HeaderText = "Port";
            this.Column2.Name = "Column2";
            // 
            // groupBox2
            // 
            this.groupBox2.Controls.Add(this.label7);
            this.groupBox2.Controls.Add(this.label6);
            this.groupBox2.Controls.Add(this.label5);
            this.groupBox2.Co

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ...
  • 1. nginx+lua學習 1.1. 網關架構 1.2. nginx命令和信號控制 1. nginx s stop 快速關閉,不管有沒有正在處理的請求 nginx s quit 優雅關閉方式,推出前完成已經接受的連接請求 2. nginx c nginx配置文件地址 啟動 3. nginx s r ...
  • 前面介紹各種容器之時,通過在容器名稱後面添加包裹數據類型的一對尖括弧,表示該容器存放的是哪種類型的元素。這樣一來總算把Java當中的各類括弧都湊齊了,例如包裹一段代碼的花括弧、指定數組元素下標的方括弧、容納方法輸入參數的圓括弧,還有最近跟在容器名稱之後的尖括弧。可是為什麼尖括弧要加到容器後面呢?它還 ...
  • 問題 在實際開發過程當中我們可能會針對某些類型使用動態代理技術(AOP),註入了一些攔截器進行處理,但是一旦某個類型被動態代理了,那麼就會生成一個代理類。這個時候在該類內部使用 方法獲取到的 類型並不是實際類型的 ,而是代理類的 。 解決 那麼我們如何在代理類當中獲取到具體類型的 呢?其實 Cast ...
  • .NET開源快速開發框架Colder(NET452+AdminLTE版) 引言 ==== 半年前將基於Easyui的快速開發框架開源,三個版本(NET4.52,NETCore和NET4.0)總共榮獲200+星,得到了大家的認可,也很高興能夠幫助到大家。但不可否認的是Easyui在現在各大主流UI框架 ...
  • 在通過MVC和Angular聯合開發項目時,項目里有幾個重要的配置文件,下麵列出這幾個配置文件的分析和比較; 主要配置文件有appsettings.json,tsconfig.json,package.json,bower.json,.bowerrc,後面隨著學習的深入再繼續補充: e.g.: bo ...
  • 本來計劃接著上篇 C# Winform模仿百度日曆,發現一時半會寫不完,只寫了一小半還不全,暫且擱置下。現在計划下班後每天至少寫一篇博客,未能完成的等周末(不加班都情況)補充完整。 本篇博客窗體換膚,不是本人原創是之前 程式員之窗的作品。我看界面挺好,當時特別特別菜(當然現在依然也很菜),對此佩服的... ...
  • 關於CAS是個什麼東西,就不多閑扯了,相信每個有過SSO經驗的都聽過CAS大名,百度百科地址: https://baike.baidu.com/item/CAS/1329561?fr=aladdin 項目所用https:/github.com/apereo/dotnet-cas-client 在.N ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...