2017-02-15 1 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) 
    } 
} 

내가 모두 리눅스와 윈도우에서 그것을 테스트하고 두 시스템에서 동일한 동작을 보여줍니다. 내가 생각할 수있는 유일한 이유는 동일한 컴퓨터에서 실행 중이더라도 보낸 사람이받는 사람보다 빠릅니다. 그렇다면 핸드 쉐이킹 메커니즘 이외의 다른 문제를 해결하는 가장 좋은 방법은 무엇입니까?

+0

그것은 나를 위해 일했습니다 : 첫째, 서버 코드를 시작한 다음 클라이언트 코드를 입력 한 다음 클라이언트 표준 입력란에 TRANSFER_FILE을 입력 한 다음 64MB 파일 이름과 대상 이름을 입력하고 예상대로 복사했습니다. –

+0

동일한 파일을 여러 번 계속 전송하려고 시도 했습니까? – azizulhakim

+0

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

답변

2

net.Connbufio.Reader에 넣은 다음 net.Conn을 계속 사용할 수 없습니다. 기능이 차단 된 이유는 데이터를 reader에 버퍼링하여 남겨 두었 기 때문에 원하는 메시지 크기에 도달하지 못하기 때문입니다.

버퍼링 된 데이터가 손실되지 않도록 reader 함수를 receiveFile 함수에 전달해야합니다.

의 반환 값은 ReadLine입니다. 나는 그 방법으로 모든 케이스를 다룰 생각이 없다면 문서를 읽고 대신 ReadBytes을 사용할 것입니다.

관련 문제