go语言工作笔记

Paulin666 · · 620 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

关闭网络连接步骤:

1. 直接关闭

2. 赋值 nil

对与网络连接相关的操作(客户端网络连接、服务器网络连接)都应该: 加锁、解锁

```bash

connect  *net.Conn  // TCP网络连接

connectMutex        sync.RWMutex      // TCP网络连接读写锁

使用时:

connectMutex.RLock()

defer connectMutex.RUnlock()

```

协程的使用:

```bash

for {

select {

case <-c.ctx.Done():

// 增加退出协程相关操作

return

default:

}

// 增加逻辑代码

// 匿名函数的使用

func(){

// ...

}()

}

```

- 注意逻辑代码和业务代码的分层处理,可以抽象的尽量抽象。

不要把底层数据直接暴露给业务代码,采用中间变量进行抽象封装

- 回调函数的使用:

1. 声明一个回调函数: type HandleCommandCallback func([]byte) error

2. 在函数中直接调用回调函数(参数、返回值要一直)

3. 在业务层具体实现回调函数的功能

- 接口的使用:

1. 声明一个接口(含有多个方法)

2. 结构体地址赋值给接口变量

3. 在结构体变量中实现接口的所有方法 func (s *structA) 接口方法 {}

## 服务器+客户端

客户端服务器代码理解:

服务器接口 + 服务器接口实例

1. 创建一个服务器实例

a. 定义服务器结构体

b. 初始化服务器结构体

c. 初始化服务器实例,初始化服务器实例时开启监听客户端的协程(新增客户端,创建客户端实例)

2. 客户端实例化

a. 定义客户端结构体

b. 初始化客户端结构体

c. 初始化客户端实例(在init()中进行命令和函数的对应注册)

- 运行过程

  1. 在NewDataServer()中进行服务器初始化 server.init()

  2. 在服务器初始化 server.init()中进行客户端接入监听 go s.accept()

  3. 在客户端接入监听中增加客户端 s.addClient(connect)

  4. 增加客户端函数中增加数据客户端 NewDataClient(...)

  5. NewDataClient(...)中进行客户端初始化 client.init()

  6. 客户端初始化 client.init()中进行 “命令-函数” 注册,以及开启消息接收 go c.receive()

- 在服务器Receive()函数中,通过协程来调用客户端的Receive()

go func(client DataClient, contentChan chan CommandContent, errChan chan error) {

  clientContent, err := client.Receive(timeoutCtx)

  ...

  }(client, s.receiveClientChannel, errChan)

--------------------------------------------

初始化全局变量通道的写法???

var a sync.Once

a.DO()...

2. 服务器接收时,进行Receive(),接收到的是客户端利用通道传递过来的数据(或者错误信息)

map的高级用法:

判断一个map[i]值是否存在?

var abc map[int]string

a, b := abc[2]

fmt.Printf("a:%v \n", a)

fmt.Printf("b:%v \n", b)

输出结果:

a:

b:false

通过判断b值可以判断map[i]的值是否存在

----------------------------------

for {

select {

case <-c.ctx.Done():  // 客户端提前退出情况

return content, fmt.Errorf("客户端提前退出")

case <-timeoutCtx.Done(): // 定时时间到情况(一般会有一个定时变量:timeoutCtx context.Context,赋值2~4秒)

return content, ErrReceiveTimeout

case content = <-c.receiveCommandChannel: // 接收到通道数据情况(没有接收到,会一直阻塞,所以需要for循环)

return content, nil

default:

// TODO: 记录信息等待图片超时处理

time.Sleep(20 * time.Millisecond) // 每隔20毫秒执行一次for循环

}

}

带超时控制:

1. ctx context.Context

2. timeoutCtx, timeoutCancel := context.WithTimeout(ctx, captureImageMaxWaitTime)

  defer timeoutCancel()

3. timeoutCtx.Done()

--------------------------

网络正常错误???

if nerr, ok := err.(net.Error); ok && nerr.Timeout() {

return

}

------------------------

如何关闭协程?

<https://www.jianshu.com/p/79d27f200bcf>

<https://blog.csdn.net/m0_37579159/article/details/79257397>

1. 当协程数量较少时:

在协程函数(go fun()) func()中,加入 for-select 函数

(在协程函数中加入for-select格式是为了及时的关闭协程)

2. 当协程数量较多时,使用WaitGroup

---------------------------

##多包数据解析:

1.  根据索引值i,遍历查找包头(注意包头的判定条件),找到了包头,将数据开始(dataStartIndex)和数据结束(dataEndIndex)位置标识出来。

2.  判断包中的命令是否是多包指令?

- 不是多包指令

- 直接存储相关数据

将数据内容  dataEndIndex-dataStartIndex  保存到  data[:]  中,

将首包找到的位置置位

将索引 i 值 添加这个包的长度( i = i + 包头+包体+包尾)

- 是多包指令:

- 找多包尾(注意多包尾判断条件)

- 找到了多包尾:

解析完整的多包数据(函数调用),解析完成,将索引值 i 添加整个多包数据长度,接着继续查找

- 没有找到多包尾

直接将索引值 i++,继续查找

-----

## 常用软件

ThinkCamWorkstation

secureCRT

FileZilla

------

从二进制流中获取数据:

content []byte

var header CommandHeader

headerReader := bytes.NewReader(content[0:binary.Size(header)])

err := binary.Read(headerReader, binary.BigEndian, &header)

if err != nil {

return headerInfo, fmt.Errorf("解析命令信息头二进制数据失败(%s)", err.Error())

}

写数据到二进制流中:

强制将二进制数据写入:

header.DataLength = uint16(binary.BigEndian.Uint16(content[i+10 : i+12]))

var responseContent = new(bytes.Buffer)

// 参考蓝卡通信协议文档,计算机收到完整的识别结果文件数据包后反馈"流水号+128"

binary.Write(responseContent, binary.LittleEndian, packet.SequenceNumber+128)

response, err := BuildCommandContent(RecognitionResultFile, packet.DeviceID, responseContent.Bytes())

if err != nil {

  return fmt.Errorf("构建命令反馈指令内容失败(%s)", err.Error())

}

-----

单例模式

var captureImageBuffer map[string]chan bool // 抓拍图片缓存

var initCaptureImageBuffer sync.Once        // 初始化抓拍缓存

initCaptureImageBuffer.Do(func() {

    captureImageBuffer = make(map[string]chan bool) // 创建抓拍图片缓存

})

-------

map + 通道的用法

------

### 类型断言

类型断言的使用?

[go语言的类型断言(Type Assertion)](https://www.jianshu.com/p/6a46fc7b6e5b)

格式: x.(T) 检查x的动态类型是否是T,其中x必须是接口值。

1. T是具体类型

2. T是接口类型

----

// 忽略正常的读取超时错误

if nerr, ok := err.(net.Error); ok && nerr.Timeout() {

    return nil

}

//上面代码功能:判断err是否是net.Error接口类型中的Timeout()类型

[go语言的错误处理模式](https://ethancai.github.io/2017/12/29/Error-Handling-in-Go/)

---

// 设置读取超时时间

err := s.connect.SetReadDeadline(time.Now().Add(200 * time.Millisecond))

if err != nil {

logger.Error("设置设备搜索反馈超时时间失败(%s)", err.Error())

return nil

}

---

代码质量的评判标准:

“高内聚、低耦合”是衡量公共库质量的一个重要方面,如果某种代码实现方式,增加了公共库和调用代码的耦合性,让模块之间产生了依赖,则比较糟糕

---

-----

// 几个常用 golang 包

1. net

2. strings

3. strconv

4. encode/json

5. convert

-------

[Go的json解析:Marshal与Unmarshal](https://www.jianshu.com/p/3534532e06ed)

-------

go 语言自定义错误的用法 ???

go 语言高级用法???

----

为什么要传结构体指针变量,不传结构体变量???

理由: 两者实现的效果一致,但是指针更快,且能节省内存空间,所以能传结构体变量的地方,尽量用结构体指针变量代替

------

### 概念理解

什么是穿透???

什么是实时透传?


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:Paulin666

查看原文:go语言工作笔记

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

620 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传