2017-02-15 116 views
0

我在上有一些文件传输问题TCP。文件传输有时会起作用,有时它会卡在中间。当它卡住时,它看起来像期待通信通道中的数据,但没有数据也没有错误。因此它被无限期地卡住了。为了让事情变得混乱,它显示了相同文件的这种行为,即对于同一个文件它有时有效,有时它不起作用。Golang TCP文件传输陷入中间

这是我的程序如何工作。它会监听传入的请求。请求格式为JSON。根据请求类型,它将执行不同的操作。我发布了与文件传输相关的代码段。

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)+"\n") 
      } 
     } 
    } 
} 

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('\n') 
     com.Identifier = strings.TrimSpace(identifier) 

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

      fmt.Print("Destination file name:") 
      destinationFileName, _ = reader.ReadString('\n') 
      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) + "\n")) 

     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) 
    } 
} 

我测试了它在Linux和Windows,它显示了这两个系统上相同的行为。我能想到的唯一的事情就是发送者比接收者更快,即使我在同一台机器上运行它。如果是这样的话,除了握手机制之外,解决它的最佳实践是什么。

+0

它为我工作:第1我开始服务器代码,然后客户端代码,然后我在客户端标准输入框中键入TRANSFER_FILE,然后我输入一个64MB文件名称和目标名称,并按预期复制它。 –

+0

您是否尝试连续多次传输相同的文件? – azizulhakim

+0

是的https://gist.github.com/gonzaloserrano/1c4bfda42bf8ddfb87cedc62c91b2878 –

回答

2

无法将net.Conn包装在bufio.Reader中,然后继续使用net.Conn。您的功能被阻止的原因是因为您将数据缓存在reader中,所以您无法达到所需的消息大小。

您需要将reader传递给receiveFile函数,以便不丢失缓冲的数据。

您也忽略ReadLineisPrefix返回值。如果您不打算处理来自该方法的所有案例,我会按照文档并使用ReadBytes