我是刚接触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
    
        // ...
    }
    
    但是,您需要注意一些极端情况。
    尽管可以在服务器端跟踪错误处理,但是从客户端角度来看,它将忽略这些错误并保存以下文件:
  • 零字节(如果服务器无法打开文件);或
  • 部分字节(如果中间的服务器的io.Copy失败)

  • 为了解决这些情况,建议在连接有效负载的开头写入文件大小的标头,例如
    服务器
  • 文件打开:
  • 失败:defer关闭conn(客户端将获得零字节)

  • 统计文件大小
  • 将文件大小写为8字节的标头(64位文件大小可覆盖任何大小),例如
  • b := make([]byte, 8)
  • binary.LittleEndian.PutUint64(b, fsize)
  • n, err := io.Copy(conn, b)

  • io。像以前一样复制文件主体
  • 返回任何错误



  • 客户
  • 将标头读入8字节缓冲区
  • 短读(即小于8个字节)无/错误的标头-服务器无法获取文件
  • 解组文件大小头:
  • fsize := binary.LittleEndian.Uint64(b)

  • 跟踪字节复制:
  • n, err := io.Copy(file, conn)

  • 验证n == fsize以确保已接收到完整的有效负载。
  • 关于go - 如何在TCP客户端中停止io.Copy(file,conn)?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62842866/

    10-16 08:32