在 C# 中创建一个 UDP 服务器
本文将展示如何在 C# 中创建一个简单的 UDP 服务器。
在 C#
中创建一个 UDP 服务器
简单介绍一下背景,UDP 协议不需要与客户端建立连接。数据只是传输而不验证客户端是否接收到它。
这种类型的协议通常用于广播数据。由于 UDP 是无连接的,因此侦听器无需在线即可接收消息。
要使用 UDP 传输数据报,你需要知道托管服务的网络设备的网络地址和服务的 UDP 端口号。流行服务的端口号由 Internet 号码分配机构 (IANA) 定义;端口号的范围是 1,024 到 65,535,可以分配给不在 IANA 列表中的服务。
在基于 IP 的网络上,特殊的网络地址用于处理 UDP 广播消息。以下解释以 Internet 的 IP 版本 4 地址族为例。
通过设置主机标识的所有位,可以将广播定向到网络的指定部分。使用地址 192.168.1.255 向网络上所有 IP 地址以 192.168.1 开头的主机广播。
我们现在准备建立或创建一个套接字,设置我们的 UDP 协议,并立即开始通信。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
这些将是用于设置服务器的库。这些库支持与网络相关的所有基本功能,例如构建套接字、处理 IPV4 地址等等。
public class SimpleUdpSrvr {
public static void Main() {
int recv;
byte[] data = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
}
}
int recv
行创建一个整数类型的 recv
变量,其中包含客户端收到的字符串消息的字节数。
byte[] data = new byte[1024];
line 用于为每个包含 1024 个单元格的单元格创建一个名为 data
的字节大小的数组,也称为数组大小。该数组是在堆中动态创建的。
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
行告诉它为套接字创建一个 ipep
变量。传入的信息告诉它应该使用什么样的套接字。
IPEndPoint
类表示具有 IP 地址和端口号的网络端点;客户端需要此配置信息来连接我们的服务。传入的 IPAddress.Any
参数告诉服务器可以使用任何 IP 地址连接到 9050 端口号; ipep
变量也是动态创建的。
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
这一行创建了一个 newsock
变量,一个我们将通过它进行通信的套接字。传入的参数告诉我们要创建什么样的套接字。
AddressFamily.InterNetwork
告诉我们我们需要本地 IP 地址。SocketType.Dgram
参数指出数据应该以数据报而不是数据包的形式流动。
最后,ProtocolType.Udp
参数告诉我们将使用的套接字的协议类型。现在,我们的套接字已创建。
newsock.Bind(ipep);
Console.WriteLine("Waiting for a client...");
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)(sender);
到目前为止,我们已经在网络中创建了一个端点,以及一个用于通信的套接字,newsock.Bind(ipep);
line 现在将帮助我们将端点绑定到套接字。
ipep
是一个变量,它包含关于我们端点的信息,并在我们的 newsock
对象的 bind()
函数中传递。
Console.WriteLine("Waiting for a client...");
此行在屏幕上打印服务器正在等待客户端发送任何消息。我们需要为将尝试与服务器通信的发送方创建另一个端点。
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
这一行创建了另一个端点,变量称为 sender
。在它的 IPAddress.Any
中传递的参数告诉我们期待任何 IP 地址,而 0
意味着它是端口号的通配符,系统应该看到并找到任何合适的端口分配给我们。
EndPoint Remote = (EndPoint)(sender);
行用于查找发送者的 IP 地址并将其存储在 remote
变量中。
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine("Message received from {0}:", Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
我们在程序开始时创建的 recv
变量现在用于 recv = newsock.ReceiveFrom(data, ref Remote);
线。我们使用之前创建的套接字 newsock
的 Receive()
函数,并且当前接收我们当时在套接字中的所有数据。
传递 Receive()
函数的参数告诉在哪里存储数据以及应该存储或预期谁的数据。data
参数是被传递的字节大小数组,数据将被写入数据数组。
ref Remote
参数告诉 IP 地址发送了数据,该函数将返回从该套接字获取的字节数并将其存储到 recv
变量中。
Console.WriteLine("Message received from {0}:", Remote.ToString());
此行在屏幕上写入通过套接字发送数据的客户端的 IP 地址。传递的参数 Remote.ToString()
将十进制数字转换为 ASCII 字符,而 remote
变量是引用客户端的第二个端点。
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
这一行将实际数据写入屏幕。通过套接字发送的数据为原始形式,应转换为 ASCII 字符才能读取。
Encoding.ASCII.GetString(data, 0, recv)
行从作为参数传递的 data
变量中获取数据,recv
参数(包含客户端发送的字节数)告诉从屏幕上的 data
变量中写入这么多字节。
string welcome = "Welcome to my test server";
data = Encoding.ASCII.GetBytes(welcome);
newsock.SendTo(data, data.Length, SocketFlags.None, Remote);
string welcome = "欢迎来到我的测试服务器";
行用欢迎来到我的测试服务器
文本初始化欢迎
字符串变量。
data = Encoding.ASCII.GetBytes(welcome);
line 将 welcome
变量作为 Encoding.ASCII.GetBytes()
函数中的参数并将其转换为原始形式,以便准备好通过另一端的套接字发送。
newsock.SendTo(data, data.Length, SocketFlags.None, Remote);
这一行通过我们之前创建的套接字 newsock
发送数据并使用 Send()
函数。它需要的参数是:包含要发送的原始数据形式的 data
变量,data.Length
参数告诉在套接字上写入多少字节,因为数据的长度等于字节数, SocketFlags.None
参数告诉我们将所有标志保持为零,标志是指示符,每个标志都有其含义。
Remote
变量参数告诉我们要将数据发送到的客户端,因为 Remote
变量存储客户端的 IP 地址和端口号。
while (true) {
data = new byte[1024];
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
newsock.SendTo(data, recv, SocketFlags.None, Remote);
}
while(true)
行表明它是一个无限循环,并且它的所有语句都将无限运行。数据 = 新字节 [1024];
行在执行时创建一个长度为 1024 的新字节大小数组。
recv = newsock.ReceiveFrom(data, ref Remote);
line 从客户端接收数据并将其保存到数据数组中。
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
将数据写入屏幕。
newsock.SendTo(data, recv, SocketFlags.None, Remote);
行将服务器从它收到的相同数据发送回客户端。
之后,服务器将再次停止并等待客户端,客户端发送给服务器的任何内容都会以相同的消息进行响应。该服务器将永远不会终止,除非它崩溃或用户手动停止它。
现在,当我们学习了如何在 C# 中创建 UDP 服务器及其基本功能后,我们可以添加更多功能并根据我们的要求制作服务器。
完整的源代码:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class SimpleUdpSrvr {
public static void Main() {
int recv;
byte[] data = new byte[1024];
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050);
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
newsock.Bind(ipep);
Console.WriteLine("Waiting for a client...");
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint Remote = (EndPoint)(sender);
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine("Message received from {0}:", Remote.ToString());
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
string welcome = "Welcome to my test server";
data = Encoding.ASCII.GetBytes(welcome);
newsock.SendTo(data, data.Length, SocketFlags.None, Remote);
while (true) {
data = new byte[1024];
recv = newsock.ReceiveFrom(data, ref Remote);
Console.WriteLine(Encoding.ASCII.GetString(data, 0, recv));
newsock.SendTo(data, recv, SocketFlags.None, Remote);
}
}
}
相关文章
C# 中的 LINQ 分组
发布时间:2024/01/20 浏览次数:51 分类:编程语言
-
LINQ 中的 group by 用于按 C# 中的某个公共值对对象序列进行分组 C# 中的 LINQ 分组 LINQ 将类似 SQL 的查询功能与 C# 中的数据结构集成在一起。
使用 C# 在 LINQ 查询中按多列分组
发布时间:2024/01/20 浏览次数:167 分类:编程语言
-
这是一篇关于 LINQ 查询的使用以及我们如何使用 LINQ 查询按列分组的文章。本文简要介绍了使用 C# 进行的 LINQ 查询。此外,它还讨论了如何使用 LINQ 查询按多列对结果进行分组。
在 C# 中捕获多个异常
发布时间:2024/01/20 浏览次数:135 分类:编程语言
-
有两种主要方法可用于捕获 C# 中的多个异常,即 Exception 类和 catch 子句中的 if 语句。使用 C# 中的 Exception 类捕获多个异常 Exception 类用于表示 C# 中的一般异常。
C# 中为无效参数或参数引发的异常类型
发布时间:2024/01/20 浏览次数:71 分类:编程语言
-
本教程将教你如何在 C# 中为无效参数或参数抛出不同类型的异常。异常提供有关 C# 程序中的运行时错误或预期不会发生或违反系统/应用程序约束的条件的信息。在本教程中,你将学习与无效参
C# 中的树形数据结构
发布时间:2024/01/20 浏览次数:73 分类:编程语言
-
C# 中的树是本文讨论的主题。一种以树的形式组织起来的层次数据称为树数据结构。C# 中的树将是本文讨论的主题。数据结构是我们需要知道的第一件事。
C# 中的 lock 语句
发布时间:2024/01/20 浏览次数:166 分类:编程语言
-
C# 中 lock 语句指定的代码部分不能被多个线程同时访问。C# 中的 lock 语句 lock(obj) 语句规定,在 C# 中,下面这段代码不能被多个线程同时访问。