系列文章目录
GO语言学习-网络基础
前言
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
}
总结
在网络模块的联系中,一定要注意语言的特性,使用双引号一定要是英文的,避免中文格式。