Go语言学习-网络基础

简介: 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
}

总结


在网络模块的联系中,一定要注意语言的特性,使用双引号一定要是英文的,避免中文格式。

目录
相关文章
|
18天前
|
Go
go语言中的数据类型
go语言中的数据类型
13 0
|
24天前
|
Go 开发者
掌握Go语言:Go语言结构体,精准封装数据,高效管理实体对象(22)
掌握Go语言:Go语言结构体,精准封装数据,高效管理实体对象(22)
|
5天前
|
数据采集 存储 Go
使用Go语言和chromedp库下载Instagram图片:简易指南
Go语言爬虫示例使用chromedp库下载Instagram图片,关键步骤包括设置代理IP、创建带代理的浏览器上下文及执行任务,如导航至用户页面、截图并存储图片。代码中新增`analyzeAndStoreImage`函数对图片进行分析和分类后存储。注意Instagram的反爬策略可能需要代码适时调整。
使用Go语言和chromedp库下载Instagram图片:简易指南
|
24天前
|
存储 安全 Go
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
掌握Go语言:Go语言类型转换,无缝处理数据类型、接口和自定义类型的转换细节解析(29)
|
1天前
|
Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第23天】Go语言的`context`包提供`Context`接口用于处理任务取消、超时和截止日期。通过传递`Context`对象,开发者能轻松实现复杂控制流。本文解析`context`包特性,讨论常见问题和解决方案,并给出代码示例。关键点包括:1) 确保将`Context`传递给所有相关任务;2) 根据需求选择适当的`Context`创建函数;3) 定期检查`Done()`通道以响应取消请求。正确使用`context`包能提升Go程序的控制流管理效率。
6 1
|
2天前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello("Alice")`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
12 1
|
2天前
|
人工智能 Go 调度
掌握Go并发:Go语言并发编程深度解析
掌握Go并发:Go语言并发编程深度解析
|
2天前
|
SQL 关系型数据库 MySQL
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
Golang数据库编程详解 | 深入浅出Go语言原生数据库编程
|
3天前
|
Go 开发者
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
【4月更文挑战第21天】本文介绍了Go语言中的流程控制语句,包括`if`、`switch`和`for`循环。`if`语句支持简洁的语法和初始化语句,但需注意比较运算符的使用。`switch`语句提供多分支匹配,可省略`break`,同时支持不带表达式的形式。`for`循环有多种形式,如基本循环和`for-range`遍历,遍历时修改原集合可能导致未定义行为。理解并避免易错点能提高代码质量和稳定性。通过实践代码示例,可以更好地掌握Go语言的流程控制。
11 3
Golang深入浅出之-Go语言流程控制:if、switch、for循环详解
|
3天前
|
Go
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值
【4月更文挑战第21天】Go语言函数是代码组织的基本单元,用于封装可重用逻辑。本文介绍了函数定义(包括基本形式、命名、参数列表和多返回值)、调用以及匿名函数与闭包。在函数定义时,注意参数命名和注释,避免参数顺序混淆。在调用时,要检查并处理多返回值中的错误。理解闭包原理,小心处理外部变量引用,以提升代码质量和可维护性。通过实践和示例,能更好地掌握Go语言函数。
17 1
Golang深入浅出之-Go语言函数基础:定义、调用与多返回值