如何在 tcp 客户端中停止 io.Copy(file, conn)?
How to halt io.Copy(file, conn) in tcp client?
我是 go lang 的新手,在 go 中编写 tcp 代码时遇到了问题。
这些是我的代码
// client
package main
import (
"log"
"net"
"os"
"io"
"fmt"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:4000")
if err != nil {
log.Fatal(err)
}
GetFileFromServer(conn, "test.txt")
}
func GetFileFromServer(conn net.Conn, fileName string) {
file, err := os.Create(fileName)
if err != nil {
log.Fatal(err)
return
}
defer file.Close()
conn.Write([]byte("get:" + fileName))
_, err = io.Copy(file, conn) // **IT DOES NOT STOP!!**
if err != nil {
log.Fatal(err)
return
}
fmt.Println("GET DONE")
}
// server
package main
import (
"bytes"
"log"
"net"
"io"
"fmt"
"strings"
"os"
)
const BUFFER_SIZE = 1024
const DIR_PATH = "./serverFile"
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:4000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listen.Accept()
if err != nil {
log.Fatal(err)
}
defer conn.Close()
go ConnHandle(conn)
}
}
func ConnHandle(conn net.Conn) {
buffer := make([]byte, BUFFER_SIZE)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
trimedBuffer := bytes.Trim(buffer, "\x00")
command := strings.Split(string(trimedBuffer), ":")
if command[0] == "get" {
PutFileToClient(conn, command[1])
return
}
}
func PutFileToClient(conn net.Conn, fileName string) {
file, err := os.Open(DIR_PATH + "\" + fileName)
if err != nil {
log.Fatal(err)
return
}
defer file.Close()
_, err = io.Copy(conn, file)
if err != nil {
log.Fatal(err)
return
}
fmt.Println("PUT DONE")
}
----我想要的----
- 服务器端和客户端执行完毕,
- 客户端发送到服务器“get:test.txt”
- 服务器收到它
- 服务器向客户端发送文件
- 客户端收到,并保存在文件中
- 终止客户端
我认为“客户端的io.Copy”没有停止的原因是它一直在尝试从连接中读取数据。
如何修复或停止它?
正如@BurakSerdar 提到的那样,客户端永远不会得到 io.EOF
因为服务器永远不会关闭连接流。
func PutFileToClient(conn net.Conn, fileName string) {
defer conn.Close() // ensures client gets a `io.EOF` on success *OR* failure
// ...
}
但是,您需要注意一些极端情况。
虽然可以在 server-side 上跟踪错误处理,但从客户端的角度来看,它会忽略这些错误并保存一个文件:
- 零字节(如果服务器无法打开文件);或
- 部分字节(如果服务器的
io.Copy
在中间失败)
为了解决这些情况,建议在连接负载的开头写一个 file-size header,例如
服务器
- 文件打开:
- 失败:
defer
关闭连接(客户端将获得零字节)
- 统计数据file-size
- 将文件大小写入 8 字节 header(64 位文件大小涵盖任何大小)例如
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, fsize)
n, err := io.Copy(conn, b)
- io.Copy 文件 body 和以前一样
- return 任何错误
客户
- 将 header 读入 8 字节缓冲区
- short-read(即少于 8 个字节)no/bad header - 服务器无法获取文件
- 解组 file-size header:
fsize := binary.LittleEndian.Uint64(b)
- 轨道字节复制:
n, err := io.Copy(file, conn)
- 验证
n == fsize
以确保收到完整的负载。
我是 go lang 的新手,在 go 中编写 tcp 代码时遇到了问题。
这些是我的代码
// client
package main
import (
"log"
"net"
"os"
"io"
"fmt"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:4000")
if err != nil {
log.Fatal(err)
}
GetFileFromServer(conn, "test.txt")
}
func GetFileFromServer(conn net.Conn, fileName string) {
file, err := os.Create(fileName)
if err != nil {
log.Fatal(err)
return
}
defer file.Close()
conn.Write([]byte("get:" + fileName))
_, err = io.Copy(file, conn) // **IT DOES NOT STOP!!**
if err != nil {
log.Fatal(err)
return
}
fmt.Println("GET DONE")
}
// server
package main
import (
"bytes"
"log"
"net"
"io"
"fmt"
"strings"
"os"
)
const BUFFER_SIZE = 1024
const DIR_PATH = "./serverFile"
func main() {
listen, err := net.Listen("tcp", "127.0.0.1:4000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listen.Accept()
if err != nil {
log.Fatal(err)
}
defer conn.Close()
go ConnHandle(conn)
}
}
func ConnHandle(conn net.Conn) {
buffer := make([]byte, BUFFER_SIZE)
_, err := conn.Read(buffer)
if err != nil {
log.Fatal(err)
}
trimedBuffer := bytes.Trim(buffer, "\x00")
command := strings.Split(string(trimedBuffer), ":")
if command[0] == "get" {
PutFileToClient(conn, command[1])
return
}
}
func PutFileToClient(conn net.Conn, fileName string) {
file, err := os.Open(DIR_PATH + "\" + fileName)
if err != nil {
log.Fatal(err)
return
}
defer file.Close()
_, err = io.Copy(conn, file)
if err != nil {
log.Fatal(err)
return
}
fmt.Println("PUT DONE")
}
----我想要的----
- 服务器端和客户端执行完毕,
- 客户端发送到服务器“get:test.txt”
- 服务器收到它
- 服务器向客户端发送文件
- 客户端收到,并保存在文件中
- 终止客户端
我认为“客户端的io.Copy”没有停止的原因是它一直在尝试从连接中读取数据。
如何修复或停止它?
正如@BurakSerdar 提到的那样,客户端永远不会得到 io.EOF
因为服务器永远不会关闭连接流。
func PutFileToClient(conn net.Conn, fileName string) {
defer conn.Close() // ensures client gets a `io.EOF` on success *OR* failure
// ...
}
但是,您需要注意一些极端情况。
虽然可以在 server-side 上跟踪错误处理,但从客户端的角度来看,它会忽略这些错误并保存一个文件:
- 零字节(如果服务器无法打开文件);或
- 部分字节(如果服务器的
io.Copy
在中间失败)
为了解决这些情况,建议在连接负载的开头写一个 file-size header,例如
服务器
- 文件打开:
- 失败:
defer
关闭连接(客户端将获得零字节)
- 失败:
- 统计数据file-size
- 将文件大小写入 8 字节 header(64 位文件大小涵盖任何大小)例如
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, fsize)
n, err := io.Copy(conn, b)
- io.Copy 文件 body 和以前一样
- return 任何错误
- 将文件大小写入 8 字节 header(64 位文件大小涵盖任何大小)例如
客户
- 将 header 读入 8 字节缓冲区
- short-read(即少于 8 个字节)no/bad header - 服务器无法获取文件
- 解组 file-size header:
fsize := binary.LittleEndian.Uint64(b)
- 轨道字节复制:
n, err := io.Copy(file, conn)
- 验证
n == fsize
以确保收到完整的负载。