gpt4 book ai didi

c# - 对 C# 中使用 UDP 协议(protocol)的套接字感到困惑

转载 作者:太空狗 更新时间:2023-10-29 21:14:39 25 4
gpt4 key购买 nike

我刚刚开始通过各种 Google 搜索学习套接字,但我在弄清楚如何在 C# 中正确使用套接字时遇到了一些问题,我需要一些帮助。

我有一个测试应用程序(Windows 窗体)和一个不同的类(实际上在它自己的 .dll 中,但这无关紧要)我有我的套接字代码的所有服务器/客户端代码。

问题 1)

在我的测试应用程序中,在服务器部分,用户可以单击“开始监听”按钮,我的套接字应用程序的服务器部分应该开始监听指定地址和端口上的连接,到目前为止一切顺利。

但是,应用程序将被阻止,在有人连接到服务器之前我无法做任何事情。如果没有人连接怎么办?我该如何处理?我可以指定一个接收超时,但那又如何呢?它抛出一个异常,我能用它做什么?我想要的是在主应用程序上进行某种事件,以便用户知道应用程序没有卡住并且正在等待连接。但是,如果没有建立连接,它应该会超时并关闭所有内容。

也许我应该使用异步调用来发送/接收方法,但它们看起来很困惑,我无法让它工作,只能同步工作(我将在下面发布我当前的代码)。

问题 2)

当某些发送/接收调用超时时,我是否需要关闭任何东西。正如您将在我当前的代码中看到的那样,我在套接字上有一堆关闭,但不知何故这感觉不对。但当操作超时并且我没有关闭套接字时,感觉也不对。

作为我的两个问题的结论……我想要一个不会阻塞的应用程序,这样用户就知道服务器正在等待连接(例如带有一个小选取框动画)。如果一段时间后还没有建立连接,我想关闭所有应该关闭的东西。当建立连接或一段时间后没有建立连接时,我想将结果通知主应用程序。

这是我的一些代码,其余类似。 Packet 类是一个自定义类,表示我的自定义数据单元,目前它只是一堆基于 enums 的属性,具有将它们转换为字节并返回到字节的方法属性。

开始监听连接的函数是这样的:

public void StartListening(string address, int port) {
try {
byte[] bufferBytes = new byte[32];

if(address.Equals("0.0.0.0")) {
udpSocket.Bind(new IPEndPoint(IPAddress.Any, port));
} else {
udpSocket.Bind(new IPEndPoint(IPAddress.Parse(address), port));
}

remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);

int numBytesReceived = udpSocket.ReceiveFrom(bufferBytes, ref remoteEndPoint);

if(numBytesReceived == 0) {
udpSocket.Close();
return;
}

Packet syncPacket = new Packet(bufferBytes);

if(syncPacket.PacketType != PacketType.Control) {
udpSocket.Close();
return;
}
} catch {
if(udpSocket != null) {
udpSocket.Close();
}
}
}

我确信我有一堆不必要的代码,但我是新手,我不确定该怎么做,非常感谢任何修复我的代码以及如何解决上述问题的帮助。

编辑:

我可能应该说明我的要求是使用 UDP 并在应用层自己实现这些东西。您可以将其视为家庭作业,但我没有这样标记,因为代码无关紧要,不会成为我成绩的一部分,而且我的问题(我的问题)是“如何编码”,因为我的 Sockets 经验很少而且不是受教了。

但是我必须说我现在解决了我的问题我想...我在演示应用程序上使用线程,这给我带来了一些问题,现在我在协议(protocol)连接中使用它,更有意义而且我可以轻松更改我的自定义协议(protocol)类属性并从演示应用程序中读取这些属性。

我已经指定了一个超时并在达到超时时抛出一个 SocketException。每当捕获到这样的异常时,套接字连接就会关闭。我只是在谈论连接握手,仅此而已。如果没有捕获到异常,则代码可能运行顺利并建立了连接。

请相应地调整您的答案。现在将其中任何一个标记为已接受的答案对我来说都没有意义,希望你能理解。

最佳答案

你搞错了。

首先,UDP是无连接的。您不连接或断开连接。您所做的就是发送和接收(每次都必须指定目的地)。您还应该知道,UDP 唯一 promise 的是每次读取都会收到完整的消息。 UDP 不保证您的消息以正确的顺序到达或完全到达。

另一方面,TCP 是基于连接的。您连接、发送/接收并最终断开连接。 TCP 是基于流的(而 UDP 是基于消息的),这意味着您可以在第一次读取时获得一半消息,在第二次读取时获得另一半消息。 TCP 向您保证一切都会以正确的顺序到达(或者会死于尝试;)。因此,使用 TCP 意味着您应该有某种逻辑来了解完整消息何时到达以及用于构建完整消息的缓冲区。

下一个大问题是关于阻塞。由于您是新手,我建议您使用线程来处理套接字。将监听器套接字放在一个线程中,将每个连接套接字放在一个单独的线程中(5 个连接的客户端 = 5 个线程)。

我还建议您使用 TCP,因为构建完整的消息比排序消息和构建交易系统(如果您想确保所有消息到达/来自客户端则需要)更容易。

更新

你还是弄错了UDP。 Close 除了清理系统资源外不做任何事情。你应该做这样的事情:

public void MySimpleServer(string address, int port) 
{
try
{
byte[] bufferBytes = new byte[32];

if(address.Equals("0.0.0.0")) {
udpSocket.Bind(new IPEndPoint(IPAddress.Any, port));
} else {
udpSocket.Bind(new IPEndPoint(IPAddress.Parse(address), port));
}

remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
while (serverCanRun)
{
int numBytesReceived = udpSocket.ReceiveFrom(bufferBytes, ref remoteEndPoint);

// just means that one of the clients closed the connection using Shutdown.
// doesnt mean that we cant continue to receive.
if(numBytesReceived == 0)
continue;

// same here, loop to receive from another client.
Packet syncPacket = new Packet(bufferBytes);
if (syncPacket.PacketType != PacketType.Control)
continue;

HandlePacket(packet, endPoint);
}
} catch {
if(udpSocket != null) {
udpSocket.Close();
}
}
}

看到了吗?因为没有连接,所以关闭一个 UDP 套接字以开始从另一个套接字开始监听只是浪费时间。同一个套接字可以从所有知道正确端口和地址的 udp 客户端接收。这就是 remoteEndPoint 的用途。它告诉发送消息的客户端。

更新2

小更新以总结我的所有评论。

UDP 是无连接的。您永远无法检测到连接是建立还是断开。 UDP 套接字上的 Close 方法只会释放系统资源。对 client.Close() 的调用不会通知服务器套接字(与 TCP 一样)。

检查连接是否打开的最佳方法是创建 ping/pong 样式的数据包。即客户端发送 PING 消息,服务器以 PONG 响应。请记住,如果消息未到达,UDP 将不会尝试重新发送消息。因此,在假设服务器已关闭(如果您没有收到 PONG)之前,您需要重新发送几次 PING。

至于客户端关闭,您需要向服务器发送您自己的消息,告诉它客户端将停止与服务器对话。为了可靠性,同样的事情发生在这里,继续重新发送 BYE 消息,直到您收到回复。

恕我直言,如果您想要可靠性,则必须为 UDP 实现事务系统。 SIP (google rfc3261) 是使用 UDP 交易的协议(protocol)示例。

关于c# - 对 C# 中使用 UDP 协议(protocol)的套接字感到困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5415171/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com