Go Network File Transfer

Posted by evdawg80 on Tue, 07 Jan 2020 09:53:27 +0100

Process analysis

With the help of TCP, the basic idea is as follows:

  1. The sender (client) sends the file name to the server, and the server saves the file name.
  2. The receiver (server) returns a message ok to the client to confirm that the file name is saved successfully.
  3. After receiving the message, the sender (client) begins to send the file data to the server.
  4. The receiver (server) reads the content of the file and writes it to the previously saved file.

Because the file transmission needs a stable and reliable connection, TCP mode is used to complete the network file transmission function.

First get the filename. The stat() function in the os package is used to obtain the file attribute information. Include the file name and file size in the file properties returned by the function. Stat parameter name passed in the absolute path of file access. The Name() function in FileInfo can extract the file list independently.

func Stat(name string) (fi FileInfo, err error)

Stat returns a FileInfo that describes the file object specified by name. If the specified file object is a symbolic link, the returned FileInfo describes the information of the file that the symbolic link points to, and this function will try to jump the link. If there is an error, the error value returned is of type * PathError.

We can know from the source code that FileInfo is an interface. To implement this interface, we must implement all the following methods of this interface

In fact, the realization of network file transmission relies on the knowledge of local file replication and TCP network programming. Let's have a look first Copying files in Go language and Go network programming Learn about it.

So the general steps of using TCP to realize file transfer can be summed up as follows

Receiver:

  1. Create a listener and close it at the end of the program.
  2. Block and wait for the client to connect to Conn, and close conn at the end of the program.
  3. Read client send file name. Save the fileName.
  4. Send back "ok".
  5. The encapsulation function RecvFile receives the file content sent by the client. Parameters fileName and conn
  6. Create file by file name, Close at the end
  7. Loop Read the network file content of the sender. When reading 0, it means that the file has been Read.
  8. Write the read content to the created file intact

Receiver code:

package main

import (
    "fmt"
    "io"
    "net"
    "os"
)

func recvFile(conn net.Conn, fileName string) {
    //Create a new file by file name
    file, err := os.Create(fileName)
    if err != nil {
        fmt.Printf("os.Create()Function execution error, error is:%v\n", err)
        return
    }
    defer file.Close()

    //Read data from network and write to local file
    for {
        buf := make([]byte, 4096)
        n, err := conn.Read(buf)

        //Write to local file, read and write
        file.Write(buf[:n])
        if err != nil {
            if err == io.EOF {
                fmt.Printf("Receive file complete.\n")
            } else {
                fmt.Printf("conn.Read()Method execution error, error is:%v\n", err)
            }
            return
        }
    }
}

func main() {

    //1. Create a listening socket
    listener, err := net.Listen("tcp", "127.0.0.1:8000")
    if err != nil {
        fmt.Printf("net.Listen()Function execution error, error is:%v\n", err)
        return
    }
    defer listener.Close()

    //Blocking monitoring
    conn, err := listener.Accept()
    if err != nil {
        fmt.Printf("listener.Accept()Method execution error, error is:%v\n", err)
        return
    }
    defer conn.Close()

    //The file name cannot be longer than 1024 bytes
    buf := make([]byte, 4096)
    n, err := conn.Read(buf)
    if err != nil {
        fmt.Printf("conn.Read()Method execution error, error is:%v\n", err)
        return
    }
    fileName := string(buf[:n])

    //Write back ok to the sender
    conn.Write([]byte("ok"))

    //Get file content
    recvFile(conn, fileName)
}

Sending end:

  1. Prompts you to enter a file name using command line parameters. Receive filename filepath (including access path)
  2. Use os.Stat() to get the file properties and get the pure fileName (remove the access path)
  3. Initiate the connection server request actively, and close the connection at the end.
  4. Send filename to receiver conn.Write()
  5. Read the confirmation data sent back by the receiver conn.Read()
  6. Judge whether it is "ok". If so, encapsulate the SendFile() function to send the contents of the file. Parameters filePath and conn
  7. Read only Open file, Close file at the end
  8. Read the local file circularly, read EOF, and finish reading.
  9. Send the read content to the receiver (server) intact conn.Write

Sender Code:

package main

import (
    "fmt"
    "io"
    "net"
    "os"
)
func sendFile(conn net.Conn, filePath string) {
    //Read only open file
    file, err := os.Open(filePath)
    if err != nil {
        fmt.Printf("os.Open()Function execution error, error is:%v\n", err)
        return
    }
    defer file.Close()

    buf := make([]byte, 4096)
    for {
        //Read the data from the local file and write it to the network receiver. How much to read, how much to write
        n, err := file.Read(buf)
        if err != nil {
            if err == io.EOF {
                fmt.Printf("Send file complete\n")
            } else {
                fmt.Printf("file.Read()Method execution error,Error is:%v\n", err)
            }
            return
        }
        //Write to network socket
        _, err = conn.Write(buf[:n])
    }
}

func main() {

    //Get command line parameters
    list := os.Args

    if len(list) != 2 {
        fmt.Printf("Format is:go run xxx.go file name\n")
        return
    }

    //Absolute path to extract file
    path := list[1]

    //Get file properties
    fileInfo, err := os.Stat(path)
    if err != nil {
        fmt.Printf("os.Stat()Function execution error, error is:%v\n", err)
        return
    }

    //Initiate connection request actively
    conn, err := net.Dial("tcp", "127.0.0.1:8000")
    if err != nil {
        fmt.Printf("net.Dial()Function execution error, error is:%v\n", err)
        return
    }
    defer conn.Close()

    //Send file name to receiver
    _, err = conn.Write([]byte(fileInfo.Name()))

    //Read server postback data
    buf := make([]byte, 4096)
    n, err := conn.Read(buf)
    if err != nil {
        fmt.Printf("conn.Read(buf)Method execution error, error is:%v\n", err)
        return
    }

    if string(buf[:n]) == "ok" {
        //Write file content to server -- with conn
        sendFile(conn, path)
    }
}

Topics: Go network Programming socket Attribute