我有一个测试应用程序(Windows窗体)和一个不同的类(实际上是它自己的.dll,但这是无关紧要的)我有我的套接字代码的所有服务器/客户端代码.
问题1)
在我的测试应用程序中,在服务器部分,用户可以单击“开始监听”按钮,我的套接字应用程序的服务器部分应该开始监听指定地址和端口上的连接,到目前为止一切顺利.
但是,应用程序将被阻止,除非有人连接到服务器,否则我无法执行任何操作.如果没有人连接怎么办?我该怎么处理?我可以指定接收超时但是呢?抛出异常,我该怎么办?我想要的是在主应用程序上进行某种活动,以便用户知道应用程序没有冻结并等待连接.但是如果没有连接,它应该超时并关闭所有内容.
也许我应该使用异步调用来发送/接收方法,但它们似乎令人困惑,我无法使它工作,只有同步工作(我将在下面发布我当前的代码).
问题2)
当某些发送/接收呼叫超时时,是否需要关闭任何内容.正如你在我当前的代码中看到的那样,我在套接字上有一堆关闭,但这种感觉不太合适.但是当操作超时且我没有关闭套接字时,它也感觉不对.
在我的两个问题的结论….我想一个不阻止的应用程序,所以用户知道服务器正在等待连接(例如有一个小的选框动画).如果在一段时间后从未建立连接,我想关闭应该关闭的所有内容.建立连接或在一段时间后没有发生连接时,我想告知结果的主要应用.
这是我的一些代码,其余的类似. Packet类是一个自定义类,代表我的自定义数据单元,它只是现在基于枚举的一堆属性,有方法将它们转换为字节并返回属性.
开始侦听连接的函数是这样的:
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.只要捕获到这样的异常,就会关闭套接字连接.我只是谈论连接握手,仅此而已.如果没有捕获异常,代码可能会顺利进行并建立连接.
请相应调整您的答案.现在,对我来说,将任何一个标记为已接受的答案是没有意义的,希望你理解.
解决方法
首先,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,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上的事务的协议示例.