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.