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 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab
  • ¥20 重新写的代码替换了之后运行hbuliderx就这样了
  • ¥100 监控抖音用户作品更新可以微信公众号提醒
  • ¥15 UE5 如何可以不渲染HDRIBackdrop背景
  • ¥70 2048小游戏毕设项目
  • ¥20 mysql架构,按照姓名分表
  • ¥15 MATLAB实现区间[a,b]上的Gauss-Legendre积分
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题