2017-02-15 7 views
0

TCPよりもファイル転送に問題があります。goです。ファイル転送は時には動作し、途中で止まってしまうことがあります。立ち往生すると、通信チャネル内のデータを期待しているように見えますが、データはなく、エラーもありません。したがって、無期限にスタックされます。事を混乱させるために、同じファイル、つまり同じファイルに対してこの動作を示します。時々動作しますが、動作しないことがあります。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

それは私のために働いた:最初に私はサーバーコードを開始し、次にクライアントコードを入力し、次にクライアントの標準入力にTRANSFER_FILEをタイプし、次に64MBのファイル名と宛先名を入力し、 –

+0

同じファイルを複数回連続して転送しようとしましたか? – azizulhakim

+0

ypp https://gist.github.com/gonzaloserrano/1c4bfda42bf8ddfb87cedc62c91b2878 –

答えて

2

net.Connbufio.Readerにラップしてから、net.Connを引き続き使用することはできません。あなたの機能がブロックされている理由は、データをreaderにバッファしたままにしておき、目的のメッセージサイズに達することがないからです。

バッファされたデータを失うことがないように、readerreceiveFile関数に渡す必要があります。

また、の戻り値はReadLineです。あなたがそのメソッドからすべてのケースを処理するつもりがないなら、私はドキュメントに従い、代わりにReadBytesを使用します。

関連する問題