对 C# 中带有 UDP 协议的套接字感到困惑

Confused about Sockets with UDP Protocol in C#(对 C# 中带有 UDP 协议的套接字感到困惑)
本文介绍了对 C# 中带有 UDP 协议的套接字感到困惑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've just started learning Sockets through various Google searches but I'm having some problems figuring it out how to properly use Sockets in C# and I'm in the need of some help.

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

I have a test application (Windows Forms) and on a different class (which is actually in it's own .dll, but that's irrelevant) I have all the server/client code for my sockets code.

问题 1)

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

On my test application, on the server part, the user can click the "start listening" button and the server part of my sockets application should start listening for connections on the specified address and port, so far so good.

但是,应用程序将被阻止,并且在有人连接到服务器之前我无法执行任何操作.如果没有人连接怎么办?我应该如何处理?我可以指定接收超时,但那又如何呢?它抛出一个异常,我该怎么办?我想要的是在主应用程序上进行某种活动,以便用户知道应用程序没有冻结并且正在等待连接.但是如果没有连接,它应该超时并关闭一切.

However, the application will be blocked and I can't do anything until someone connects to the server. What if no one connects? How should I handle that? I could specify a receive timeout but what then? It throws an exception, what can I do with that? What I would like is to have some sort of activity on the main application so the user knows the application didn't froze and is waiting for connections. But if a connection doesn't come, it should timeout and close everything.

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

Maybe I should use asynchronous calls to send/receive methods but they seem confusing and I was not able to make it work, only synchronous work (I'll post my current code below).

问题 2)

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

Do I need to close anything when some send/receive call times out. As you'll see on my current code, I have a bunch of closes on the socket, but this doesn't feel right somehow. But it also doesn't feel right when an operation times out and I don't close the socket.

结束我的两个问题....我想要一个不会阻塞的应用程序,这样用户就知道服务器正在等待连接(例如,带有一点选框动画).如果一段时间后从未建立连接,我想关闭应该关闭的所有内容.当连接建立或一段时间后没有发生时,我想将结果通知主应用程序.

In conclusion of my two questions.... I would like an application that doesn't block so the user knows the server is waiting for a connection (with a little marquee animation for instance). If a connection is never established after a period of time, I want to close everything that should be closed. When a connection is established or if it doesn't happen after a period of time, I would like to inform the main application of the result.

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

Here's some of my code, the rest is similar. The Packet class is a custom class that represents my custom data unit, it's just a bunch of properties based on enums for now, with methods to convert them to bytes and back into properties.

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

The function that starts to listen for connections is something like this:

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();
        }
    }
}

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

I'm sure that I have a bunch of unnecessary code but I'm new at this and I'm not sure what do, any help fixing up my code and how to solve the issues above is really appreciated.

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

I should probably have stated that my requirements are to use UDP and implement these things myself in the application layer. You can consider this as homework but I haven't tagged as such because the code is irrelevant and will not be part of my grade and my problem (my question) is in "how to code" as my Sockets experience is minimal and it's not taught.

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

However I must say that I solved my problem for now I think... I was using threading on the demo application which was giving me some problems, now I'm using it in the protocol connections, makes more sense and I can easily change my custom protocol class properties and read those from the demo application.

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

I have specified a timeout and throw a SocketException if it reaches the timeout. Whenever an exception like this is caught, the socket connection is closed. I'm just talking about the connection handshake, nothing more. If no exceptions are caught, the code probably went smooth and the connection is established.

请相应地调整您的答案.目前,我将其中任何一个标记为已接受的答案是没有意义的,希望您能理解.

Please adapt your answers accordingly. Right now it doesn't make sense for me to marky any of them as the accepted answer, hope you understand.

推荐答案

你有点搞错了.

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

First of all, UDP is connection-less. You do not connect or disconnect. All you do is to send and receive (must specify destination each time). You should also know that the only thing UDP promises is that a complete message arrives on each read. UDP do not guarantee that your messages arrive in the correct order or that they arrive at all.

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

TCP on the other hand is connection-based. You connect, send/receive and finally disconnect. TCP is stream-based (while UDP is message-based) which means that you can get a half message in the first read and the other half at the second read. TCP promises you that everything will arrive and in the correct order (or will die trying ;). So using TCP means that you should have some kind of logic to know when a complete message has arrived and a buffer that you use to build the complete message.

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

The next big question was about blocking. Since you are new at this, I recommend that you use Threads to handle sockets. Put the listener socket in one thread and each connecting socket in a separate thread (5 connected clients = 5 threads).

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

I also recommend that you use TCP since it's easier to build complete messages than ordering messages and build a transaction system (which will needed if you want to make sure that all messages arrives to/from clients).

你还是把 UDP 弄错了.除了清理系统资源之外,关闭不会做任何事情.你应该这样做:

You still got UDP wrong. Close doesn't do anything other than cleaning up system resources. You should do something like this instead:

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 的用途.它告诉哪个客户端发送消息.

See? since there are no connection it's just waste of time to close a UDP socket to start listening from another one. The same socket can receive from ALL udp clients that know the correct port and address. That's what the remoteEndPoint is for. It tells which client that send the message.

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

Small update to make a summary of all my comments.

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

UDP is connectionless. You can never detect if a connection have been established or disconnected. The Close method on a UDP socket will only free system resources. A call on client.Close() will not notify the server socket (as it will with TCP).

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

The best way to check if a connection is open is to create a ping/pong style of packet. i.e. the client sends a PING message and the server responds with a PONG. Remember that UDP will not try to resend your messages if they do not arrive. Therefore you need to resend the PING a couple of times before assuming that the server is down (if you do not receive a PONG).

对于关闭的客户端,您需要向服务器发送自己的消息,告诉它客户端将停止与服务器通信.为了可靠性,这里也是一样,请继续重新发送 BYE 消息,直到收到回复.

As for clients closing you need to send your own message to the server telling it that the the client is going to stop talking to the server. For reliability the same thing goes here, keep resending the BYE message until you receive a reply.

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

imho it's mandatory that you implement a transactional system for UDP if you want reliability. SIP (google rfc3261) is an example of a protocol which uses transactions over UDP.

这篇关于对 C# 中带有 UDP 协议的套接字感到困惑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

DispatcherQueue null when trying to update Ui property in ViewModel(尝试更新ViewModel中的Ui属性时DispatcherQueue为空)
Drawing over all windows on multiple monitors(在多个监视器上绘制所有窗口)
Programmatically show the desktop(以编程方式显示桌面)
c# Generic Setlt;Tgt; implementation to access objects by type(按类型访问对象的C#泛型集实现)
InvalidOperationException When using Context Injection in ASP.Net Core(在ASP.NET核心中使用上下文注入时发生InvalidOperationException)
LINQ many-to-many relationship, how to write a correct WHERE clause?(LINQ多对多关系,如何写一个正确的WHERE子句?)