<s id="0oyqk"></s>
  • <option id="0oyqk"><optgroup id="0oyqk"></optgroup></option>
  • <source id="0oyqk"><bdo id="0oyqk"></bdo></source>
  • golang socket斷點續傳大文件的實現方法

     更新時間:2019-09-09 09:01:28   作者:佚名   我要評論(0)

    在日常編程中,我們肯定會遇到用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斷點續傳大文件的實現方法就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持腳本之家。

    您可能感興趣的文章:

    • golang網絡socket粘包問題的解決方法
    • Go語言實現socket實例
    • Go get命令使用socket代理的方法

    相關文章

    • gorm golang 并發連接數據庫報錯的解決方法

      gorm golang 并發連接數據庫報錯的解決方法

      底層報錯 error:cannot assign requested address 原因 并發場景下 client 頻繁請求端口建立tcp連接導致端口被耗盡 解決方案 root執行即可 sysctl -w net.
      2019-09-09
    • golang socket斷點續傳大文件的實現方法

      golang socket斷點續傳大文件的實現方法

      在日常編程中,我們肯定會遇到用socket傳送文件內容,如果是大文件的,總不能傳送到一半因某原因斷掉了,又從新傳送文件內容吧。對,我們需要續傳,也就是接著
      2019-09-09
    • golang http連接復用方法

      golang http連接復用方法

      server端 golang httpserver 默認開啟keepalive連接復用選項 handler函數需要完整讀body數據,構造返回消息,否則當數據不能一次發送完成時,連接復用就會失效
      2019-09-09
    • 詳解golang 模板(template)的常用基本語法

      詳解golang 模板(template)的常用基本語法

      模板 在寫動態頁面的網站的時候,我們常常將不變的部分提出成為模板,可變部分通過后端程序的渲染來生成動態網頁,golang提供了html/template包來支持模板
      2019-09-09
    • golang并發下載多個文件的方法

      golang并發下載多個文件的方法

      背景說明 假設有一個分布式文件系統,現需要從該系統中并發下載一部分文件到本地機器。 已知該文件系統的部分節點ip, 以及需要下載的文件fileID列表,并能通過
      2019-09-09
    • golang解析html網頁的方法

      golang解析html網頁的方法

      1.先看一下整個結構: 主要是web和html目錄,分別存放go代碼和html相關的資源文件。 2.html代碼比較簡單,代碼如下: <html> <head> <title>Go web</tit
      2019-09-09
    • golang并發ping主機的方法

      golang并發ping主機的方法

      利用了golang對高并發的良好支持,同目錄下將ip每行一個寫入pinglist.txt文件即可 其實這個功能用linux一條命令就能搞定: cat pinglist.txt | xargs -P 10
      2019-09-09
    • php常用經典函數集錦【數組、字符串、棧、隊列、排序等】

      php常用經典函數集錦【數組、字符串、棧、隊列、排序等】

      本文實例總結了php常用經典函數。分享給大家供大家參考,具體如下: 數組函數 數組操作 range(start,end) 創建一個數組,值從start開始,一直到end結束
      2019-09-09
    • Go 實現百萬WebSocket連接的方法示例

      Go 實現百萬WebSocket連接的方法示例

      大家好!我是 Sergey Kamardin,是 Mail.Ru 的一名工程師。 本文主要介紹如何使用 Go 開發高負載的 WebSocket 服務。 如果你熟悉 WebSockets,但對 Go 了解不
      2019-09-09
    • golang抓取網頁并分析頁面包含的鏈接方法

      golang抓取網頁并分析頁面包含的鏈接方法

      1. 下載非標準的包,"golang.org/x/net/html" 2. 先安裝git,使用git命令下載 git clone https://github.com/golang/net 3. 將net包,放到GOROOT路徑下 比如
      2019-09-09

    最新評論

    种子磁力搜索器