• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    golang socket断点续传大文件的实现方法

    在日常编程中,我们肯定会遇到用socket传送文件内容,如果是大文件的,总不能传送到一半因某原因断掉了,又从新传送文件内容吧。对,我们需要续传,也就是接着上次传送的位置继续发送文件内容。

    续传的话,其实并不难,我理解的思路大概如下:

    客户端发送消息询问服务端,你上次接收到的文件内容位置

    服务端告诉客户端上次接收到的文件内容位置

    客户端就从上次断点的位置继续发送文件内容

    客户端发送文件内容完毕后通知服务端,然后断开连接

    下面我们看看代码的实现

    服务端

    // file name: server.go
    
    package main
    
    import (
     "os"
     "io"
     "net"
     "log"
     "strconv"
     // "time"
    )
    
    // 把接收到的内容append到文件
    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))
     }
    }
    
    // 获取已接收内容的大小
    // (断点续传需要把已接收内容大下通知客户端从哪里开始发送文件内容)
    func getFileStat() int64 {
     fileinfo, err := os.Stat("test_1.txt")
     if err != nil {
      // 如果首次没有创建test_1.txt文件,则直接返回0
      // 告诉客户端从头开始发送文件内容
      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]))
      // 判断客户端发送过来的消息
      // 如果是'start-->‘则表示需要告诉客户端从哪里开始读取文件数据发送
      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":
       // 如果接收到客户端通知所有文件内容发送完毕消息则退出
       log.Fatalf("receive over\n")
       return
      // default:
      //  time.Sleep(time.Second * 1)
      }
      // 把客户端发送的内容保存到文件
      writeFile(buf[:n])
     }
    }
    
    func main() {
     // 建立监听
     l, err := net.Listen("tcp", ":8888")
     if err != nil {
      log.Fatalf("error listen: %s\n", err)
     }
     defer l.Close()
    
     log.Println("waiting accept.")
     // 允许客户端连接,在没有客户端连接时,会一直阻塞
     conn, err := l.Accept()
     if err != nil {
      log.Fatalf("accept faild: %s\n", err)
     }
     serverConn(conn)
    }

    客户端

    // file name: client.go
    
    package main
    
    import (
     "os"
     "io"
     "net"
     "log"
     "time"
     "strconv"
    )
    
    // 获取服务端发送的消息
    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
    }
    
    // 发送消息到服务端
    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()
    
     // 发送"start-->"消息通知服务端,我要开始发送文件内容了
     // 你赶紧告诉我你那边已经接收了多少内容,我从你已经接收的内容处开始继续发送
     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
     // 设置从哪里开始读取文件内容
     _, 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 {
      // 每次发送10个字节大小的内容
      data := make([]byte, 10)
      n, err := fp.Read(data)
      if err != nil {
       if err == io.EOF {
        // 如果已经读取完文件内容
        // 就发送'--end'消息通知服务端,文件内容发送完了
        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)
      }
      // 发送文件内容到服务端
      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)
     }

    客户端读取文件test.txt内容发送到服务端,服务端把接收到的文件内容保存在test_1.txt文件中。我们模拟断点续传的方式是:

    第一次先发送test.txt文件内容到服务端

    修改test.txt文件,加一些内容

    再次运行server socket以及client socket,观察客户端是不是只发送新增的文件内容到服务端

    # 假设我的test.txt文件有以下内容
    $ cat test.txt
    hello golang.
    
    # 先运行server socket再运行client socket(分别在两个终端窗口运行)
    $ go run server.go
    $ go run client.go
    
    # 服务端会输出以下内容
    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
    
    # 客户端会输出如下内容
    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
    
    # 这时候我们看看test_1.txt内容跟test.txt完全一致
    $ cat test_1.txt
    hello golan.
    
    # ------- 模拟断点续传 ----------
    # 现在我们往test.txt追加内容: hello python.
    $ cat test.txt
    hello golang.
    hello python.
    
    # 我们再一次运行server socket 和 client socket(分别在两个终端窗口运行)
    $ go run server.go
    $ go run client.go
    
    # 服务端会输出以下内容
    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
    # 服务端在接收到客户端发送的 start--> 信息后会获取上次接收到文件内容位置,并通知客户端(这里file size 是12)
    
    # 客户端会输出以下内容
    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
    # 我们客户端获取到了服务端返回的文件位置,通过 Seek 来指定从哪里开始读取文件
    # 通过日志可以看到我们客户端只发送了后面追加的内容: hello python. 到服务端
    
    # 我们看看此时test_1.txt文件的内容是否跟test.txt一致
    $ cat test_1.txt
    hello golang.
    hello python.

    以上这篇golang socket断点续传大文件的实现方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

    您可能感兴趣的文章:
    • 解决Goland中利用HTTPClient发送请求超时返回EOF错误DEBUG
    • Golang中Delve版本太低无法Debug的问题
    • Golang命令行进行debug调试操作
    • Goland 断点调试Debug的操作
    上一篇:golang 检查网络状态是否正常的方法
    下一篇:golang并发下载多个文件的方法
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    golang socket断点续传大文件的实现方法 golang,socket,断点,续传,大,