建网站公司汽车六万公里是否累变速箱油,如何查询网站是织梦做的,网站建设开发哪个好学,二个字最吉利最旺财的公司名在日常的开发与运维中#xff0c;文件传输工具是不可或缺的利器。无论是跨服务器传递配置文件#xff0c;还是快速从一台机器下载日志文件#xff0c;一个高效、可靠且简单的文件传输工具能够显著提高工作效率。今天#xff0c;我想分享我自己手撸一个文件传输工具的全过程…在日常的开发与运维中文件传输工具是不可或缺的利器。无论是跨服务器传递配置文件还是快速从一台机器下载日志文件一个高效、可靠且简单的文件传输工具能够显著提高工作效率。今天我想分享我自己手撸一个文件传输工具的全过程包括需求场景、技术点分析以及实现的编码过程。
为什么要搞这个
在我的日常学习和工作中经常遇到以下几个需求场景
1跨服务器的文件共享因为我们目前有两台服务器有时需要在服务器之间同步配置文件或共享资源。
2简单的文件传输对于某些单文件传输的任务现有工具配置较复杂使用成本较高。希望能有一个简易的工具不依赖复杂的配置和额外的库。
3文件列表管理与下载需要方便地列出文件存储目录中的内容并支持选择性下载某些文件。
技术点分析
为了满足上述需求我需要实现一个功能完整的文件传输工具。以下是一些关键技术点和设计思路
TCP通信
使用 TCP 协议构建文件传输工具确保传输的可靠性。实现客户端和服务器的双向通信用于文件传输、列表查询等功能。
文件传输机制
通过 TCP 套接字发送和接收文件内容。客户端在上传文件时需要传递文件元信息文件名和大小以便服务器正确创建文件。
命令解析
支持多种命令如 LIST、UPLOAD 和 DOWNLOAD让工具更易扩展。
进度条展示
使用第三方库 pb 在终端展示传输进度条提升用户体验。
工具开发过程
接下来我将分享从零开始实现这个工具的完整过程。
首先的整体的机制服务器监听一个固定的端口接受客户端的 TCP 连接。根据客户端发送的命令执行不同的操作在Upload操作时可以指定操作类型比如查询文件列表等。 文件传输服务端
需要支持命令启动而且在启动时能够指定端口号、文件保存的目录等所以就需要使用Go语言的cobra插件。
main.go文件
var rootCmd cobra.Command{Use: ,Run: func(cmd *cobra.Command, args []string) {fmt.Println(Running myapp...)},
}func main() {rootCmd.AddCommand(ClientCmd())rootCmd.AddCommand(ServerCmd())if err : rootCmd.Execute(); err ! nil {panic(err)}
}server命令代码实现
先定义命令参数和默认值比如port, webport代表要启动的TCP服务端口号和Web服务端口号path, secret代表文件的保存路径和将来可能要做安全机制的密码功能。
const (DefaultPathDir tmpDefaultServerPort 8088DefaultWebPort 8089
)var (port, webport intpath, secret string
)主要方法
func ServerCmd() *cobra.Command {command : cobra.Command{Use: server,Run: func(cmd *cobra.Command, args []string) {quit : make(chan os.Signal, 1)signal.Notify(quit, os.Interrupt, syscall.SIGTERM)initCommand()go func() {runHttpServer()runServer(port, path)}()-quit},}command.Flags().StringVarP(path, path, , , path to serve)command.Flags().StringVarP(secret, secret, , , path to serve)command.Flags().IntVarP(port, port, , 0, path to serve)command.Flags().IntVarP(webport, webport, , 0, path to serve)return command
}支持TCP文件上传的主要逻辑
func runServer(port int, savePath string) {listener, err : net.Listen(tcp, fmt.Sprintf(:%d, port))if err ! nil {fmt.Println(Error starting server:, err)return}defer func() {_ listener.Close()}()fmt.Println(Server is listening on port, port)for {conn, err : listener.Accept()if err ! nil {fmt.Println(Connection error:, err)continue}go handleConnection(conn, savePath)}
}func handleConnection(conn net.Conn, savePath string) {defer func() {_ conn.Close()}()reader : bufio.NewReader(conn)// 读取文件元信息文件名和文件大小meta, err : reader.ReadString(\n)if err ! nil {fmt.Println(Error reading file metadata:, err)return}meta strings.TrimSpace(meta) // 清除换行符parts : strings.Split(meta, |)if len(parts) ! 2 {fmt.Println(Invalid metadata received)return}fileName : parts[0]fileSize : 0_, err fmt.Sscanf(parts[1], %d, fileSize)if err ! nil {fmt.Println(Error parsing file size:, err)return}// 确保保存路径存在fullPath : filepath.Join(savePath, fileName)if err os.MkdirAll(filepath.Dir(fullPath), 0755); err ! nil {fmt.Println(Error creating directories:, err)return}// 创建文件f, err : os.Create(fullPath)if err ! nil {fmt.Println(Error creating file:, err)return}defer func() {_ f.Close()}()// 创建进度条bar : pb.Start64(int64(fileSize))bar.Set(pb.Bytes, true)defer bar.Finish()// 读取数据并写入文件proxyReader : bar.NewProxyReader(reader)if _, err io.Copy(f, proxyReader); err ! nil {fmt.Println(Error saving file:, err)return}fmt.Println(File received:, fullPath)
}启动服务端
go build ./FTransferor server --path filepath --port 8081 后续会考虑支持的功能
1文件列表功能通过读取服务器的文件保存目录列出所有文件。如使用 LIST 命令返回文件名列表。
2文件下载功能客户端通过 DOWNLOAD filename 命令请求文件。服务端确认文件存在后先发送文件大小然后传输文件内容。
文件传输客户端
客户端主要就是能够接收命令参数找到服务端、指定自己要上传的文件然后就是支持文件上传。
参数定义
server为将要上传的服务端地址file为要进行上传的文件名次action参数为客户端的操作如LIST、DOWNLOAD等后续会进行扩展。
var (server, file, action string
)主要的client方法
func ClientCmd() *cobra.Command {command : cobra.Command{Use: cli,Run: func(cmd *cobra.Command, args []string) {startClient(server, file)},}command.Flags().StringVarP(server, server, , , path to serve)command.Flags().StringVarP(file, file, , , path to serve)command.Flags().StringVarP(action, action, , , path to serve)return command
}文件上传方法 func startClient(serverAddr string, filePath string) {f, err : os.Open(filePath)if err ! nil {fmt.Println(Error opening file:, err)return}defer func() {_ f.Close()}()// 获取文件信息fileInfo, err : f.Stat()if err ! nil {fmt.Println(Error getting file info:, err)return}conn, err : net.Dial(tcp, serverAddr)if err ! nil {fmt.Println(Error connecting to server:, err)return}defer func() {_ conn.Close()}()// 发送文件元信息文件名|文件大小meta : fmt.Sprintf(%s|%d\n, fileInfo.Name(), fileInfo.Size())if _, err conn.Write([]byte(meta)); err ! nil {fmt.Println(Error sending metadata:, err)return}// 创建进度条bar : pb.Start64(fileInfo.Size())bar.Set(pb.Bytes, true)defer bar.Finish()// 发送文件数据proxyWriter : bar.NewProxyWriter(conn)if _, err io.Copy(proxyWriter, f); err ! nil {fmt.Println(Error sending file:, err)return}fmt.Println(File sent successfully!)
}实现效果
1工具编译
拿到代码后我们需要在相关操作系统编译成可执行文件需要go1.23以上的版本命令
go mod tidy go build2启动服务端
最简单的启动方式就是直接使用默认参数
./FTransferor server当然也可以指定参数比如指定端口号为9910文件的保存路径为当前目录下的yankaka目录如果没有该目录会自动创建
./FTransferor server --port 9910 --path yankaka3使用客户端
使用客户端就需要输入必要的参数了因为TCP是点对点链接客户端必须要有一个连接目标下面就上传一个文件看下效果
./FTransferor cli --server yankaka.chat:9910 --file go1.23.3.linux-amd64.tar.gz 此时会显示一个进度条就是文件上传的进度和上传速度 服务端也会显示并在上传成功后有提示 上传完成后看下目标目录的相关文件是否存在 发现是存在并且正常的至此我们这款文件上传工具的基本功能就已经实现了
收获与总结
通过手撸这个文件传输工具我对TCP编程有了更深刻的理解其实有些功能并不需要利用HTTP、RPC等上层协议进行实现更别说各种各样的框架了最简单的功能往往TCP协议就足够了。