简体   繁体   English

如何在Go中通过套接字检索文件数据?

[英]How do I retrieve file data over a socket in Go?

I've got two small programs communicating nicely over a socket where the receiving side is in Go. 我有两个小程序可以在接收端位于Go中的套接字上很好地通信。 Everything works peachy when my messages are tiny enough to fit in the 1024 byte buffer and can be received in a single Read from the connection but now I want to transfer data from an image that is 100k+ or more. 当我的消息很小到可以容纳1024字节的缓冲区并且可以从连接中读取一次时,一切都会变好,但是现在我想从100k以上的图像中传输数据。 I'm assuming the correct solution is not to increase the buffer until any image can fit inside. 我假设正确的解决方案是在任何图像都适合内部之前不要增加缓冲区。

Pseudo-go: 伪go:

var buf = make([]byte,1024)
conn, err := net.Dial("tcp", ":1234")

for {
    r, err := conn.Read(buf[0:])
    go readHandler(string(buf[0:r]),conn)
}

How can I improve my socket read routine to accept both simple messages of a few bytes and also larger data? 如何改善套接字读取例程,以接受几个字节的简单消息以及更大的数据? Bonus points if you can turn the total image data into an io.Reader for use in image.Decode. 如果您可以将全部图像数据转换为io.Reader以便在image.Decode中使用,则可以加分。

I have no direct experience with TCP in Go but to me it seems that you fell victim of a quite typical misunderstanding of what guarntees TCP offers. 我没有在Go中使用TCP的直接经验,但是对我来说,您似乎是TCP担保人的典型误解的受害者。

The thing is, in contrast with, say, UDP and SCTP , TCP does not have the concept of message boundaries because it's stream-oriented. 与UDP和SCTP相反,TCP没有消息边界的概念,因为它是面向流的。 It means, TCP transports opaque streams of bytes and you have very little control of "chunking" that stream with regard to the receiving side. 这意味着,TCP传输不透明的字节流,并且就接收方而言,您几乎无法控制该“流”。

I suspect what you observe as "sending a 100k+ message" is the runtime/network library on the sender side typically "deceiving" you by consuming your "message" into its internal buffers and then streaming it in whatever chunks OS's TCP stack allows it to (on ubiquitous hardware/software it's usually about 8k). 我怀疑您观察到的“发送100k +消息”是发送方的运行时/网络库,通常通过将“消息”消耗到其内部缓冲区中,然后将其流式传输到任何块中来“欺骗”您OS的TCP堆栈允许它(在无处不在的硬件/软件上,通常约为8k)。 The size of pieces the receiver gets that stream is completely undefined; 接收器获取该流的片段大小是完全不确定的; the only thing defined is ordering of the bytes in the stream, which is preserved. 唯一定义的是流中字节的顺序,该顺序被保留。

Hence it might turn out you have to resonsider your approach to receiving data. 因此,结果可能是您不得不重新考虑接收数据的方法。 The exact approach varies depending on the nature of the data being streamed: 确切的方法取决于流数据的性质:

  • The easiest way (if you have the control over the application-level protocol) is to pass the length of the following "message payload" in a special length field of fixed format. 最简单的方法(如果您可以控制应用程序级协议)是在固定格式的特殊长度字段中传递以下“消息有效负载”的长度。 Then destreaming the whole message is a two-step process: 1) receive that many bytes to get the length field, read it, check the value for sanity, then 2) read that many following bytes and be done with it. 然后,将整个消息降级为两个步骤:1)接收那么多字节以获取length字段,对其进行读取,检查其值是否合理,然后2)读取随后的许多字节并对其进行处理。
  • If you have no control over the app-level protocol, parsing messages becomes more involved and usually requires some sort of complicated state machine. 如果您无法控制应用程序级协议,则解析消息将变得更加复杂,并且通常需要某种复杂的状态机。

For more info, look at this and this . 有关更多信息,请参见thisthis

You can use io.ReadFull to read a []byte of a specific length. 您可以使用io.ReadFull读取特定长度的[]byte This assumes that you know beforehand how many bytes you need to read. 假设您事先知道需要读取多少个字节。

As for image.Decode , it should be possible to pass the conn directly to the image.Decode function. 至于image.Decode ,应该可以将conn直接传递给image.Decode函数。 This assumes that you do not perform any reads from the connection until the image is decoded. 假定您不对连接进行任何读取,直到对图像进行解码为止。

Your code 您的密码

for {
    r, err := conn.Read(buf[0:])
    go readHandler(string(buf[0:r]),conn)
}

seems to be suggesting that the goroutine you are starting is reading from conn This doesn't seem like a good idea, because you will end up having multiple concurrent reads from the connection (without having control over the order in which the reads will happen): one in the for-loop, another one in readHandler . 似乎暗示您正在启动的goroutine正在从conn读取数据,这似乎不是一个好主意,因为您最终将从连接中进行多个并发读取(无法控制读取发生的顺序) :一个在for循环中,另一个在readHandler

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM