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

我刚开始通过各种谷歌搜索来学习套接字,但是我在解决如何在C#中正确使用套接字时遇到了一些问题,而我需要一些帮助。

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

问题1)

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

但是,应用程序将被阻止,除非有人连接到服务器,否则我无法执行任何操作。 如果没有人连接怎么办? 我该怎么处理? 我可以指定接收超时但是呢? 抛出exception,我该怎么办? 我想要的是在主应用程序上进行某种活动,以便用户知道应用程序没有冻结并等待连接。 但是如果没有连接,它应该超时并关闭所有内容。

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

问题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并在应用层自己实现这些。 您可以将此视为家庭作业,但我没有这样标记,因为代码是无关紧要的,不会成为我的成绩的一部分而我的问题(我的问题)是“如何编码”,因为我的套接字体验很少而且不是授课。

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

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

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

你的东西有点不对劲。

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

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

下一个重要问题是关于阻止。 由于您是新手,我建议您使用Threads来处理套接字。 将侦听器套接字放在一个线程中,并将每个连接套接字放在一个单独的线程中(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上的事务的协议示例。

根据您的描述,我觉得您应该使用TCP套接字而不是UDP。 不同的是

TCP – 您等待特定IP上的连接:某些用户可以连接到它的端口,直到套接字关闭可以通过发送和接收信息进行通信。 这就像打电话给某人。

UDP – 您在某个IP上等待消息:端口。 想要通信的用户只是通过UDP发送消息。 您将通过UDP接收消息。 交货顺序无法保证。 这更像是向某人发送蜗牛邮件。 没有建立专用的通信渠道。

现在来解决你的问题

服务器

  1. 使用TCP系列创建一个Socket。
  2. 创建一个线程并接受该线程中的连接或使用Socket的BeginAccept apis。
  3. 在主线程中,您仍然可以显示自动收报机或您想要做的任何事情。

客户

  1. 连接到服务器。
  2. 通过发送和接收数据进行通信。