小山儿 發表於 2019-7-7 19:49:00

Go语言 之TCP文件传输

<p><span style="font-size: 15px">服务端实现流程大致如下:</span></p>
<ol>
<li><span style="font-size: 15px">创建监听listener,程序结束时关闭。</span></li>
<li><span style="font-size: 15px">阻塞等待客户端连接,程序结束时关闭conn。</span></li>
<li><span style="font-size: 15px">读取客户端发送文件名。保存fileName。</span></li>
<li><span style="font-size: 15px">回发“ok”给客户端做应答</span></li>
<li><span style="font-size: 15px">封装函数 RecvFile接收客户端发送的文件内容。传参fileName&nbsp;和conn</span></li>
<li><span style="font-size: 15px">按文件名Create文件,结束时Close</span></li>
<li><span style="font-size: 15px">循环Read客户端发送的文件内容,当读到EOF说明文件读取完毕。</span></li>
<li><span style="font-size: 15px">将读到的内容原封不动Write到创建的文件中</span></li>
</ol>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">package main

import (
    </span>"fmt"
    "net"
    "os"
    "runtime"<span style="color: rgba(0, 0, 0, 1)">
)

func Handler(conn net</span>.<span style="color: rgba(0, 0, 0, 1)">Conn) {
    buf </span>:= make([]byte, 2048<span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">读取客户端发送的内容</span>
    n, err := conn.<span style="color: rgba(0, 0, 0, 1)">Read(buf)
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
      fmt</span>.<span style="color: rgba(0, 0, 0, 1)">Println(err)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
    }
    fileName </span>:= <span style="color: rgba(0, 0, 255, 1)">string</span>(buf[:<span style="color: rgba(0, 0, 0, 1)">n])
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取客户端ip+port</span>
    addr := conn.RemoteAddr().<span style="color: rgba(0, 0, 255, 1)">String</span><span style="color: rgba(0, 0, 0, 1)">()
    fmt</span>.Println(addr + ": 客户端传输的文件名为--" +<span style="color: rgba(0, 0, 0, 1)"> fileName)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">告诉客户端已经接收到文件名</span>
    conn.Write([]byte("ok"<span style="color: rgba(0, 0, 0, 1)">))
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建文件</span>
    f, err := os.<span style="color: rgba(0, 0, 0, 1)">Create(fileName)
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
      fmt</span>.<span style="color: rgba(0, 0, 0, 1)">Println(err)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
    }
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">循环接收客户端传递的文件内容</span>
    <span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> {
      buf </span>:= make([]byte, 2048<span style="color: rgba(0, 0, 0, 1)">)
      n</span>, _ := conn.<span style="color: rgba(0, 0, 0, 1)">Read(buf)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">结束协程</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(0, 0, 255, 1)">string</span>(buf[:n]) == "finish"<span style="color: rgba(0, 0, 0, 1)"> {
            fmt</span>.Println(addr + ": 协程结束"<span style="color: rgba(0, 0, 0, 1)">)
            runtime</span>.<span style="color: rgba(0, 0, 0, 1)">Goexit()
      }
      f</span>.Write(buf[:<span style="color: rgba(0, 0, 0, 1)">n])
    }
    defer conn</span>.<span style="color: rgba(0, 0, 0, 1)">Close()
    defer f</span>.<span style="color: rgba(0, 0, 0, 1)">Close()
}

func main() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建tcp监听</span>
    listen, err := net.Listen("tcp", ":8000"<span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
      fmt</span>.<span style="color: rgba(0, 0, 0, 1)">Println(err)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
    }
    defer listen</span>.<span style="color: rgba(0, 0, 0, 1)">Close()

    </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">阻塞等待客户端</span>
      conn, err := listen.<span style="color: rgba(0, 0, 0, 1)">Accept()
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
            fmt</span>.<span style="color: rgba(0, 0, 0, 1)">Println(err)
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建协程</span>
<span style="color: rgba(0, 0, 0, 1)">      go Handler(conn)
    }
}</span></pre>
</div>
<p><span style="font-size: 15px">客户端实现流程大致如下:</span></p>
<ol>
<li><span style="font-size: 15px">提示用户输入文件名。接收文件名path(含访问路径)</span></li>
<li><span style="font-size: 15px">使用os.Stat()获取文件属性,得到纯文件名(去除访问路径)</span></li>
<li><span style="font-size: 15px">主动连接服务器,结束时关闭连接</span></li>
<li><span style="font-size: 15px">给接收端(服务器)发送文件名conn.Write()</span></li>
<li><span style="font-size: 15px">读取接收端回发的确认数据conn.Read()</span></li>
<li><span style="font-size: 15px">判断是否为“ok”。如果是,封装函数SendFile() 发送文件内容。传参path和conn</span></li>
<li><span style="font-size: 15px">只读Open文件, 结束时Close文件</span></li>
<li><span style="font-size: 15px">循环读文件,读到EOF终止文件读取</span></li>
<li><span style="font-size: 15px">将读到的内容原封不动Write给接收端(服务器)</span></li>
</ol>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">package main

import (
    </span>"fmt"
    "io"
    "net"
    "os"<span style="color: rgba(0, 0, 0, 1)">
)

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">发送文件到服务端</span>
func SendFile(filePath <span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 128, 128, 1)">fileSize</span> int64, conn net.<span style="color: rgba(0, 0, 0, 1)">Conn) {
    f</span>, err := os.<span style="color: rgba(0, 0, 0, 1)">Open(filePath)
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
      fmt</span>.<span style="color: rgba(0, 0, 0, 1)">Println(err)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
    }
    defer f</span>.<span style="color: rgba(0, 0, 0, 1)">Close()
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> <span style="color: rgba(0, 128, 128, 1)">count</span><span style="color: rgba(0, 0, 0, 1)"> int64
    </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> {
      buf </span>:= make([]byte, 2048<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">读取文件内容</span>
      n, err := f.<span style="color: rgba(0, 0, 0, 1)">Read(buf)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> err != nil &amp;&amp; io.EOF ==<span style="color: rgba(0, 0, 0, 1)"> err {
            fmt</span>.Println("文件传输完成"<span style="color: rgba(0, 0, 0, 1)">)
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">告诉服务端结束文件接收</span>
            conn.Write([]byte("finish"<span style="color: rgba(0, 0, 0, 1)">))
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
      }
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">发送给服务端</span>
      conn.Write(buf[:<span style="color: rgba(0, 0, 0, 1)">n])

      </span><span style="color: rgba(0, 128, 128, 1)">count</span> +=<span style="color: rgba(0, 0, 0, 1)"> int64(n)
      sendPercent </span>:= float64(<span style="color: rgba(0, 128, 128, 1)">count</span>) / float64(<span style="color: rgba(0, 128, 128, 1)">fileSize</span>) * 100<span style="color: rgba(0, 0, 0, 1)">
      value </span>:= fmt.<span style="color: rgba(0, 128, 128, 1)">Sprintf</span>("%.2f",<span style="color: rgba(0, 0, 0, 1)"> sendPercent)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">打印上传进度</span>
      fmt.Println("文件上传:" + value + "%"<span style="color: rgba(0, 0, 0, 1)">)
    }
}

func main() {
    fmt</span>.<span style="color: rgba(0, 0, 255, 1)">Print</span>("请输入文件的完整路径:"<span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建切片,用于存储输入的路径</span>
    <span style="color: rgba(0, 0, 255, 1)">var</span> str <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">
    fmt</span>.Scan(&amp;<span style="color: rgba(0, 0, 0, 1)">str)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">获取文件信息</span>
    fileInfo, err := os.<span style="color: rgba(0, 128, 128, 1)">Stat</span><span style="color: rgba(0, 0, 0, 1)">(str)
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
      fmt</span>.<span style="color: rgba(0, 0, 0, 1)">Println(err)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
    }
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建客户端连接</span>
    conn, err := net.Dial("tcp", ":8000"<span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
      fmt</span>.<span style="color: rgba(0, 0, 0, 1)">Println(err)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
    }
    defer conn</span>.<span style="color: rgba(0, 0, 0, 1)">Close()
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">文件名称</span>
    fileName := fileInfo.<span style="color: rgba(0, 0, 0, 1)">Name()
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">文件大小</span>
    <span style="color: rgba(0, 128, 128, 1)">fileSize</span> := fileInfo.<span style="color: rgba(0, 0, 0, 1)">Size()
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">发送文件名称到服务端</span>
    conn.<span style="color: rgba(0, 0, 0, 1)">Write([]byte(fileName))
    buf </span>:= make([]byte, 2048<span style="color: rgba(0, 0, 0, 1)">)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">读取服务端内容</span>
    n, err := conn.<span style="color: rgba(0, 0, 0, 1)">Read(buf)
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
      fmt</span>.<span style="color: rgba(0, 0, 0, 1)">Println(err)
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
    }
    revData </span>:= <span style="color: rgba(0, 0, 255, 1)">string</span>(buf[:<span style="color: rgba(0, 0, 0, 1)">n])
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> revData == "ok"<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">发送文件数据</span>
      SendFile(str, <span style="color: rgba(0, 128, 128, 1)">fileSize</span>,<span style="color: rgba(0, 0, 0, 1)"> conn)
    }
}</span></pre>
</div>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/yang-2018/p/11147418.html
頁: [1]
查看完整版本: Go语言 之TCP文件传输