C#中非同步socket

2021-05-26 07:00:16

Socket是什麼

參考:https://blog.csdn.net/jia12216/article/details/82702960

Socket是應用層與TCP/IP協定族通訊的中間軟體抽象層,它是一組介面。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協定族隱藏在Socket介面後面,對使用者來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協定。
vv

Socket通訊過程介紹

伺服器端先初始化Socket,然後與埠繫結(bind),對埠進行監聽(listen),呼叫accept阻塞,等待使用者端連線。在這時如果有個使用者端初始化一個Socket,然後連線伺服器(connect),如果連線成功,這時使用者端與伺服器端的連線就建立了。使用者端傳送資料請求,伺服器端接收請求並處理請求,然後把迴應資料傳送給使用者端,使用者端讀取資料,最後關閉連線,一次互動結束。
在這裡插入圖片描述

單執行緒、多執行緒、同步、非同步概念理解

單執行緒:一個任務從頭至尾由一執行緒完成,在一個任務完成之前,不接受第二個任務。
多執行緒 ,:將一個任務拆分成不同的階段(部分),並交給不同的 執行緒 分別處理。

什麼是同步,什麼是非同步
非同步:和同步相對,同步是順序執行,而非同步是彼此獨立,在等待某個事件的過程中繼續做自己的事,不要等待這一事件完成後再工作。執行緒是實現非同步的一個方式,非同步是讓呼叫方法的主執行緒不需要同步等待另一個執行緒的完成,從而讓主執行緒幹其他事情。
我們把在完成了一次呼叫後通過狀態通知回撥來告知呼叫者的方式稱為非同步過程
在非同步過程中當呼叫一個方法時,呼叫者並不能夠立刻得到結果,只有當這個方法呼叫完畢後呼叫者才能獲得呼叫結果。這樣做的好處是什麼呢?答案是高效。
以資料傳輸為例:不必等待該資料傳輸完成並返回socket通訊結果,直接繼續傳輸。一個 顧客一邊等待設定、一邊做些別的事情,就是多執行緒了。

非同步和多執行緒:不是同等關係,非同步是目的,多執行緒只是實現非同步的一個手段,實現非同步可以採用多執行緒技術或者交給其他程序來處理。
顯而易見,非同步 可以提供更高的效率,它可以利用 等待 的時間去完成一些事情。

非同步Socket程式設計程式碼

程式碼參考https://gameinstitute.qq.com/community/detail/119099

伺服器端程式碼

using System;  
using System.Collections.Generic;  
using System.Text;  
using System.Net;  
using System.Net.Sockets;  
  
namespace AsyncServer  
{  
    public class AsyncTCPServer  
    {  
        public void Start()  
        {  
            //建立通訊端  
            IPEndPoint ipe = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6065);  
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
            //繫結埠和IP  
            socket.Bind(ipe);  
            //設定監聽  
            socket.Listen(10);  
            //連線使用者端  
            AsyncAccept(socket);  
        }  
  
        /// <summary>  
        /// 連線到使用者端  
        /// </summary>  
        /// <param name="socket"></param>  
        private void AsyncAccept(Socket socket)  
        {  
            socket.BeginAccept(asyncResult =>  
            {  
                //獲取使用者端通訊端  
                Socket client = socket.EndAccept(asyncResult);  
                Console.WriteLine(string.Format("使用者端{0}請求連線...", client.RemoteEndPoint));  
                AsyncSend(client, "伺服器收到連線請求");  
                AsyncSend(client, string.Format("歡迎你{0}",client.RemoteEndPoint));  
                AsyncReveive(client);  
            }, null);  
        }  
  
        /// <summary>  
        /// 接收訊息  
        /// </summary>  
        /// <param name="client"></param>  
        private void AsyncReveive(Socket socket)  
        {  
            byte[] data = new byte[1024];  
            try  
            {  
                //開始接收訊息  
                socket.BeginReceive(data, 0, data.Length, SocketFlags.None,  
                asyncResult =>  
                {  
                    int length = socket.EndReceive(asyncResult);  
                    Console.WriteLine(string.Format("使用者端傳送訊息:{0}", Encoding.UTF8.GetString(data)));  
                }, null);  
            }  
            catch (Exception ex)  
            {  
                Console.WriteLine(ex.Message);  
            }  
        }  
  
        /// <summary>  
        /// 傳送訊息  
        /// </summary>  
        /// <param name="client"></param>  
        /// <param name="p"></param>  
        private void AsyncSend(Socket client, string p)  
        {  
            if (client == null || p == string.Empty) return;  
            //資料轉碼  
            byte[] data = new byte[1024];  
            data = Encoding.UTF8.GetBytes(p);  
            try  
            {  
                //開始傳送訊息  
                client.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>  
                {  
                    //完成訊息傳送  
                    int length = client.EndSend(asyncResult);  
                    //輸出訊息  
                    Console.WriteLine(string.Format("伺服器發出訊息:{0}", p));  
                }, null);  
            }  
            catch (Exception e)  
            {  
                Console.WriteLine(e.Message);  
            }  
        }  
    }  
}  

其中使用的非同步操作函數

參考 https://blog.csdn.net/freesundark/article/details/5565069
在這裡插入圖片描述

(1)以BeginAccept(AsynscCallBack,object)為例,開始一個非同步操作接受一個連線嘗試。

引數:一個委託。一個物件。物件包含此請求的狀態資訊。
其中回撥方法必須使用EndAccept方法
應用程式呼叫BeginAccept方法後,系統會使用單獨的執行緒執行指定的回撥方法並在EndAccept上一直處於阻塞狀態,直至監測到掛起的連結。EndAccept會返回新的socket物件供你來同遠端主機資料互動。返回的socket即為Client

(2)BeginSend()表示開始將資料非同步傳送到連線的Socket,它最常用的宣告如下所示。

 public IAsyncResult BeginSend(byte[] buffer,  int offset,int size,SocketFlags socketFlags,  AsyncCallback callback,object state);  

Buffer表示要傳送的資料,offset表示buffer中傳送資料的位置,size為傳送位元組數的大小,socketFlags指SocketFlags值的按位元組合。

(3)Socket建構函式:

public socket(AddressFamily 定址型別, SocketType 通訊端型別, ProtocolType 協定型別)

但需要注意的是通訊端型別與協定型別並不是可以隨便組合。
從上表中可以看出,這些方法都是成對出現的。這些方法能避免網路通訊中的阻塞現象。這些方法的使用機制是在Begin開頭的方法中註冊一個回撥函數,當對應的事件發生時,呼叫該回撥函數,且在回撥函數中呼叫對應的End開頭的方法。

使用者端程式碼

using System;  
using System.Collections.Generic;  
using System.Text;  
using System.Net;  
using System.Net.Sockets;  
  
namespace AsyncClient  
{  
    public class AsyncTCPClient  
    {  
        /// <summary>  
        /// 連線到伺服器  
        /// </summary>  
        public void AsynConnect()  
        {  
            //埠及IP  
            IPEndPoint ipe = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6065);  
            //建立通訊端  
            Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
            //開始連線到伺服器  
            client.BeginConnect(ipe, asyncResult =>  
            {  
                client.EndConnect(asyncResult);  
                //向伺服器傳送訊息  
                AsynSend(client,"你好我是使用者端");  
                AsynSend(client, "第一條訊息");  
                AsynSend(client, "第二條訊息");  
                //接受訊息  
                AsynRecive(client);  
            }, null);  
        }  
  
        /// <summary>  
        /// 傳送訊息  
        /// </summary>  
        /// <param name="socket"></param>  
        /// <param name="message"></param>  
        public void AsynSend(Socket socket, string message)  
        {  
            if (socket == null || message == string.Empty) return;  
            //編碼  
            byte[] data = Encoding.UTF8.GetBytes(message);  
            try  
            {  
                socket.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>  
                {  
                    //完成傳送訊息  
                    int length = socket.EndSend(asyncResult);  
                    Console.WriteLine(string.Format("使用者端傳送訊息:{0}", message));  
                }, null);  
            }  
            catch (Exception ex)  
            {  
                Console.WriteLine("異常資訊:{0}", ex.Message);  
            }  
        }  
  
        /// <summary>  
        /// 接收訊息  
        /// </summary>  
        /// <param name="socket"></param>  
        public void AsynRecive(Socket socket)  
        {  
            byte[] data = new byte[1024];  
            try  
            {  
                //開始接收資料  
                socket.BeginReceive(data, 0, data.Length, SocketFlags.None,  
                asyncResult =>  
                {  
                    int length = socket.EndReceive(asyncResult);  
                    Console.WriteLine(string.Format("收到伺服器訊息:{0}", Encoding.UTF8.GetString(data)));  
                    AsynRecive(socket);  
                }, null);  
            }  
            catch (Exception ex)  
            {  
                Console.WriteLine("異常資訊:", ex.Message);  
            }  
        }  
    }  
}  

參考https://blog.csdn.net/qq_40544338/article/details/103599590

C#中=>是什麼意思?

lambda表示式,表示一個匿名函數,=>前面的是引數,後面的是函數體。你可以把它當作一個函數。