欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#中使用async和await實現(xiàn)異步Udp通訊的示例代碼

 更新時間:2022年07月28日 09:17:03   作者:拂面清風(fēng)三點水  
本文主要介紹了C#中使用async和await實現(xiàn)異步Udp通訊的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

在之前的C#版本中, 如果我們想要進行異步的Udp, 需要單開線程接收消息, C#7.1開始, 我們可以使用async/await關(guān)鍵字來編寫異步代碼, 我們今天一起來探索怎么實現(xiàn).

C/S架構(gòu)

我們要實現(xiàn)兩個app, 一個客戶端和一個服務(wù)器, 各自都可以發(fā)消息和收消息.
發(fā)消息很簡單, 收消息的話需要一直在端口上監(jiān)聽.

udp相比tcp來說簡單了很多, 不需要一直保持連接, 也不需要處理發(fā)送回調(diào), 因為udp不可靠, 只要發(fā)了就不管, 丟了也與我無關(guān). 而且因為不需要保證順序, 所以沒有發(fā)送緩存, 只要請求發(fā)送, 立馬就發(fā), 收到的包也不會堆積, 肯定是整包, 所以我們也不需要處理粘包問題.

整個實現(xiàn)的關(guān)鍵點有:

  • Sockets.socket: socket類, tcp和udp共用.
  • System.Net.IPEndPoint: 端口類, tcp和udp共用.
  • Sockets.socket.Bind: 綁定本地端口方法, 主要是服務(wù)器使用.
  • Sockets.socket.Create: 綁定遠端端口方法, 主要是客戶端使用.
  • Sockets.socket.SendTo: 向指定端口發(fā)送數(shù)據(jù), 主要是服務(wù)器使用.
  • Sockets.socket.ReceiveFrom: 從指定端口接收數(shù)據(jù), 主要是服務(wù)器使用.
  • Sockets.socket.Send: 從綁定的端口發(fā)送數(shù)據(jù), 主要是客戶端使用.
  • Sockets.socket.Receive: 從綁定的端口接收數(shù)據(jù), 主要是客戶端使用.
  • async 關(guān)鍵字: 標識方法為異步方法.
  • await 關(guān)鍵字: 標識異步執(zhí)行方法, 等待返回.
  • System.Threading.Tasks.Task: 異步任務(wù)類

客戶端實現(xiàn)

我們先來研究客戶端, 服務(wù)器的實現(xiàn)大致相同, 只是有細微的差別.

客戶端主流程和實現(xiàn)

// 構(gòu)建socket對象
Socket udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

// 連接遠端口, 用于向遠端發(fā)送消息, 這里是自己的機器同時當服務(wù)器和客戶端, 所以都是127...
// 注意這里的連接只是將`socket`對象與ip和端口綁定, 不是tcp中的連接概念.
// 內(nèi)部會分配新的本地端口, 發(fā)送給遠端, 供遠端使用
var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8060);
udpSocket.Connect(endPoint);

// 發(fā)送消息
SendMessageToServer("客戶端說:Hello Server!");

// 監(jiān)聽消息
StartRecvMessage();
Console.ReadKey();

客戶端發(fā)送消息實現(xiàn)

static void SendMessageToServer(string message)
{
    udpSocket.Send(Encoding.UTF8.GetBytes(message));
}

因為之前已經(jīng)和遠端口綁定了, 所以客戶端可以直接發(fā)送消息, 在內(nèi)部會自動分配一個客戶端自己的本地端口, 服務(wù)器端使用這個端口來向本客戶端發(fā)送消息, 我們會在服務(wù)器實現(xiàn)中看到.

客戶端監(jiān)聽消息實現(xiàn)

// 從byte中轉(zhuǎn)換string
static string ConverBytesToString(Decoder decoder, byte[] bytes, int len)
{
? ? var nchar = decoder.GetCharCount(bytes, 0, len);

? ? var bytesChar = new char[nchar];
? ? nchar = decoder.GetChars(bytes, 0, len, bytesChar, 0);

? ? var result = new string(bytesChar, 0, nchar);
? ? return result;
}

// 從連接的端口接收消息, 返回讀取到的字節(jié)數(shù)
static int SocketRecvMessage()
{
? ? var nread = udpSocket.Receive(buffer);
? ? return nread;
}

// 開始異步接收消息
static async void StartRecvMessage()
{
? ? Console.WriteLine("客戶端開始監(jiān)聽: " + udpSocket.LocalEndPoint);
? ? var decoder8 = Encoding.UTF8.GetDecoder();

? ? while (true)
? ? {
? ? ? ? var nread = await Task.Run<int>(SocketRecvMessage);
? ? ? ? var message = ConverBytesToString(decoder8, buffer, nread);

? ? ? ? Console.WriteLine($"收到來自服務(wù)器的消息: {message}");
? ? }
}

上面的代碼中, 主要的部分是:

async/await/Task.Run<int>(xxx):

  • async:標識方法StartRecvMessage將采用異步方式執(zhí)行
  • await: 標識要等待的操作, 而這種操作是需要耗時的, 比如socket, io等, 也可以是單純就是要等待多久(Task.Delay(500); // 等待500ms).
  • Task.Run<int>(xxx): 將耗時的操作包裝為異步任務(wù)(類似開了一個線程來執(zhí)行該操作).

udpSocket.Receive(buffer): 從連接好的遠端口接收消息, 這是一個阻斷性的操作, 在消息回來之前會停留在這里不動.

上面的異步還能寫成下面的形式, 只是將耗時操作推遲到了更具體的操作而已:

// 從連接的端口接收消息, 返回讀取到的字節(jié)數(shù)
static async Task<int> SocketRecvMessage()
{
? ? var nread = await Task.Run<int>(() => udpSocket.Receive(buffer));
? ? return nread;
}

// 開始異步接收消息
static async void StartRecvMessage()
{
? ? Console.WriteLine("客戶端開始監(jiān)聽: " + udpSocket.LocalEndPoint);
? ? var decoder8 = Encoding.UTF8.GetDecoder();

? ? while (true)
? ? {
? ? ? ? var nread = await SocketRecvMessage();
? ? ? ? var message = ConverBytesToString(decoder8, buffer, nread);

? ? ? ? Console.WriteLine($"收到來自服務(wù)器的消息: {message}");
? ? }
}

我們還能進一步簡化代碼:

// 開始異步接收消息
static async void StartRecvMessage()
{
? ? Console.WriteLine("客戶端開始監(jiān)聽: " + udpSocket.LocalEndPoint);
? ? var decoder8 = Encoding.UTF8.GetDecoder();

? ? while (true)
? ? {
? ? ? ? var nread = await Task.Run<int>(() => udpSocket.Receive(buffer));
? ? ? ? var message = ConverBytesToString(decoder8, buffer, nread);

? ? ? ? Console.WriteLine($"收到來自服務(wù)器的消息: {message}");
? ? }
}

服務(wù)器實現(xiàn)

服務(wù)器和客戶端的實現(xiàn)差別很小.

主要區(qū)別在于服務(wù)器針對的是很多客戶端, 所以在收發(fā)消息上對于端口的處理不一樣.

服務(wù)器主流程和實現(xiàn)

// 構(gòu)建socket對象
Socket udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

// 綁定本地端口, 監(jiān)聽來自于各個客戶端的消息
var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8060);
udpSocket.Bind(endPoint);

// 監(jiān)聽消息
StartRecvMessage();
Console.ReadKey();

服務(wù)器發(fā)送消息實現(xiàn)

// 向指定的客戶端端口發(fā)送消息
// 注意這里和客戶端的實現(xiàn)不一樣, 還是因為服務(wù)器會對應(yīng)多個客戶端, 所以每次發(fā)送都需要指明目的地
static void SendMessageToClient(EndPoint endPoint, string message)
{
? ? udpSocket.SendTo(Encoding.UTF8.GetBytes(message), endPoint);
}

服務(wù)器監(jiān)聽消息實現(xiàn)

static (int, EndPoint) SocketRecvMessage()
{
? ? EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);

? ? var nread = udpSocket.ReceiveFrom(buffer, ref endPoint);
? ? return (nread, endPoint);
}

static async void StartRecvMessage()
{
? ? Console.WriteLine("服務(wù)器開始監(jiān)聽: " + udpSocket.LocalEndPoint);
? ? var decoder8 = Encoding.UTF8.GetDecoder();

? ? while(true)
? ? {
? ? ? ? var (nread, endPoint) = await Task.Run<(int, EndPoint)>(SocketRecvMessage);
? ? ? ? var message = ConverBytesToString(decoder8, buffer, nread);

? ? ? ? Console.WriteLine($"收到來自客戶端[{endPoint}]的消息: {message}");

? ? ? ? SendMessageToClient(endPoint, "服務(wù)器對你說Hi!");
? ? }
}

上面的代碼中, 主要的差別在對于端口的處理上:

  • SocketRecvMessage返回的是一個元組(int, EndPoint): 即讀取到的字節(jié)數(shù), 還有客戶端的端口信息.
  • ReceiveFrom: 接收消息指定了端口, 服務(wù)器每次接收消息都要使用端口信息用來標識發(fā)送消息的客戶端.

優(yōu)化過后的代碼為:

static async void StartRecvMessage()
{
? ? Console.WriteLine("服務(wù)器開始監(jiān)聽: " + udpSocket.LocalEndPoint);
? ? var decoder8 = Encoding.UTF8.GetDecoder();

? ? while(true)
? ? {
? ? ? ? EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
? ? ? ? var nread = await Task.Run<int>(() => udpSocket.ReceiveFrom(buffer, ref endPoint));
? ? ? ? var message = ConverBytesToString(decoder8, buffer, nread);

? ? ? ? Console.WriteLine($"收到來自客戶端[{endPoint}]的消息: {message}");

? ? ? ? SendMessageToClient(endPoint, "服務(wù)器對你說Hi!");
? ? }
}

下面是完整的代碼:

// --- AsyncUdpClient.cs

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace test
{
? ? class AsyncUdpClient
? ? {
? ? ? ? static Socket udpSocket;
? ? ? ? static byte[] buffer = new byte[4096];

? ? ? ? public static void Main()
? ? ? ? {
? ? ? ? ? ? udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

? ? ? ? ? ? var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8060);
? ? ? ? ? ? //udpSocket.Bind(endPoint);
? ? ? ? ? ? udpSocket.Connect(endPoint);

? ? ? ? ? ? SendMessageToServer("客戶端說:Hello Server!");
? ? ? ? ? ? StartRecvMessage();

? ? ? ? ? ? Console.ReadKey();
? ? ? ? }

? ? ? ? static void SendMessageToServer(string message)
? ? ? ? {
? ? ? ? ? ? udpSocket.Send(Encoding.UTF8.GetBytes(message));
? ? ? ? }

? ? ? ? static async void StartRecvMessage()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("客戶端開始監(jiān)聽: " + udpSocket.LocalEndPoint);
? ? ? ? ? ? var decoder8 = Encoding.UTF8.GetDecoder();

? ? ? ? ? ? while (true)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? var nread = await Task.Run<int>(() => udpSocket.Receive(buffer));
? ? ? ? ? ? ? ? var message = ConverBytesToString(decoder8, buffer, nread);

? ? ? ? ? ? ? ? Console.WriteLine($"收到來自服務(wù)器的消息: {message}");

? ? ? ? ? ? ? ? #region 交互
? ? ? ? ? ? ? ? Console.WriteLine("是否繼續(xù)監(jiān)聽?[yes|no]");
? ? ? ? ? ? ? ? var str = await Task.Run<string>(() => Console.ReadLine());
? ? ? ? ? ? ? ? if (str == "yes")
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? Console.WriteLine("繼續(xù)監(jiān)聽...");
? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? Console.WriteLine("客戶端停止監(jiān)聽.");
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? ? ? #endregion
? ? ? ? ? ? }
? ? ? ? }

? ? ? ? static string ConverBytesToString(Decoder decoder, byte[] bytes, int len)
? ? ? ? {
? ? ? ? ? ? var nchar = decoder.GetCharCount(bytes, 0, len);

? ? ? ? ? ? var bytesChar = new char[nchar];
? ? ? ? ? ? nchar = decoder.GetChars(bytes, 0, len, bytesChar, 0);

? ? ? ? ? ? var result = new string(bytesChar, 0, nchar);
? ? ? ? ? ? return result;
? ? ? ? }
? ? }
}

// --- AsyncUdpServer.cs

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace test
{
? ? static class AsyncUdpServer
? ? {
? ? ? ? static Socket udpSocket;
? ? ? ? static byte[] buffer = new byte[4096];

? ? ? ? public static void Main()
? ? ? ? {
? ? ? ? ? ? udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

? ? ? ? ? ? var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8060);
? ? ? ? ? ? udpSocket.Bind(endPoint);
? ? ? ? ? ? //udpSocket.Connect(endPoint);

? ? ? ? ? ? StartRecvMessage();
? ? ? ? ? ? Console.ReadKey();
? ? ? ? }

? ? ? ? static void SendMessageToClient(EndPoint endPoint, string message)
? ? ? ? {
? ? ? ? ? ? udpSocket.SendTo(Encoding.UTF8.GetBytes(message), endPoint);
? ? ? ? }

? ? ? ? static async void StartRecvMessage()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("服務(wù)器開始監(jiān)聽: " + udpSocket.LocalEndPoint);
? ? ? ? ? ? var decoder8 = Encoding.UTF8.GetDecoder();

? ? ? ? ? ? while(true)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
? ? ? ? ? ? ? ? var nread = await Task.Run<int>(() => udpSocket.ReceiveFrom(buffer, ref endPoint));
? ? ? ? ? ? ? ? var message = ConverBytesToString(decoder8, buffer, nread);

? ? ? ? ? ? ? ? Console.WriteLine($"收到來自客戶端[{endPoint}]的消息: {message}");

? ? ? ? ? ? ? ? SendMessageToClient(endPoint, "服務(wù)器對你說Hi!");

#region 交互
? ? ? ? ? ? ? ? Console.WriteLine("是否繼續(xù)監(jiān)聽?[yes|no]");
? ? ? ? ? ? ? ? var str = await Task.Run<string>(()=> Console.ReadLine());
? ? ? ? ? ? ? ? if (str == "yes")
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? Console.WriteLine("繼續(xù)監(jiān)聽...");
? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? Console.WriteLine("服務(wù)器停止監(jiān)聽.");
? ? ? ? ? ? ? ? return;
#endregion

? ? ? ? ? ? }
? ? ? ? }

? ? ? ? static string ConverBytesToString(Decoder decoder, byte[] bytes, int len)
? ? ? ? {
? ? ? ? ? ? var nchar = decoder.GetCharCount(bytes, 0, len);

? ? ? ? ? ? var bytesChar = new char[nchar];
? ? ? ? ? ? nchar = decoder.GetChars(bytes, 0, len, bytesChar, 0);

? ? ? ? ? ? var result = new string(bytesChar, 0, nchar);
? ? ? ? ? ? return result;
? ? ? ? }
? ? }
}

總結(jié)

今天我們使用aync/await關(guān)鍵字實現(xiàn)了異步的udp通訊.

主要是了解和實踐異步關(guān)鍵字的知識和使用, 同時對傳統(tǒng)的單開線程來進行udp通訊方式進行了優(yōu)化, 這

樣的好處是不需要自己維護多線程環(huán)境, 不需要保證線程安全, 各種鎖之類的操作.

udp通訊本身很簡單, 只要搞清楚Bind, Connect還有端口的概念即可.

aync/await對于長期寫同步代碼或者使用異步callback形式回調(diào)的同學(xué)來說, 可能會有一定的理解困難,

但是其實也就那么回事, 我們簡單理解為協(xié)程即可(只是比協(xié)程使用起來更方便).

到此這篇關(guān)于C#中使用async和await實現(xiàn)異步Udp通訊的示例代碼的文章就介紹到這了,更多相關(guān)C# 異步Udp通訊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論