2017-02-15 3 views
0

У меня возникла проблема с передачей файлов через TCP в go. Передача файлов работает иногда, и иногда она застревает посередине. Когда он застревает, похоже, что он ожидает данных в канале связи, но нет данных и ошибок. Следовательно, он застрял бесконечно. Чтобы сделать что-то непонятным, это показывает такое поведение для того же файла. I.e для того же файла он работает иногда, а иногда и не работает.Передача файлов протокола 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 в клиентском stdin, затем я ввел имя имени файла 64 МБ и имя получателя, и он был скопирован как ожидалось. –

+0

Вы пытались передать один и тот же файл непрерывно несколько раз? – azizulhakim

+0

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

ответ

2

Вы не можете обернуть net.Conn в bufio.Reader, а затем продолжить использовать net.Conn. Причина, по которой ваша функция заблокирована, заключается в том, что вы оставили данные, буферизированные в reader, поэтому вы никогда не достигнете желаемого размера сообщения.

Вам необходимо передать reader функции receiveFile, чтобы не потерять буферные данные.

Вы также игнорируете возвращаемое значение isPrefix от ReadLine. Я буду следовать документации и использовать ReadBytes вместо этого, если вы не будете обрабатывать все случаи из этого метода.