我正在编写一个用于设计的iPhone应用程序的服务器应用程序。 iPhone应用程序是用C#(MonoTouch)编写的,服务器也是用C#编写的(.NET 4.0)
我正在为网络层使用异步套接字。服务器允许两个或更多iPhone(“设备”)彼此连接并能够双向发送数据。 根据传入消息,服务器要么自己处理消息,要么将数据中继到与发送设备相同的组中的其他设备。它可以通过首先解码数据包的标头,并决定它是什么类型的数据包来做出这个决定。
这是通过以前8个字节是两个整数的方式对流进行成帧,报头的长度和有效负载的长度(可以比报头大得多)来完成的。
服务器从套接字读取(异步)前8个字节,因此它具有两个部分的长度。然后再次读取,直到标题部分的总长度。
然后对报头进行反序列化,并根据其中的信息,查看是否应将剩余数据(有效负载)转发到其他设备,或者是服务器本身需要使用的内容。 如果需要转发到另一台设备,则下一步是读取以1024字节为单位进入套接字的数据,并通过另一个连接到接收设备的套接字使用异步发送直接写入这些数据。 / p>
这减少了服务器的内存需求,因为我没有将整个数据包加载到缓冲区中,然后将其重新发送到接收方。
但是,由于异步套接字的性质,我无法保证在一次读取中接收整个有效负载,因此必须继续读取,直到我收到所有字节。在转发到其最终目的地的情况下,这意味着我正在为从发送方接收的每个字节块调用BeginSend(),并将该块转发到接收方,一次一个块。
这个问题是因为我使用异步套接字,这使得另一个线程可能与同一个接收者(因此同一个最终目标套接字)进行类似的操作,因此很可能是来自两个线程都会混淆并破坏发送给该收件人的所有数据。 例如:如果第一个线程发送一个块,并且正在等待发送方的下一个块(因此它可以向前中继),则第二个线程可以发送其中一个数据块,并破坏第一个线程(和第二个线程就是这个问题)数据。
在我写这篇文章时,我只是想知道它是否只是锁定套接字对象一样简单?!这是正确的选择,还是会导致其他问题(例如:通过从远程设备发回的锁定套接字接收数据的问题?)
提前致谢!
答案 0 :(得分:2)
我前面遇到类似的情况,我不再有完整的解决方案了,但这就是我的所作所为:
希望有所帮助
答案 1 :(得分:0)
不确定问题出在哪里。既然你提到了服务器,我就假设是TCP,是吗?
手机需要将部分PDU与另一部手机通信。它作为客户端连接到另一部手机上的服务器。建立套接字对。它将数据发送到服务器套接字。套接字对是唯一的 - 两个手机之间可能没有其他流可能会中断这一点,(当然会减慢它)。
我没有看到假设正确实现的异步/同步套接字应该如何影响这一点,要么应该正常工作。
有什么我在这里看不到的吗?
BTW,Maciek计划通过添加'AA'起始字节来支持协议是一个很好的想法 - 协议取决于发送一个长度作为第一个元素总是似乎最终搞砸并导致节点试图出列宇宙中有原子的更多字节。RGDS, 马丁
好的,现在我明白了这个问题,(我完全误解了OP网络的拓扑结构 - 我认为每部手机都运行TCP服务器以及客户端,但是PC上只有一台服务器/无论如何 - LA-聊天室)。我不明白为什么你不能用互斥锁来锁定套接字类,所以序列化消息。您可以将消息排队到套接字,但这会产生您要避免的内存影响。
您可以专门提供一个连接,只向手机提供说明,例如'打开另一个套接字连接给我并返回此GUID - 然后一条消息将在套接字上流式传输'。这会占用一对套接字仅用于控制,并将服务器的容量减半:(
您是否坚持使用您所描述的协议,或者您是否可以将消息分解为每个块中包含一些ID的块?然后,您可以将消息多路复用到一个套接字对上。
另一种替代方案,即再次需要分组消息,引入一个“控制消息”,(可能是一个55开头而不是AA的块),它包含一个消息ID,(GUID?),手机使用建立与服务器的第二个套接字连接,传递ID,然后在新的套接字连接上发送第二个消息。
另一个,(感到无聊?),说服手机识别新消息可能正在等待的方式是关闭手机正在接收消息的服务器套接字。然后手机可以再次连接,告诉服务器它只有xxxx字节的消息ID yyyy。然后,服务器可以回复一条指令,为新消息zzzz打开另一个套接字,然后恢复发送消息yyyy。这可能需要在服务器上进行一些缓冲,以确保在“中断”期间没有数据丢失。您可能希望实现这种“中断后重启流”功能,因为手机往往在桥接器/隧道下进行,就像流式传输360MB视频文件的最后一个KB一样:(我知道TCP应该处理丢弃的数据包,但如果手机无线层因任何原因决定关闭套接字......
这些解决方案都不是特别令人满意。有兴趣看到其他想法突然出现..
RGDS, 马丁
答案 2 :(得分:0)
感谢大家的帮助,我意识到最简单的方法是在客户端上使用同步发送命令,或至少在发送下一个项目之前必须完成的发送命令。我在客户端使用自己的发送队列来处理这个问题,而不是在需要发送内容时调用send()的应用程序的各个部分。