2014-09-25 86 views
4

我是Go的新手,并且在调试此客户机/服务器文件传输代码时遇到麻烦。当我从服务器请求一个719kb的png文件时,我得到了719kb ..但是并不完美,当我打开它时,png并没有完全显示(有些被切断了,我在哪里会出错?Go中的TCP客户机/服务器文件传输

// CLIENT /// 
    package main 

import (
    "bufio" 
    "bytes" 
    "fmt" 
    "io" 
    "log" 
    "net" 
    "os" 
    "strings" 
) 

const BUFFER_SIZE = 1024 

func main() { 

    //get port and ip address to dial 

    if len(os.Args) != 3 { 
     fmt.Println("useage example: tcpClient 127.0.0.1 7005") 
     return 
    } 

    var ip string = os.Args[1] 
    var port string = os.Args[2] 

    connection, err := net.Dial("tcp", ip+":"+port) 
    if err != nil { 
     fmt.Println("There was an error making a connection") 
    } 

    //read from 
    reader := bufio.NewReader(os.Stdin) 
    fmt.Print("Please enter 'get <filename>' or 'send <filename>' to transfer files to the server\n\n") 
    inputFromUser, _ := reader.ReadString('\n') 
    arrayOfCommands := strings.Split(inputFromUser, " ") 

    if arrayOfCommands[0] == "get" { 
     getFileFromServer(arrayOfCommands[1], connection) 

    } else if arrayOfCommands[0] == "send" { 
     sendFileToServer(arrayOfCommands[1], connection) 
    } else { 
     fmt.Println("Bad Command") 
    } 

} 

func sendFileToServer(fileName string, connection net.Conn) { 

    var currentByte int64 = 0 
    fmt.Println("send to client") 
    fileBuffer := make([]byte, BUFFER_SIZE) 

    var err error 

    //file to read 
    file, err := os.Open(strings.TrimSpace(fileName)) // For read access. 
    if err != nil { 
     connection.Write([]byte("-1")) 
     log.Fatal(err) 
    } 
    connection.Write([]byte("send " + fileName)) 
    //read file until there is an error 
    for err == nil || err != io.EOF { 

     _, err = file.ReadAt(fileBuffer, currentByte) 
     currentByte += BUFFER_SIZE 
     fmt.Println(fileBuffer) 
     connection.Write(fileBuffer) 
    } 

    file.Close() 
    connection.Close() 

} 

func getFileFromServer(fileName string, connection net.Conn) { 

    var currentByte int64 = 0 

    fileBuffer := make([]byte, BUFFER_SIZE) 

    var err error 
    file, err := os.Create(strings.TrimSpace(fileName)) 
    if err != nil { 
     log.Fatal(err) 
    } 
    connection.Write([]byte("get " + fileName)) 
    for { 

     connection.Read(fileBuffer) 
     cleanedFileBuffer := bytes.Trim(fileBuffer, "\x00") 

     _, err = file.WriteAt(cleanedFileBuffer, currentByte) 

     currentByte += BUFFER_SIZE 

     if err == io.EOF { 
      break 
     } 

    } 

    file.Close() 
    return 

} 

// END CLIENT // 
// SERVER // 
    package main 

import (
    "bytes" 
    "fmt" 
    "io" 
    "log" 
    "net" 
    "os" 
    "strings" 
) 

const BUFFER_SIZE = 1024 
const PORT = "7005" 

func main() { 

    fmt.Println("start listening") 

    server, error := net.Listen("tcp", "localhost:"+PORT) 
    if error != nil { 
     fmt.Println("There was an error starting the server" + error.Error()) 
     return 
    } 

    //infinate loop 
    for { 

     connection, error := server.Accept() 
     if error != nil { 
      fmt.Println("There was am error with the connection" + error.Error()) 
      return 
     } 
     fmt.Println("connected") 
     //handle the connection, on it's own thread, per connection 
     go connectionHandler(connection) 
    } 
} 

func connectionHandler(connection net.Conn) { 
    buffer := make([]byte, BUFFER_SIZE) 

    _, error := connection.Read(buffer) 
    if error != nil { 
     fmt.Println("There is an error reading from connection", error.Error()) 
     return 
    } 
    fmt.Println("command recieved: " + string(buffer)) 

    //loop until disconntect 

    cleanedBuffer := bytes.Trim(buffer, "\x00") 
    cleanedInputCommandString := strings.TrimSpace(string(cleanedBuffer)) 
    arrayOfCommands := strings.Split(cleanedInputCommandString, " ") 

    fmt.Println(arrayOfCommands[0]) 
    if arrayOfCommands[0] == "get" { 
     sendFileToClient(arrayOfCommands[1], connection) 
    } else if arrayOfCommands[0] == "send" { 
     fmt.Println("getting a file") 

     getFileFromClient(arrayOfCommands[1], connection) 

    } else { 
     _, error = connection.Write([]byte("bad command")) 
    } 

} 

func sendFileToClient(fileName string, connection net.Conn) { 
    var currentByte int64 = 0 
    fmt.Println("send to client") 
    fileBuffer := make([]byte, BUFFER_SIZE) 

    //file to read 
    file, err := os.Open(strings.TrimSpace(fileName)) // For read access. 
    if err != nil { 

     log.Fatal(err) 
    } 
    var err2 error 

    //read file until there is an error 
    for { 

     _, err2 = file.ReadAt(fileBuffer, currentByte) 
     currentByte += BUFFER_SIZE 
     fmt.Println(fileBuffer) 
     connection.Write(fileBuffer) 

     if err2 == io.EOF { 
      break 
     } 
    } 

    file.Close() 
    return 

} 

func getFileFromClient(fileName string, connection net.Conn) { 

    var currentByte int64 = 0 

    fileBuffer := make([]byte, BUFFER_SIZE) 

    var err error 
    file, err := os.Create(strings.TrimSpace(fileName)) 
    if err != nil { 
     log.Fatal(err) 
    } 
    connection.Write([]byte("get " + fileName)) 
    for err == nil || err != io.EOF { 

     connection.Read(fileBuffer) 

     cleanedFileBuffer := bytes.Trim(fileBuffer, "\x00") 

     _, err = file.WriteAt(cleanedFileBuffer, currentByte) 
     if len(string(fileBuffer)) != len(string(cleanedFileBuffer)) { 
      break 
     } 
     currentByte += BUFFER_SIZE 

    } 

    connection.Close() 
    file.Close() 
    return 

} 

// END SERVER // 

回答

5

你需要考虑从ReadAt返回的字节数,否则最后fileBuffer您发送会得到额外的垃圾字节

例:

n, err := file.ReadAt(fileBuffer, currentByte) 
connection.Write(fileBuffer[:n]) 

而且bytes.Trim(fileBuffer, "\x00")将摧毁几乎所有的二进制文件,因为通常他们使用空字节填补空间。

而且这样做的正确的方法是只使用io.Copy

file, err := os.Open(strings.TrimSpace(fileName)) // For read access. 
if err != nil { 
    log.Fatal(err) 
} 
defer file.Close() // make sure to close the file even if we panic. 
n, err = io.Copy(connection, file) 
if err != nil { 
    log.Fatal(err) 
} 
fmt.Println(n, "bytes sent")