golang socket breakpoint continuation of large file implementation

  • 2020-07-21 08:30:01
  • OfStack

In daily programming, we are sure to encounter socket to transfer the file content, if it is a large file, can not be transferred to 1 half for some reason broken, it is to transfer the file content again. Yes, we need to continue to transmit, that is, to continue to send the contents of the file from the last location.

In fact, it is not difficult to follow. The train of thought I understand is as follows:

The client sends a message asking the server where the contents of the last file you received were located

The server tells the client where the file content was last received

The client continues to send the file contents from the location of the last breakpoint

The client notifies the server after sending the file content, and then disconnects

Now let's look at the implementation of the code

The service side


// file name: server.go

package main

import (
 "os"
 "io"
 "net"
 "log"
 "strconv"
 // "time"
)

//  Take the content you receive append To the file 
func writeFile(content []byte) {
 if len(content) != 0 {
  fp, err := os.OpenFile("test_1.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0755)
  defer fp.Close()
  if err != nil {
   log.Fatalf("open file faild: %s\n", err)
  }
  _, err = fp.Write(content)
  if err != nil {
   log.Fatalf("append content to file faild: %s\n", err)
  }
  log.Printf("append content:  【 %s 】  success\n", string(content))
 }
}

//  Gets the size of the received content 
// ( Breakpoint continuation requires a large notification of received content where does the client start to send file content )
func getFileStat() int64 {
 fileinfo, err := os.Stat("test_1.txt")
 if err != nil {
  //  If it's not created the first time test_1.txt File is returned directly 0
  //  Tell the client to send file content from scratch 
  if os.IsNotExist(err) {
   log.Printf("file size: %d\n", 0)
   return int64(0)
  }
  log.Fatalf("get file stat faild: %s\n", err)
 }
 log.Printf("file size: %d\n", fileinfo.Size())
 return fileinfo.Size() 
}

func serverConn(conn net.Conn) {
 defer conn.Close()
 for {
  var buf = make([]byte, 10)
  n, err := conn.Read(buf)
  if err != nil {
   if err == io.EOF {
    log.Println("server io EOF\n")
    return
   }
   log.Fatalf("server read faild: %s\n", err)
  }
  log.Printf("recevice %d bytes, content is  【 %s 】 \n", n, string(buf[:n]))
  //  Determine the message sent by the client 
  //  If it is 'start--> 'is meant to tell the client where to start reading the file data to send 
  switch string(buf[:n]) {
  case "start-->":
   off := getFileStat()
   // int conver string
   stringoff := strconv.FormatInt(off, 10)
   _, err = conn.Write([]byte(stringoff))
   if err != nil {
    log.Fatalf("server write faild: %s\n", err)
   }
   continue
  case "<--end":
   //  Exit if the client receives a notification that all file contents have been sent 
   log.Fatalf("receive over\n")
   return
  // default:
  //  time.Sleep(time.Second * 1)
  }
  //  Saves the content sent by the client to a file 
  writeFile(buf[:n])
 }
}

func main() {
 //  Set up to monitor 
 l, err := net.Listen("tcp", ":8888")
 if err != nil {
  log.Fatalf("error listen: %s\n", err)
 }
 defer l.Close()

 log.Println("waiting accept.")
 //  Allows a client connection, if no client connection is available 1 The straight block 
 conn, err := l.Accept()
 if err != nil {
  log.Fatalf("accept faild: %s\n", err)
 }
 serverConn(conn)
}

The client


// file name: client.go

package main

import (
 "os"
 "io"
 "net"
 "log"
 "time"
 "strconv"
)

//  Gets the message sent by the server 
func clientRead(conn net.Conn) int {
 buf := make([]byte, 5)
 n, err := conn.Read(buf)
 if err != nil {
  log.Fatalf("receive server info faild: %s\n", err)
 }
 // string conver int
 off, err := strconv.Atoi(string(buf[:n]))
 if err != nil {
  log.Fatalf("string conver int faild: %s\n", err)
 }
 return off
}

//  Send a message to the server 
func clientWrite(conn net.Conn, data []byte) {
 _, err := conn.Write(data)
 if err != nil {
  log.Fatalf("send  【 %s 】  content faild: %s\n", string(data), err)
 }
 log.Printf("send  【 %s 】  content success\n", string(data))
}

// client conn
func clientConn(conn net.Conn) {
 defer conn.Close()

 //  send "start-->" A message notifies the server that I'm about to start sending the file contents 
 //  Just tell me how much you've received , I'll start sending from what you've already received 
 clientWrite(conn, []byte("start-->"))
 off := clientRead(conn)

 // send file content
 fp, err := os.OpenFile("test.txt", os.O_RDONLY, 0755)
 if err != nil {
  log.Fatalf("open file faild: %s\n", err)
 }
 defer fp.Close()

 // set file seek
 //  Where do you start reading file contents 
 _, err = fp.Seek(int64(off), 0)
 if err != nil {
  log.Fatalf("set file seek faild: %s\n", err)
 }
 log.Printf("read file at seek: %d\n", off)

 for {
  //  Every time send 10 Three bytes of content 
  data := make([]byte, 10)
  n, err := fp.Read(data)
  if err != nil {
   if err == io.EOF {
    //  If you have finished reading the file contents 
    //  Is sent '<--end' The message informs the server that the file content has been sent 
    time.Sleep(time.Second * 1)
    clientWrite(conn, []byte("<--end"))
    log.Println("send all content, now quit")
    break
   }
   log.Fatalf("read file err: %s\n", err)
  }
  //  Send file contents to the server 
  clientWrite(conn, data[:n])
 }
}

func main() {
 // connect timeout 10s
 conn, err := net.DialTimeout("tcp", ":8888", time.Second * 10)
 if err != nil {
  log.Fatalf("client dial faild: %s\n", err)
 }
 clientConn(conn)
 }

The client reads the file test.txt and sends it to the server, which saves the received file in es25EN_1.txt. We simulate breakpoint continuation by:

First send the test. txt file contents to the server

Modify the ES33en. txt file, add 1 more

Run server socket and client socket again to see if the client only sends the newly added file content to the server


#  Suppose I test.txt The file has the following contents 
$ cat test.txt
hello golang.

#  First run server socket Run again client socket (Run in two terminal Windows respectively) 
$ go run server.go
$ go run client.go

#  The server outputs the following 
2018/04/05 23:37:13 waiting accept.
2018/04/05 23:37:15 recevice 8 bytes, content is  【 start--> 】 
2018/04/05 23:37:15 file size: 0
2018/04/05 23:37:15 recevice 10 bytes, content is  【 hello gola 】 
2018/04/05 23:37:15 append content:  【 hello gola 】  success
2018/04/05 23:37:15 recevice 2 bytes, content is  【 n. 】 
2018/04/05 23:37:15 append content:  【 n. 】  success
2018/04/05 23:37:16 recevice 6 bytes, content is  【 <--end 】 
2018/04/05 23:37:16 receive over
exit status 1

#  The client outputs the following 
2018/04/05 23:37:15 send  【 start--> 】  content success
2018/04/05 23:37:15 read file at seek: 0
2018/04/05 23:37:15 send  【 hello gola 】  content success
2018/04/05 23:37:15 send  【 n. 】  content success
2018/04/05 23:37:16 send  【 <--end 】  content success
2018/04/05 23:37:16 send all content, now quit

#  Now let's see test_1.txt Content with test.txt completely 1 to 
$ cat test_1.txt
hello golan.

# -------  Simulate breakpoint continuation  ----------
#  Now let's go to test.txt Additional content : hello python.
$ cat test.txt
hello golang.
hello python.

#  We will have a 1 Time is running server socket  and  client socket( Run in two terminal Windows respectively) 
$ go run server.go
$ go run client.go

#  The server outputs the following 
2018/04/05 23:44:31 waiting accept.
2018/04/05 23:44:34 recevice 8 bytes, content is  【 start--> 】 
2018/04/05 23:44:34 file size: 12
2018/04/05 23:44:34 recevice 10 bytes, content is  【 
hello pyt 】 
2018/04/05 23:44:34 append content:  【 
hello pyt 】  success
2018/04/05 23:44:34 recevice 4 bytes, content is  【 hon. 】 
2018/04/05 23:44:34 append content:  【 hon. 】  success
2018/04/05 23:44:35 recevice 6 bytes, content is  【 <--end 】 
2018/04/05 23:44:35 receive over
exit status 1
#  The server receives what the client sends  start-->  The information is then retrieved where the file content was last received and notified to the client (here file size  is 12 ) 

#  The client outputs the following 
2018/04/05 23:44:34 send  【 start--> 】  content success
2018/04/05 23:44:34 read file at seek: 12
2018/04/05 23:44:34 send  【 
hello pyt 】  content success
2018/04/05 23:44:34 send  【 hon. 】  content success
2018/04/05 23:44:35 send  【 <--end 】  content success
2018/04/05 23:44:35 send all content, now quit
#  Our client gets the location of the file returned by the server  Seek  To specify where to start reading files 
#  You can see from the log that our client has only sent the appends : hello python.  To the server 

#  Let's look at this test_1.txt Whether the contents of the file follow test.txt1 to 
$ cat test_1.txt
hello golang.
hello python.

Related articles: