doufan2541 2017-02-15 20:27
浏览 36
已采纳

Golang TCP文件传输陷入中间

I'm having some file transfer issue over TCP in go. The file transfer works sometimes and sometimes it gets stuck in the middle. When it gets stuck, it looks like it is expecting data in the communication channel but there is no data and no error as well. Hence it gets stuck indefinitely. To make thing confusing it shows this behavior for same file i.e for same file it works sometimes and sometimes it doesn't work.

This is how my program works. It'll listen for incoming requests. The requests are in JSON format. Based on request type it'll do different operation. I'm posting the code segment related to file transfer.

server.go

package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    _"io"
    "net"
    "os"
)

const (
    COMMAND_RECEIVE_FILE    = "TRANSFER_FILE"
    COMMAND_EXIT            = "EXIT"

    CONNECTION_TYPE = "tcp"
    CONNECTION_PORT = "3645"
    CONNECTION_HOST = ""
    BUFFER_SIZE     = 1024
)

type Command struct {
    Identifier string `json:"identifier"`
    Name       string `json:"name"`
    Size       int64  `json:"size"`
}

type Result struct {
    Message     string        `json:"message"`
}

func receiveFile(connection net.Conn, fileName string, fileSize int64) Result {
    fmt.Println("Receiving file")
    result := Result{Message: ""}

    file, err := os.Create(fileName)
    if err != nil {
        fmt.Println(err)
        result.Message = "Error opening file: " + fileName
        return result
    }

    defer file.Close()

    fileBuffer := make([]byte, BUFFER_SIZE)
    bytesRead := int64(0)
    count := 0
    for {
        if fileSize-bytesRead < int64(BUFFER_SIZE) {
            fileBuffer = make([]byte, fileSize-bytesRead)
        }

        fmt.Println("Reading ", BUFFER_SIZE, " bytes of data")
        n, err := connection.Read(fileBuffer)
        count++
        fmt.Println("Completed reading", n, " bytes of data, count=", count)
        file.Write(fileBuffer[0:n])
        bytesRead += int64(n)

        if err != nil {
            result.Message = "File transfer incomplete"
            break
        }

        if bytesRead >= fileSize {
            result.Message = "File transfer complete"
            break
        }
    }

    file.Chmod(0777)

    return result
}

func main() {
    ln, err := net.Listen(CONNECTION_TYPE, CONNECTION_HOST + ":"+CONNECTION_PORT)
    if err != nil {
        fmt.Println("error opening a tcp connection")
    }

    for {
        fmt.Println("waiting for new connection")
        conn, err := ln.Accept()
        if err != nil {

        } else {
            var commandStr string
            reader := bufio.NewReader(conn)

            var exitStatus = 1
            for exitStatus == 1 {
                fmt.Println("Waiting for new command: ")
                line,_,err := reader.ReadLine()
                if err != nil {
                    conn.Close()
                    exitStatus = 0
                    break
                } else {
                    fmt.Println("Size read :", len(line))
                }
                commandStr = string(line)
                fmt.Println("CommandStr: ", commandStr)


                var msg Command
                err = json.Unmarshal([]byte(commandStr), &msg)
                if err != nil {
                    fmt.Println("Error")
                    conn.Close()
                    break
                }

                result := Result{}
                fmt.Println("Received new command: ", msg.Identifier)
                switch msg.Identifier {

                case COMMAND_RECEIVE_FILE:
                    result = receiveFile(conn, msg.Name, msg.Size)

                case COMMAND_EXIT:
                    exitStatus = 0
                    conn.Close()
                default:
                    result = Result{Message: "Unrecognized command"}
                }

                out, _ := json.Marshal(result)
                fmt.Fprint(conn, string(out)+"
")
            }
        }
    }
}

test.go

package main

import (
    "bufio"
    "encoding/json"
    "fmt"
    "io"
    "log"
    "net"
    "os"
    "strings"
    _"time"
)

const (
    COMMAND_TRANSFER_FILE   = "TRANSFER_FILE"
    COMMAND_EXIT            = "EXIT"

    CONNECTION_TYPE = "tcp"
    CONNECTION_PORT = "3645"
    CONNECTION_HOST = ""
)

type Command struct {
    Identifier string `json:"identifier"`
    Name       string `json:"name"`
    Size       int64  `json:"size"`
}

type Result struct {
    Message     string        `json:"message"`
}

func main() {
    conn, _ := net.Dial(CONNECTION_TYPE, CONNECTION_HOST + ":" + CONNECTION_PORT)
    decoder := json.NewDecoder(conn)
    com := Command{}

    sourceFileName := ""
    destinationFileName := ""
    for {
        com = Command{}
        reader := bufio.NewReader(os.Stdin)
        identifier, _ := reader.ReadString('
')
        com.Identifier = strings.TrimSpace(identifier)

        switch com.Identifier {
        case COMMAND_TRANSFER_FILE:
            fmt.Print("Source file name:")
            sourceFileName, _ = reader.ReadString('
')
            sourceFileName = strings.TrimSpace(sourceFileName)

            fmt.Print("Destination file name:")
            destinationFileName, _ = reader.ReadString('
')
            com.Name = strings.TrimSpace(destinationFileName)

            file, err := os.Open(sourceFileName)
            if err != nil {
                log.Fatal(err)
            }
            defer file.Close()

            fileInfo, err := file.Stat()
            fileSize := fileInfo.Size()
            com.Size = fileSize

        case COMMAND_EXIT:
            conn.Close()
            os.Exit(0)
        }

        out, _ := json.Marshal(com)
        conn.Write([]byte(string(out) + "
"))

        if strings.Compare(com.Identifier, COMMAND_TRANSFER_FILE) == 0 {
            file, err := os.Open(sourceFileName)
            if err != nil {
                log.Fatal(err)
            }
            defer file.Close()

            n, err := io.Copy(conn, file)
            if err != nil {
                log.Fatal(err)
            }
            fmt.Println(n, "bytes sent")
        }

        var msg Result
        err := decoder.Decode(&msg)
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println(msg)
    }
}

I tested it on both Linux and Windows and it shows same behavior on both system. The only thing I can think of is that the sender is faster than the receiver even though I'm running it on the same machine. If that is the case, what will be a best practice to solve it other than the handshaking mechanism.

  • 写回答

1条回答 默认 最新

  • dongshang1934 2017-02-15 22:53
    关注

    You can't wrap the net.Conn in a bufio.Reader, then continue to use the net.Conn. The reason your function is blocked is because you left data buffered in the reader, so you won't ever reach the desired message size.

    You need to pass the reader to the receiveFile function in order to not lose the buffered data.

    You are also ignoring the isPrefix return value from ReadLine. I would follow the documentation and use ReadBytes instead if you're not going to handle all cases from that method.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 关于#网络安全#的问题:求ensp的网络安全,不要步骤要完成版文件
  • ¥15 可否在不同线程中调用封装数据库操作的类
  • ¥20 使用Photon PUN2解决游戏得分同步的问题
  • ¥15 微带串馈天线阵列每个阵元宽度计算
  • ¥15 keil的map文件中Image component sizes各项意思
  • ¥30 BC260Y用MQTT向阿里云发布主题消息一直错误
  • ¥20 求个正点原子stm32f407开发版的贪吃蛇游戏
  • ¥15 划分vlan后,链路不通了?
  • ¥20 求各位懂行的人,注册表能不能看到usb使用得具体信息,干了什么,传输了什么数据
  • ¥15 Vue3 大型图片数据拖动排序