前言
GO语言网络学习,网络的底层实现原理还是通过p网络的osix api实现,net 包是实现了对于网络接口的高级封装。此文章网络模块的学习主要是对于net 包中接口的练习。
一、网络模块学习
GO语言联系net包中TCP的协议实现客户端和服务端,客户端通过命令实现文件的上传和下载。
二、功能模块
1.客户端代码
思路: 客户端,上传文件,打开文件,读取数据,然后发送数据
客户端下载文件, 在网络中获取数据,然后写入文件;
代码如下(示例):
package main import ( "flag" "fmt" "io" "log" "net" "os" "path" "strings" ) var ( up int fileName string ) const SOURCE_DIR = "E:/GoSource/tcp-trans-file/tcp-trans-file/sourceFile/" const DOWNLOAD_DIR = "E:/GoSource/tcp-trans-file/tcp-trans-file/download/" // 实现命令行参数 func getComandArgs() { flag.IntVar(&up, "up", 1, "0 下载, 1 上传") flag.StringVar(&fileName, "f", "", "文件名称") flag.Parse() } func main() { log.SetFlags(log.Llongfile) getComandArgs() // 建立网络链接,监听localhsot:9090 端口 conn, err := net.Dial("tcp", "localhost:9090") if err != nil { log.Println(err) return } defer conn.Close() // fileName 中路径中的window格式路径转换成linux 格式 fileName = strings.ReplaceAll(fileName, "\\", "/") _, fileName = path.Split(fileName) // 分割术只识别”/“, 不能够识别”\“ isUp := byte(1) if up != 1 { isUp = byte(0) } // 第一个包的格式 0:up/down 1Byte: filenamelen, 其它 filename fileInfoByte := make([]byte, 0, 1024) // 0 :len 1024 cap fileInfoByte = append(fileInfoByte, isUp, byte(len(fileName))) fileInfoByte = append(fileInfoByte, []byte(fileName)...) fileInfoByte = fileInfoByte[:1024] if _, err := conn.Write(fileInfoByte); err != nil { log.Println(err) return } if isUp == 1 { err := sendFile(conn, SOURCE_DIR+fileName) if err != nil { log.Println(err) return } } else { fmt.Printf("%s", DOWNLOAD_DIR+fileName) err := recvFile(conn, DOWNLOAD_DIR+fileName) if err != nil { log.Println(err) return } } // fmt.Println(isUp) // fmt.Println(fileInfoByte) /* data := make([]byte, 1024) copy(data, "hello server\n") number, err := conn.Write(data) if err != nil { fmt.Println(err) return } fmt.Println(number, err) data1 := make([]byte, 1024) n, err := conn.Read(data1) if err != nil { log.Println(err) return } fmt.Println(string(data1[:n])) */ } func sendFile(conn net.Conn, filePath string) (err error) { src, err := os.Open(filePath) if err != nil { log.Println(err) return } defer src.Close() buf := make([]byte, 1024) n := 0 for { n, err = src.Read(buf) if err != nil && err != io.EOF { log.Println(err) return } else { err = nil } if n == 0 { break } if _, err = conn.Write(buf[:n]); err != nil { log.Println(err) return } } return } func recvFile(conn net.Conn, filePath string) (err error) { file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Println(err) return } defer file.Close() buf := make([]byte, 1024) n := 0 for { n, err = conn.Read(buf) if err != nil && err != io.EOF { log.Println(err) return } else { err = nil } if n == 0 { break } file.Write(buf) } return }
2.服务端代码
代码如下(示例):
package main import ( "io" "log" "net" "os" ) const OP_UPLOAD = 1 const OP_DOWNLOAD = 0 const UPLOAD_DIR = "E:/GoSource/tcp-trans-file/tcp-trans-file/upload/" const SOURCE_DIR = "E:/GoSource/tcp-trans-file/tcp-trans-file/sourceFile/" func main() { log.SetFlags(log.Llongfile) listener, err := net.Listen("tcp", ":9090") if err != nil { log.Println(err) return } defer listener.Close() log.Println("listen on:", listener.Addr().String()) for { conn, err := listener.Accept() if err != nil { log.Println(err) return } log.Println("new conn:", conn.RemoteAddr()) go handle(conn) } } func handle(conn net.Conn) { defer func() { recover() log.Println("close conn", conn.RemoteAddr()) conn.Close() }() //文件的前1024个字节存放文件信息, 0: 上传还是下载 fileInfoBytes := make([]byte, 1024) _, err := conn.Read(fileInfoBytes) if err != nil { log.Println(err) return } op := int(fileInfoBytes[0]) fileNameLen := int(fileInfoBytes[1]) fileName := string(fileInfoBytes[2 : fileNameLen+2]) if op == OP_UPLOAD { err := receFile(conn, UPLOAD_DIR+fileName) if err != nil { log.Println(err) return } } else if op == OP_DOWNLOAD { err := sendFile(conn, SOURCE_DIR+fileName) if err != nil { log.Println(err) return } } /* fmt.Println(string(data[:n])) n, err = conn.Write([]byte("recv:" + string(data[:n]))) if err != nil { log.Println(err) return } */ } func receFile(conn net.Conn, filePath string) (err error) { file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0644) //file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Println(err) return } defer file.Close() buf := make([]byte, 1024) n := 0 for { n, err = conn.Read(buf) if err != nil && err != io.EOF { log.Println(err) return } else { err = nil } if n == 0 { break } file.Write(buf) } return } func sendFile(conn net.Conn, filePath string) (err error) { src, err := os.Open(filePath) if err != nil { log.Println(err) return } defer src.Close() buf := make([]byte, 1024) n := 0 for { n, err = src.Read(buf) if err != nil && err != io.EOF { log.Println(err) return } else { err = nil } if n == 0 { break } if _, err = conn.Write(buf[:n]); err != nil { log.Println(err) return } } return }
总结
在网络模块的联系中,一定要注意语言的特性,使用双引号一定要是英文的,避免中文格式。