Go网络编程的学习代码示例:网络文件传输

文章目录

  • 前言
  • 代码仓库
  • 概要
  • 内容
  • 流程
  • 代码(有详细注释)
    • recv.go
    • send.go
  • 结果
  • 总结
  • 参考资料
  • 作者的话

前言

Go网络编程的学习代码示例:网络文件传输。


代码仓库

  • yezhening/Programming-examples: 编程实例 (github.com)
  • Programming-examples: 编程实例 (gitee.com)

概要

接续上一章“Go网络编程的学习代码示例:客户端/服务端(C/S)模型”内容,本章将传输的用户输入数据改为传输文件

注意:关于网络编程的代码,上一章有详细的注释,本章不再过多注释,只注释新增的网络文件传输相关内容


内容

  • 使用服务端/客户端(C/S)模型
  • 实现发送方(客户端)和接收方(服务端)的简单网络文件传输:发送方发送文件名,接收方回复响应,发送方再发送文件内容,接收方接收文件内容并保存在当前目录

网络传输文件的形式和本地传输文件类似,本质是写读文件


流程

发送方:客户端

  1. 接收用户输入,获取完整文件路径并提取文件信息:fmt.Scan()、os.Stat()
  2. 发送连接请求:net.Dial()
  3. 发送文件名:net.Write()
  4. 接收,接收方接收到文件名后的响应信息:net.Read()
  5. 发送文件内容:os.Open()、os.Read()、net.Write()、os.Close()
  6. 关闭连接:net.Close()

接收方:服务端

  1. 监听连接请求:net.Listen()
  2. 接受连接请求:net.Accept()
  3. 接收文件名:net.Read()
  4. 发送,接收到文件名后的响应信息:net.Write()
  5. 接收文件内容:os.Create()、net.Read()、os.Write()、os.Close()
  6. 关闭连接:net.Close()

代码(有详细注释)

recv.go

package main

import (
	"fmt"
	"io" // 文件相关
	"net"
	"os" // 操作系统/文件相关
)

// 五、接收文件内容
func recv_file(file_name string, conn net.Conn) {
	file, create_error := os.Create(file_name) // 在当前路径下创建文件
	// 返回值:File结构体
	if create_error != nil {
		fmt.Println("os.Create():", create_error)
		return
	}

	defer file.Close() // 延迟关闭文件

	buffer := make([]byte, 1024)
	// 循环处理
	for {
		read_count, read_error := conn.Read(buffer) // 网络读取文件,按字节
		if read_error != nil {
			// 注意:读取完成时,会读取到EOF文件结束标志,错误标志置位为“EOF”,需要进行额外判断
			if read_error == io.EOF {
				fmt.Println("接收完成:", read_error)
			} else {
				fmt.Println("net.Read():", read_error)
			}

			return // 注意:接收完成和出现错误两种情况,函数返回,其他情况持续向本地写入文件
		}
		// if n == 0 { //对端断开,出问题
		// 	fmt.Println("接收完毕2")
		// 	break
		// }
		file.Write(buffer[:read_count]) // 本地写入文件,按字节
	}
}

func main() {
	// 一、监听连接请求
	listener, listen_error := net.Listen("tcp4", "127.0.0.1:8000")
	if listen_error != nil {
		fmt.Println("net.Listen():", listen_error)
		return
	}

	// 六、2.关闭连接
	defer listener.Close()

	// 二、接受连接请求
	conn, accept_error := listener.Accept()
	if accept_error != nil {
		fmt.Println("net.Accept():", accept_error)
		return
	}

	// 六、1.关闭连接
	defer conn.Close()

	// 三、接收文件名
	buffer := make([]byte, 1024)
	read_count, read_error := conn.Read(buffer)
	if read_error != nil {
		fmt.Println("net.Read():", read_error)
		return
	}

	file_name := string(buffer[:read_count]) // 获取文件名
	// 注意:网络字节转换为字符串

	// 四、发送,接收到文件名后的响应信息
	conn.Write([]byte("ok"))

	// 五、接收文件内容
	recv_file(file_name, conn)
}

send.go

package main

import (
	"fmt"
	"io" // 文件相关
	"net"
	"os" // 操作系统/文件相关
)

// 五、发送文件内容
func send_file(file_path string, conn net.Conn) {
	file, open_error := os.Open(file_path) // 打开文件
	// 返回值:File结构体
	if open_error != nil {
		fmt.Println("os.Open():", open_error)
		return
	}

	defer file.Close() // 延迟关闭文件

	buffer := make([]byte, 1024)
	// 循环处理
	for {
		read_count, read_error := file.Read(buffer) // 本地读取文件,按字节
		if read_error != nil {
			// 注意:读取完成时,会读取到EOF文件结束标志,错误标志置位为“EOF”,需要进行额外判断
			if read_error == io.EOF {
				fmt.Println("发送完成:", read_error)
			} else {
				fmt.Println("os.Read()", read_error)
			}

			return // 注意:发送完成和出现错误两种情况,函数返回,其他情况持续向网络写入文件
		}

		conn.Write(buffer[:read_count]) // 网络写入文件,按字节
	}
}

func main() {
	// 一、1.接收用户输入,获取完整文件路径。如:C:\Users\DSHH\Desktop\temp.txt
	fmt.Print("请输入完整文件路径:")
	var file_path string
	fmt.Scan(&file_path)

	// 一、2.提取文件信息
	file_info, stat_error := os.Stat(file_path)
	// 返回值:FileInfo = fs.FileInfo接口类型
	if stat_error != nil {
		fmt.Println("os.Stat():", stat_error)
		return
	}

	// 二、发送连接请求
	conn, dial_error := net.Dial("tcp4", "127.0.0.1:8000")
	if dial_error != nil {
		fmt.Println("net.Dial():", dial_error)
		return
	}

	// 六、关闭连接
	defer conn.Close()

	//  三、发送文件名
	_, write_error := conn.Write([]byte(file_info.Name()))
	// Name()返回string,如:temp.txt,转换为byte切片在网络中传输
	if write_error != nil {
		fmt.Println("net.Write():", write_error)
		return
	}

	// 四、接收,接收方接收到文件名后的响应信息
	buffer := make([]byte, 1024)
	read_count, read_error := conn.Read(buffer)
	if read_error != nil {
		fmt.Println("net.Read():", read_error)
		return
	}

	// 五、发送文件内容
	if string(buffer[:read_count]) == "ok" { // 注意:网络接收为byte字节序列,需要转换为string类型才能作比较
		send_file(file_path, conn)
		// 注意,参数使用完整文件路径而不是文件名,因为后续需要在指定路径打开文件
	}
}

结果

send.go:

PS C:\Users\DSHH\Desktop\go_test> go run send.go
请输入完整文件路径:C:\Users\DSHH\Desktop\temp.txt
发送完成: EOF
PS C:\Users\DSHH\Desktop\go_test>

recv.go:

PS C:\Users\DSHH\Desktop\go_test> go run recv.go
接收完成: EOF
PS C:\Users\DSHH\Desktop\go_test>

总结

Go网络编程的学习代码示例:网络文件传输。


参考资料

  • 黑马程序员20个小时快速入门go语言(下)_哔哩哔哩_bilibili

作者的话

  • 感谢参考资料的作者/博主
  • 作者:夜悊
  • 版权所有,转载请注明出处,谢谢~
  • 如果文章对你有帮助,请点个赞或加个粉丝吧,你的支持就是作者的动力~
  • 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
  • 文章在认识上有错误的地方, 敬请批评指正
  • 望读者们都能有所收获

你可能感兴趣的:(Go,golang,开发语言,网络,后端,架构)