如何安全地将数据通过服务器套接字传输到另一个套接字?
我正在为正在设计的 iPhone 应用程序编写服务器应用程序。 iPhone应用程序是用C#(MonoTouch)编写的,服务器也是用C#(.NET 4.0)编写的,
我在网络层使用异步套接字。服务器允许两个或多个 iPhone(“设备”)相互连接并能够双向发送数据。 根据传入消息,服务器要么自行处理消息,要么将数据中继到与发送设备位于同一组中的其他设备。它可以通过首先解码数据包的标头并确定数据包的类型来做出此决定。
这是通过以前 8 个字节是两个整数、标头长度和有效负载长度(可以比标头大得多)的方式对流进行构建来完成的。
服务器从套接字读取(异步)前 8 个字节,因此它具有两个部分的长度。然后它再次读取,直到标题部分的总长度。
然后,它反序列化标头,并根据其中的信息,可以查看剩余数据(有效负载)是否应该转发到另一个设备,或者是服务器本身需要处理的内容。 如果需要将其转发到另一个设备上,那么下一步是以 1024 字节为单位读取进入套接字的数据,并通过连接到接收设备的另一个套接字使用异步发送直接写入这些数据。
这减少了服务器的内存需求,因为我不会将整个数据包加载到缓冲区中,然后将其重新发送给收件人。
但是,由于异步套接字的性质,我不能保证在一次读取中接收到整个有效负载,因此必须继续读取,直到收到所有字节。在中继到其最终目的地的情况下,这意味着我要为从发送方收到的每个字节块调用 BeginSend() ,并将该块转发到接收方,一次一个块。
问题在于,因为我使用的是异步套接字,所以这使得另一个线程有可能对同一接收者(因此最终目标套接字)执行类似的操作,因此来自两个线程的块可能会混淆并损坏发送给该收件人的所有数据。 例如:如果第一个线程发送了一个数据块,并且正在等待来自发送者的下一个数据块(因此它可以向前中继它),则第二个线程可能会发送其中一个数据块,并破坏第一个线程的数据块(以及第二个线程就此事而言)数据。
当我写这篇文章时,我只是想知道它是否像锁定套接字对象一样简单?这是正确的选项,还是可能导致其他问题(例如:通过从远程设备发回的锁定套接字接收数据时出现问题?)
提前致谢!
I'm writing a server application for an iPhone application im designing. iPhone app is written in C# (MonoTouch) and the server is written in C# too (.NET 4.0)
I'm using asynchronous sockets for the network layer. The server allows two or more iPhones ("devices") to connect to each other and be able to send data bi-directionally.
Depending on the incoming message, the server either processes the message itself , or relays the data through to the other device(s) in the same group as the sending device. It can make this decision by decoding the header of the packet first, and deciding what type of packet it is.
This is done by framing the stream in a way that the first 8 bytes are two integers, the length of the header and the length of the payload (which can be much larger than the header).
The server reads (asynchronously) from the socket the first 8 bytes so it has the lengths of the two sections. It then reads again, up to the total length of the header section.
It then deserializes the header, and based on the information within, can see if the remaining data (payload) should be forwarded onto another device, or is something that the server itself needs to work with.
If it needs to be forwarded onto another device, then the next step is to read data coming into the socket in chunks of say, 1024 bytes, and write these directly using an async send via another socket that is connected to the recipient device.
This reduces the memory requirements of the server, as i'm not loading in the entire packet into a buffer, then re-sending it down the wire to the recipient.
However, because of the nature of async sockets, I am not guaranteed to receive the entire payload in one read, so have to keep reading until I receive all the bytes. In the case of relaying onto its final destination, this means that i'm calling BeginSend() for each chunk of bytes I receive from the sender, and forwarding that chunk onto the recipient, one chunk at a time.
The issue with this is that because I am using async sockets, this leaves the possibility of another thread doing a similar operation with the same recipient (and therefore same final destination socket), and so it is likely that the chunks coming from both threads will get mixed up and corrupt all the data going to that recipient.
For example: If the first thread sends a chunk, and is waiting for the next chunk from the sender (so it can relay it onwards), the second thread could send one of its chunks of data, and corrupt the first thread's (and the second thread's for that matter) data.
As I write this, i'm just wondering is it as simple as just locking the socket object?! Would this be the correct option, or could this cause other issues (e.g.: issues with receiving data through the locked socket that's being sent BACK from the remote device?)
Thanks in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不久前我遇到了类似的情况,我不再有完整的解决方案,但这几乎是我所做的:
希望这有助于少量
I was facing a similar scenario a while back, I don't have the complete solution anymore, but here's pretty much what I did :
Hope that helps a bit
不确定问题出在哪里。既然你提到了服务器,我假设是 TCP,是吗?
一部电话需要将您的一些 PDU 传送到另一部电话。它作为客户端连接到另一部手机上的服务器。建立了套接字对。它将数据发送到服务器套接字。套接字对是唯一的 - 两部手机之间可能发生的其他流不应中断此操作(当然会减慢速度)。
我不明白异步/同步套接字(假设正确实现)应该如何影响这一点,或者应该工作正常。
这里有什么我看不到的吗?
顺便说一句,Maciek 计划通过添加“AA”起始字节来增强协议是一个好主意 - 协议依赖于仅发送一个长度,因为第一个元素似乎最终总是会搞砸并导致节点试图将更多字节出队宇宙中有原子。
平均值,
马丁
好的,现在我明白了这个问题,(我完全误解了 OP 网络的拓扑 - 我以为每部手机都运行 TCP 服务器以及客户端,但 PC 上只有一台服务器/无论什么 -聊天室)。我不明白为什么你不能用互斥锁锁定套接字类,因此序列化消息。您可以将消息排队到套接字,但这会产生您试图避免的内存影响。
您可以专用一个连接来仅向电话提供指令,例如“打开另一个给我的套接字连接并返回此 GUID - 然后消息将在套接字上流式传输”。这会占用一个套接字对来进行控制,并将服务器的容量减半:(
您是否坚持使用您所描述的协议,或者您可以将消息分成块,每个块中都有一些 ID 吗?然后您可以多路复用 另一种选择,同样需要对消息进行
分块,是引入一个“控制消息”(可能是一个以 55 开头而不是 AA 的块),其中包含消息 ID(GUID?),电话用于建立与服务器的第二个套接字连接,传递 ID,然后在新的套接字连接上发送第二条消息。
另一种说服电话(感到无聊了吗?)的方法是说服手机认识到可能正在等待新消息。关闭电话正在接收消息的服务器套接字,然后电话可以再次连接,告诉服务器它只收到了 xxxx 字节的消息 ID yyyy,然后服务器可以回复一条指令,以打开另一个套接字以接收新消息。消息 zzzz 然后继续发送消息 yyyy。这可能需要在服务器上进行一些缓冲,以确保在“中断”期间不会丢失数据。无论如何,您可能想要实现这种“中断后重新启动流”功能,因为手机往往会在桥/隧道下,就像正在流式传输 360MB 视频文件的最后 KB :( 我知道 TCP 应该处理丢失的数据包,但是如果电话无线层出于某种原因决定关闭套接字......
这些解决方案都不是特别令人满意。有兴趣看看其他想法的出现......
Rgds,
马丁
Not sure where the problem is. Since you mentioned servers, I assume TCP, yes?
A phone needs to communicate some of your PDU to another phone. It connects as a client to the server on the other phone. A socket-pair is established. It sends the data off to the server socket. The socket-pair is unique - no other streams that might be happening between the two phones should interrupt this, (will slow it up, of course).
I don't see how async/sync sockets, assuming implemented correctly, should affect this, either should work OK.
Is there something I cannot see here?
BTW, Maciek's plan to bolster up the protocol by adding an 'AA' start byte is an excellent idea - protocols depending on sending just a length as the first element always seem to screw up eventually and result in a node trying to dequeue more bytes that there are atoms in the universe.
Rgds,
Martin
OK, now I understand the problem, (I completely misunderstood the topology of the OP network - I thought each phone was running a TCP server as well as client/s, but there is just one server on PC/whatever a-la-chatrooms). I don't see why you could not lock the socket class with a mutex, so serializing the messages. You could queue the messages to the socket, but this has the memory implications that you are trying to avoid.
You could dedicate a connection to supplying only instructions to the phone, eg 'open another socket connection to me and return this GUID - a message will then be streamed on the socket'. This uses up a socket-pair just for control and halves the capacity of your server :(
Are you stuck with the protocol you have described, or can you break your messages up into chunks with some ID in each chunk? You could then multiplex the messages onto one socket pair.
Another alternative, that again would require chunking the messages, is introduce a 'control message', (maybee a chunk with 55 at start instead of AA), that contains a message ID, (GUID?), that the phone uses to establish a second socket connection to the server, passes up the ID and is then sent the second message on the new socket connection.
Another, (getting bored yet?), way of persuading the phone to recognise that a new message might be waiting would be to close the server socket that the phone is receiving a message over. The phone could then connect up again, tell the server that it only got xxxx bytes of message ID yyyy. The server could then reply with an instruction to open another socket for new message zzzz and then resume sending message yyyy. This might require some buffering on the server to ensure no data gets lost during the 'break'. You might want to implement this kind of 'restart streaming after break' functionality anyway since phones tend to go under bridges/tunnels just as the last KB of a 360MB video file is being streamed :( I know that TCP should take care of dropped packets, but if the phone wireless layer decides to close the socket for whatever reason...
None of these solutions is particularly satisfying. Interested to see whay other ideas crop up..
Rgds,
Martin
感谢大家的帮助,我意识到最简单的方法是在客户端上使用同步发送命令,或者至少是一个必须在发送下一个项目之前完成的发送命令。我在客户端上使用自己的发送队列来处理此问题,而不是应用程序的各个部分在需要发送内容时仅调用 send() 。
Thanks for the help everyone, i've realised the simpliest approach is to use synchronous send commands on the client, or at least a send command that must complete before the next item is sent. Im handling this with my own send queue on the client, rather than various parts of the app just calling send() when they need to send something.