如何在golang中通知TCP客户端文件结束?

5
我想在golang中通过TCP发送文件。这是我的服务器代码:
    c is connected *net.TCPConn

    file, _ := os.Open(fn)
    defer file.Close()
    io.Copy(c, file)
    // c.CloseWrite()

以及客户端:

    as above, c is connected *net.TCPConn

    file, _ := os.Create("file.txt")
    defer file.Close()
    io.Copy(file, c)

我的问题是:按照这种方式,客户端无法接收文件的EOF信号。因此,io.Copy会阻塞。我必须调用c.CloseWrite来通知客户端文件已经结束。
如果我想发送文件,这种方法就不起作用了,我该怎么解决?
3个回答

6
如果您正在使用TCP连接,那么os.EOF错误意味着连接已被另一端关闭。
我认为可靠地发送文件的唯一方法是实现多状态协议。
例如,在传输的第一个状态中,告诉客户端要读取多少字节并进入第二个状态。在第二个状态中,如果已经读取了所有字节,则说明它已经读取了整个文件。如果在读取所有字节之前检测到os.EOF,则丢弃并重新开始。

2
所以我必须使用c.Read,io.Copy在这里无用? - dilfish
@dilfish,Allent 想问你的是:你是不是想把一个文件发送给客户端,但在此之后不终止 TCP 连接? 如果是的话,那么这是行不通的,因为 TCP 只传输不透明的字节流,解释它是客户端的任务。所以你需要在 TCP 之上设计一个“应用层协议”。正如 Allen 建议的那样,服务器可能首先指示将要发送多少字节,然后客户端读取那么多字节,将它们写入文件并等待其他“传输指示器”或其他内容。 - kostix
谢谢,我的问题是,io.Copy对这种情况不合适吗?看起来似乎不是。 - dilfish
正确,当处理TCP连接时,您需要使用net包中的函数。我认为即使在关闭连接后也需要某种应用层协议,以防连接过早关闭。 - Allen Hamilton

2
在普通的C语言中,我们可以使用shutdown(fd, SHUT_WR)函数来向另一端发送EOF(文件结束)信号关闭TCP连接。在Go语言中同样可以这样做:
func shutdownWrite(conn net.Conn) {
     // anonymous interface. Could explicitly use TCP instead.
     if  v, ok := conn.(interface{ CloseWrite() error }); ok {
         v.CloseWrite()
     }
 }

请查看https://golang.org/src/net/tcpsock_posix.go?s=2073:2109#L75


0

如果从发送方关闭连接,一切都能正常工作。
现在我也做了同样的事情 - 通过TCP传输文件。如果添加

,一切都能正常工作。
defer conn.Close()

打开连接后。
例如:

conn, err := net.Dial("tcp", client)
defer conn.Close()

问题是“传输多个文件”。上面的问题代码仅适用于一个文件,但第二个文件无法工作,因为客户端的“io.copy()”被阻止了。 - kkkkkk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接