Go网络文件传输
流程分析
借助TCP完成文件的传输,基本思路如下:
- 发送方(客户端)向服务端发送文件名,服务端保存该文件名。
- 接收方(服务端)向客户端返回一个消息ok,确认文件名保存成功。
- 发送方(客户端)收到消息后,开始向服务端发送文件数据。
- 接收方(服务端)读取文件内容,写入到之前保存好的文件中。
由于文件传输需要稳定可靠的连接,所以采用TCP方式完成网络文件传输功能。
首先获取文件名。借助os包中的stat()函数来获取文件属性信息。在函数返回的文件属性中包含文件名和文件大小。Stat参数name传入的是文件访问的绝对路径。FileInfo中的Name()函数可以将文件名单独提取出来。
func Stat(name string) (fi FileInfo, err error)
Stat返回一个描述name指定的文件对象的FileInfo。如果指定的文件对象是一个符号链接,返回的FileInfo描述该符号链接指向的文件的信息,本函数会尝试跳转该链接。如果出错,返回的错误值为*PathError类型。
我们通过源码可以得知FileInfo是一个接口,要实现这个接口就必须实现这个接口的如下所有方法
实现网络文件传输实质上时借助了本地文件复制和TCP网络编程相关知识,可以先看看Go语言复制文件和Go网络编程了解相关内容。
所以关于使用TCP实现文件传输大致步骤可以归结为如下步骤
接收端:
- 创建监听 listener,程序结束时关闭。
- 阻塞等待客户端连接 conn,程序结束时关闭conn。
- 读取客户端发送文件名。保存 fileName。
- 回发“ok”。
- 封装函数 RecvFile 接收客户端发送的文件内容。传参 fileName 和 conn
- 按文件名 Create 文件,结束时 Close
- 循环 Read 发送端网络文件内容,当读到 0 说明文件读取完毕。
- 将读到的内容原封不动Write到创建的文件中
接收端代码:
package main
import (
"fmt"
"io"
"net"
"os"
)
func recvFile(conn net.Conn, fileName string) {
//按照文件名创建新文件
file, err := os.Create(fileName)
if err != nil {
fmt.Printf("os.Create()函数执行错误,错误为:%v\n", err)
return
}
defer file.Close()
//从网络中读数据,写入本地文件
for {
buf := make([]byte, 4096)
n, err := conn.Read(buf)
//写入本地文件,读多少,写多少
file.Write(buf[:n])
if err != nil {
if err == io.EOF {
fmt.Printf("接收文件完成。\n")
} else {
fmt.Printf("conn.Read()方法执行出错,错误为:%v\n", err)
}
return
}
}
}
func main() {
//1.创建监听socket
listener, err := net.Listen("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Printf("net.Listen()函数执行错误,错误为:%v\n", err)
return
}
defer listener.Close()
//阻塞监听
conn, err := listener.Accept()
if err != nil {
fmt.Printf("listener.Accept()方法执行错误,错误为:%v\n", err)
return
}
defer conn.Close()
//文件名的长度不能超过1024个字节
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
fmt.Printf("conn.Read()方法执行错误,错误为:%v\n", err)
return
}
fileName := string(buf[:n])
//回写ok给发送端
conn.Write([]byte("ok"))
//获取文件内容
recvFile(conn, fileName)
}
发送端:
- 提示用户使用命令行参数输入文件名。接收文件名 filepath(含访问路径)
- 使用 os.Stat()获取文件属性,得到纯文件名 fileName(去除访问路径)
- 主动发起连接服务器请求,结束时关闭连接。
- 发送文件名到接收端 conn.Write()
- 读取接收端回发的确认数据 conn.Read()
- 判断是否为“ok”。如果是,封装函数 SendFile() 发送文件内容。传参 filePath 和 conn
- 只读 Open 文件, 结束时Close文件
- 循环读本地文件,读到 EOF,读取完毕。
- 将读到的内容原封不动 conn.Write 给接收端(服务器)
发送端代码:
package main
import (
"fmt"
"io"
"net"
"os"
)
func sendFile(conn net.Conn, filePath string) {
//只读打开文件
file, err := os.Open(filePath)
if err != nil {
fmt.Printf("os.Open()函数执行出错,错误为:%v\n", err)
return
}
defer file.Close()
buf := make([]byte, 4096)
for {
//从本地文件中读数据,写给网络接收端。读多少,写多少
n, err := file.Read(buf)
if err != nil {
if err == io.EOF {
fmt.Printf("发送文件完毕\n")
} else {
fmt.Printf("file.Read()方法执行错误,错误为:%v\n", err)
}
return
}
//写到网络socket中
_, err = conn.Write(buf[:n])
}
}
func main() {
//获取命令行参数
list := os.Args
if len(list) != 2 {
fmt.Printf("格式为:go run xxx.go 文件名\n")
return
}
//提取文件的绝对路径
path := list[1]
//获取文件属性
fileInfo, err := os.Stat(path)
if err != nil {
fmt.Printf("os.Stat()函数执行出错,错误为:%v\n", err)
return
}
//主动发起连接请求
conn, err := net.Dial("tcp", "127.0.0.1:8000")
if err != nil {
fmt.Printf("net.Dial()函数执行出错,错误为:%v\n", err)
return
}
defer conn.Close()
//发送文件名给接收端
_, err = conn.Write([]byte(fileInfo.Name()))
//读取服务器回发数据
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil {
fmt.Printf("conn.Read(buf)方法执行出错,错误为:%v\n", err)
return
}
if string(buf[:n]) == "ok" {
//写文件内容给服务器 -- 借助conn
sendFile(conn, path)
}
}
Go网络文件传输的更多相关文章
- Linux网络编程:socket文件传输范例
基于TCP流协议的socket网络文件传输Demo: 实现:C语言功能:文件传输(可以传任何格式的文件) /********************************************** ...
- Windows网络编程:winsock文件传输范例
基于TCP流协议的winsock网络文件传输Demo: 实现:C语言 功能:文件传输(可以传任何格式的文件) /******************************************** ...
- TCP协议,UDP,以及TCP通信服务器的文件传输
TCP通信过程 下图是一次TCP通讯的时序图.TCP连接建立断开.包含大家熟知的三次握手和四次握手. 在这个例子中,首先客户端主动发起连接.发送请求,然后服务器端响应请求,然后客户端主动关闭连接.两条 ...
- linux网络环境下socket套接字编程(UDP文件传输)
今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...
- 循序渐进Java Socket网络编程(多客户端、信息共享、文件传输)
目录[-] 一.TCP/IP协议 二.TCP与UDP 三.Socket是什么 四.Java中的Socket 五.基本的Client/Server程序 六.多客户端连接服务器 七.信息共享 八.文件传输 ...
- 循序渐进Socket网络编程(多客户端、信息共享、文件传输)
循序渐进Socket网络编程(多客户端.信息共享.文件传输) 前言:在最近一个即将结束的项目中使用到了Socket编程,用于调用另一系统进行处理并返回数据.故把Socket的基础知识总结梳理一遍. 1 ...
- 【RL-TCPnet网络教程】第38章 TFTP简单文件传输基础知识
第38章 TFTP简单文件传输基础知识 本章节为大家讲解TFTP(Trivial File Transfer Protocol,简单文件传输协议)的基础知识,方便后面章节的实战操作. (本章 ...
- 【RL-TCPnet网络教程】第35章 FTP文件传输协议基础知识
第35章 FTP文件传输协议基础知识 本章节为大家讲解FTP(File Transfer Protocol,文件传输协议)的基础知识,方便后面章节的实战操作. (本章的知识点主要整理自网络) ...
- c/c++ 网络编程 文件传输
网络编程 文件传输 1,文件发送端 2,文件接收端 文件发送端: #include <iostream> #include <string.h> #include <sy ...
随机推荐
- HZOJ 星际旅行
正解欧拉路,其实看完题解还是挺简单的,由于对欧拉路这种东西没怎么接触过,所以考试时没想出来,知识还是有漏洞啊. 另外这题的题解写的也不是很清楚(可能大佬作者觉得这是一道送分题……),首先判断联通(注意 ...
- Android 高仿新浪微博底部导航栏,实现双击首页Tab,页面的ListView滚动、刷新
现在很多APP,如微信.QQ.微博等等,它们的主页面都无一例外的选择使用底部Tab导航, 通过这种方式,可以很好的把页面层级分化,很好的提高用户体验.相信,很多Android开发者,都使用到过这种经典 ...
- 自定义element树表格图标
如下图: css代码: /deep/.el-icon-arrow-right:before { content: "\e6d9"; } /deep/.el-table__expan ...
- Android中使用lambda表达式
lambda 语法简介 视频为本篇播客知识点讲解,建议采用超清模式观看, 欢迎点击订阅我的优酷 如果刚学Android,不知道怎么写点击事件可以跳转,传送门 要想在Android中使用lambda语法 ...
- 【Pandas】Pandas求某列字符串的长度,总结经验教训
测试集大小: test.shape(898, 11) 对某列的字符串做统计长度1.for遍历法:start = time.time()for i in test.index.values: test. ...
- hdu 3374 String Problem (字符串最小最大表示 + KMP求循环节)
Problem - 3374 KMP求循环节. http://www.cnblogs.com/wuyiqi/archive/2012/01/06/2314078.html 循环节推导的证明相当 ...
- svg和canvas比较以及svg简单介绍
文章地址:https://www.cnblogs.com/sandraryan/ 什么是svg 可缩放矢量图形(Scalable Vector Graphics) 1. 一种使用XML描述的2D图形语 ...
- Python--day24--复习
- PHP Warning: Module 'json' already loaded in Unknown on line 0
Zend加密文件.启动服务器后无法正常运行加密后的文件.处理方法: 某台服务器上跑着php 5.3.8,线上服务运行正常. 某天拆分php错误日志时,发现其中有一段连续的错误信息. [14-Feb ...
- 前端开发之HTML
前端 编程主要就是三部分:使用数据,存储数据和处理数据. 什么是前端: 前端就是使用数据的过程,通过规定的格式将服务端的数据在浏览器上更好的展示给用户. 前端的工具: HTML CSS 和 JavaS ...