最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

C/S架構(gòu)

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

udp相比tcp來(lái)說(shuō)簡(jiǎn)單了很多, 不需要一直保持連接, 也不需要處理發(fā)送回調(diào), 因?yàn)閡dp不可靠, 只要發(fā)了就不管, 丟了也與我無(wú)關(guān). 而且因?yàn)椴恍枰WC順序, 所以沒(méi)有發(fā)送緩存, 只要請(qǐng)求發(fā)送, 立馬就發(fā), 收到的包也不會(huì)堆積, 肯定是整包, 所以我們也不需要處理粘包問(wèn)題.

整個(gè)實(shí)現(xiàn)的關(guān)鍵點(diǎn)有:

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

客戶(hù)端實(shí)現(xiàn)

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

客戶(hù)端主流程和實(shí)現(xiàn)

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

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

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

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

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

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

因?yàn)橹耙呀?jīng)和遠(yuǎn)端口綁定了, 所以客戶(hù)端可以直接發(fā)送消息, 在內(nèi)部會(huì)自動(dòng)分配一個(gè)客戶(hù)端自己的本地端口, 服務(wù)器端使用這個(gè)端口來(lái)向本客戶(hù)端發(fā)送消息, 我們會(huì)在服務(wù)器實(shí)現(xiàn)中看到.

客戶(hù)端監(jiān)聽(tīng)消息實(shí)現(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;
}

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

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

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

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

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

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

udpSocket.Receive(buffer): 從連接好的遠(yuǎn)端口接收消息, 這是一個(gè)阻斷性的操作, 在消息回來(lái)之前會(huì)停留在這里不動(dòng).

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

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

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

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

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

我們還能進(jìn)一步簡(jiǎn)化代碼:

// 開(kāi)始異步接收消息
static async void StartRecvMessage()
{
? ? Console.WriteLine("客戶(hù)端開(kāi)始監(jiān)聽(tīng): " + 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($"收到來(lái)自服務(wù)器的消息: {message}");
? ? }
}

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

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

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

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

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

// 綁定本地端口, 監(jiān)聽(tīng)來(lái)自于各個(gè)客戶(hù)端的消息
var endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8060);
udpSocket.Bind(endPoint);

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

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

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

服務(wù)器監(jiān)聽(tīng)消息實(shí)現(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ù)器開(kāi)始監(jiān)聽(tīng): " + 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($"收到來(lái)自客戶(hù)端[{endPoint}]的消息: {message}");

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

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

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

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

static async void StartRecvMessage()
{
? ? Console.WriteLine("服務(wù)器開(kāi)始監(jiān)聽(tīng): " + 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($"收到來(lái)自客戶(hù)端[{endPoint}]的消息: {message}");

? ? ? ? SendMessageToClient(endPoint, "服務(wù)器對(duì)你說(shuō)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("客戶(hù)端說(shuō):Hello Server!");
? ? ? ? ? ? StartRecvMessage();

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

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

? ? ? ? static async void StartRecvMessage()
? ? ? ? {
? ? ? ? ? ? Console.WriteLine("客戶(hù)端開(kāi)始監(jiān)聽(tīng): " + 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($"收到來(lái)自服務(wù)器的消息: {message}");

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

? ? ? ? ? ? ? ? Console.WriteLine("客戶(hù)端停止監(jiān)聽(tīng).");
? ? ? ? ? ? ? ? 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ù)器開(kāi)始監(jiān)聽(tīng): " + 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($"收到來(lái)自客戶(hù)端[{endPoint}]的消息: {message}");

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

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

? ? ? ? ? ? ? ? Console.WriteLine("服務(wù)器停止監(jiān)聽(tīng).");
? ? ? ? ? ? ? ? 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)鍵字實(shí)現(xiàn)了異步的udp通訊.

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

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

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

aync/await對(duì)于長(zhǎng)期寫(xiě)同步代碼或者使用異步callback形式回調(diào)的同學(xué)來(lái)說(shuō), 可能會(huì)有一定的理解困難,

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

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

相關(guān)文章

最新評(píng)論

油尖旺区| 浦东新区| 宁强县| 灌阳县| 类乌齐县| 军事| 湘潭县| 阜新市| 五莲县| 鹤庆县| 德阳市| 应城市| 丹寨县| 长葛市| 凤山县| 南靖县| 鞍山市| 裕民县| 贡觉县| 波密县| 房山区| 广宗县| 柳江县| 广灵县| 钦州市| 通许县| 蚌埠市| 安多县| 武邑县| 崇信县| 绿春县| 新田县| 滦南县| 仁怀市| 平乡县| 海阳市| 凯里市| 呼伦贝尔市| 边坝县| 新闻| 长春市|