【问题标题】:Go connection.Writer and reader not behaving properly, reading 2 write in one read operationGo connection.Writer 和 reader 行为不正常,一次读取操作读取 2 写入
【发布时间】:2017-06-24 12:29:49
【问题描述】:

我正在尝试从客户端读取文件,然后将其发送到服务器。

这样,你在客户端程序中输入send <fileName>,然后<fileName>将被发送到服务器。服务器通过 TCP 连接从客户端读取两件事,首先是命令send <fileName>,其次是文件的内容。

但是,有时我的程序会在<fileName> 字符串中随机包含文件内容。例如,假设我有一个名为 xyz.txt 的文本文件,其内容是“Hellow world”。服务器有时会收到send xyz.txtHellow world。有时它不会,但它工作得很好。

我认为这是同步或不刷新读写器缓冲区的问题。但我不太确定。 提前致谢!

客户端代码:

func sendFileToServer(fileName string, connection net.Conn) {

    fileBuffer := make([]byte, BUFFER_SIZE)
    var err error

    file, err := os.Open(fileName) // For read access.

    lock := make(chan int)
    w := bufio.NewWriter(connection)

    go func(){
        w.Write([]byte("send " + fileName))
        w.Flush()
        lock <- 1
    }()

    <-lock
    // make a read buffer
    r := bufio.NewReader(file)

    //read file until there is an error
    for err == nil || err != io.EOF {
        //read a chunk
        n, err := r.Read(fileBuffer)
        if err != nil && err != io.EOF {
            panic(err)
        }
        if n == 0 {
            break
        }
        // write a chunk
        if _, err := w.Write(fileBuffer[:n]); err != nil {
            panic(err)
        }
    }
    file.Close()
    connection.Close()
    fmt.Println("Finished sending.")
}

服务端代码:(connectionHandler 是一个 goroutine,为来自客户端的每个 TCP 连接请求调用)

func connectionHandler(connection net.Conn, bufferChan chan []byte, stringChan chan string) {
    buffer := make([]byte, 1024)

    _, error := connection.Read(buffer)
    if error != nil {
        fmt.Println("There is an error reading from connection", error.Error())
        stringChan<-"failed"
        return 
    }
    fmt.Println("command recieved: " + string(buffer))
    if("-1"==strings.Trim(string(buffer), "\x00")){
        stringChan<-"failed"
        return
    }

    arrayOfCommands := strings.Split(string(buffer)," ")
    arrayOfCommands[1] = strings.Replace(arrayOfCommands[1],"\n","",-1)
    fileName := strings.Trim(arrayOfCommands[1], "\x00")    

    if arrayOfCommands[0] == "get" {
        fmt.Println("Sending a file " + arrayOfCommands[1])
        sendFileToClient(fileName, connection, bufferChan, stringChan)
    } else if arrayOfCommands[0] == "send" {
        fmt.Println("Getting a file " + arrayOfCommands[1])
        getFileFromClient(fileName, connection, bufferChan, stringChan)
    } else {
        _, error = connection.Write([]byte("bad command"))
    }
    fmt.Println("connectionHandler finished")
}


func getFileFromClient(fileName string, connection net.Conn,bufferChan chan []byte, stringChan chan string) { //put the file in memory
    stringChan<-"send"
    fileBuffer := make([]byte, BUFFER_SIZE)

    var err error
    r := bufio.NewReader(connection)

    for err == nil || err != io.EOF {
        //read a chunk
        n, err := r.Read(fileBuffer)
        if err != nil && err != io.EOF {
            panic(err)
        }
        if n == 0 {
            break
        }
        bufferChan<-fileBuffer[:n]
        stringChan<-fileName
    }

    connection.Close()
    return 

}

【问题讨论】:

    标签: file go tcp io server


    【解决方案1】:

    TCP 是一种流协议。它没有消息。网络(在我们不需要担心的某些限制内)可以一次发送一个字节或一次发送所有数据。即使你很幸运,网络按照你想要的方式发送数据包,也没有什么能阻止接收端将数据包连接到一个缓冲区中。

    换句话说:没有什么可以使每个Read 调用返回与您在某些特定Write 调用中编写的一样多的字节。你有时会很幸运,有时,正如你所注意到的,你不会很幸运。如果没有错误,您从流中执行的所有读取都将返回您写入的所有字节,这是您唯一的保证。

    你需要定义一个合适的协议。

    这与 Go 无关。每种编程语言都会以这种方式运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-15
      • 2017-06-27
      • 2013-10-20
      • 1970-01-01
      相关资源
      最近更新 更多