go语言基础-----20-----TCP网络编程

2023-11-05

1 网络编程介绍

目前主流服务器一般均采用的都是”Non-Block + I/O多路复用”(有的也结合了多线程、多进程)。不过I/O多路复用也给使用者带来了不小的复杂度,以至于后续出现了许多高性能的I/O多路复用框架, 比如libevent、libev、libuv等,以帮助开发者简化开发复杂性,降低心智负担。
不过Go的设计者似乎认为I/O多路复用的这种通过回调机制割裂控制流 的方式依旧复杂,且有悖于“一般逻辑”设计,为此Go语言将该“复杂性”隐藏在Runtime中了:Go开发者无需关注socket是否是 non-block的,也无需亲自注册文件描述符的回调,只需在每个连接对应的goroutine中以“block I/O”的方式对待socket处理即可。

2 TCP socket api

Read(): 从连接上读取数据。
•Write(): 向连接上写入数据。
•Close(): 关闭连接。
•LocalAddr(): 返回本地网络地址。
•RemoteAddr(): 返回远程网络地址。
•SetDeadline(): 设置连接相关的读写最后期限。等价于同时调用SetReadDeadline()SetWriteDeadline()。 •SetReadDeadline(): 设置将来的读调用和当前阻塞的读调用的超时最后期限。即设置读数据时最大允许时间。
•SetWriteDeadline(): 设置将来写调用以及当前阻塞的写调用的超时最后期限。即设置写数据时最大允许时间。

3 TCP连接的建立

服务端是一个标准的Listen + Accept的结构,而在客户端Go语言使用net.Dial()或net.DialTimeout()进行连接建立。

下面给出go实现的简单的TCP网络编程例子。

服务端代码:

package main

import (
	"fmt"
	"net"
	_ "time"
)

func process(conn net.Conn) {
	// 1. 客户端退出要关闭通信连接。
	defer conn.Close()

	// 2. 循环读取客户端的数据。
	for {
		//创建一个新的切片
		buf := make([]byte, 1024)

		// 3. 等待客户端发送信息,如果客户端没发送,协程就阻塞Read()。
		// 问:go中的net.Conn的Read接口与Linux底层的系统调用函数read有何区别?
		// 答:go中的Read内部调用底层的read,双方没数据时都会阻塞,但是阻塞层面不一样。Read是阻塞在go语言层面,不会阻塞在调用read的时候。
		// read阻塞是Linux内核方面的阻塞。面试可能经常会问到。

		// fmt.Printf("服务器在等待客户端%v的输入\n", conn.RemoteAddr().String())
		// 可以设置读超时,但超过这个时间客户端没发送数据,那么会超时返回,我们按需进行处理。
		// conn.SetReadDeadline(time.Now().Add(time.Duration(1) * time.Second))
		n, err := conn.Read(buf) // 默认是阻塞的
		if err != nil {
			fmt.Println("服务器read err=", err)
			fmt.Println("客户端退出了")
			return
		}

		// 4. 显示客户端发送内容到服务器的终端
		fmt.Print(string(buf[:n]) + "\n")

	}
}

func main() {

	ipPort := "0.0.0.0:8888"
	fmt.Println("服务器开始监听在:", ipPort)

	// 1. Linsten监听。
	listen, err := net.Listen("tcp", ipPort)
	if err != nil {
		fmt.Println("监听失败,err=", err)
		return
	}

	// 2. 延时关闭Linsten。
	defer listen.Close() // 函数退出的时候调用

	for {
		// 3. 循环等待客户端连接
		fmt.Println("等待客户端连接...")
		conn, err := listen.Accept() // 与C的accept一样,没有新连接时,会阻塞在这里。
		if err != nil {
			fmt.Println("Accept() err=", err)
		} else {
			fmt.Printf("Accept() success con=%v,客户端Ip=%v\n", conn, conn.RemoteAddr().String())
		}

		// 4. 这里准备起个协程为客户端服务
		go process(conn)
	}

	//fmt.Printf("监听成功,suv=%v\n", listen)
}

客户端代码:

package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"strings"
)

func main() {

	// 1. 连接到服务器,并可以设置连接模式,ip和端口号。类似Linux C的Connect函数。
	conn, err := net.Dial("tcp", "127.0.0.1:8888")
	if err != nil {
		fmt.Println("client dial err=", err)
		return
	}

	// 2. 函数退出时关闭连接。
	defer conn.Close()

	// 3. 从命令行中读取数据,循环发送给服务器。
	// 获取输入IO对象,方便读取用户输入的数据。在命令行输入单行数据。
	reader := bufio.NewReader(os.Stdin)
	for {
		//从终端读取一行用户的输入,并发给服务器。每次发完会在ReadString等待用户输入数据。
		fmt.Println("请输入内容:")
		line, err := reader.ReadString('\n')
		if err != nil {
			fmt.Println("readString err=", err)
		}
		//去掉输入后的换行符
		line = strings.Trim(line, "\r\n")

		// 如果是exit,则退出客户端
		if line == "exit" {
			fmt.Println("客户端退出了")
			break
		}

		// 将line发送给服务器
		n, e := conn.Write([]byte(line))
		if e != nil {
			fmt.Println("conn.write err = ", e)
		}

		fmt.Printf("客户端发送了%d字节的数据\n", n)
	}
}

例如我在客户端往服务器发送了3行的数据,结果:
在这里插入图片描述

4 go中的net.Conn的Read接口与Linux底层的系统调用函数read有何区别?

go中的Read内部调用底层的read,双方没数据时都会阻塞,但是阻塞层面不一样。Read是阻塞在go语言层面,不会阻塞在调用read的时候。read阻塞是Linux内核方面的阻塞。面试可能经常会问到。

5 客户端连接异常情况分析

  • 1、网络不可达或对方服务未启动。
  • 2、对方服务的listen backlog满 。
  • 3、网络延迟较大,Dial阻塞并超时。

5.1 客户端连接异常-网络不可达或对方服务未启动
如果传给Dial的Addr是可以立即判断出网络不可达,或者Addr中端口对应的服务没有启动,端口未被监听,Dial会几乎立即返回错误,比如:

下面我们来测试看看 客户端连接异常-网络不可达或对方服务未启动的情况。go会报什么样的错误,方便以后我们排查错误。

首先测试Addr地址错误的情况:
服务器代码:用回上面的即可。

客户端代码:

package main

import (
	"log"
	"net"
)

func main() {
	log.Println("begin dial...")
	conn, err := net.Dial("tcp", "a:8888") // Addr错误。
	// conn, err := net.Dial("tcp", ":8888")  // 正常写法,等价于conn, err := net.Dial("tcp", "127.0.0.1:8888")。此时让服务器的8888端口不打开。
	if err != nil {
		log.Println("dial error:", err)
		return
	}
	defer conn.Close()
	log.Println("dial ok")
}

结果,客户端填服务器的地址错误时,会报如下错误:
在这里插入图片描述

测试服务器没有开启服务或者端口未被监听的情况:

服务器代码:服务器直接不运行即可。

客户端代码:

package main

import (
	"log"
	"net"
)

func main() {
	log.Println("begin dial...")
	//conn, err := net.Dial("tcp", "a:8888") // Addr错误。
	conn, err := net.Dial("tcp", ":8888")  // 正常写法,等价于conn, err := net.Dial("tcp", "127.0.0.1:8888")。此时让服务器的8888端口不打开。
	if err != nil {
		log.Println("dial error:", err)
		return
	}
	defer conn.Close()
	log.Println("dial ok")
}

结果:
在这里插入图片描述

5.2 客户端连接异常-对方服务的listen backlog满
listen backlog满,即客户端的连接数到达了服务器的最大限制。所以这里的连接数应该是指服务器未处理的连接数,因为服务器的Accept还没处理呢。(和Linux的backlog一样?Linux的backlog是指已经连接的最大连接数???我太久没用Linux C的网络编程接口了,有点忘记了。)

对方服务器很忙,瞬间有大量client端连接尝试向server建立,server端的listen backlog队列满,server accept不及时((即便服务端不accept,那么在backlog数量范畴里面,客户端的connect都会是成功的,因为new conn已经加入到server side的listen queue中了,accept只是从queue中取出一个conn而 已),这将导致client端Dial阻塞。

服务器代码:

package main

import (
	"log"
	"net"
	"time"
)

func main() {
	// 1. Linsten监听。
	l, err := net.Listen("tcp", ":8888")
	if err != nil {
		log.Println("error listen:", err)
		return
	}

	// 2. 延时关闭Linsten。
	defer l.Close()
	log.Println("listen ok")

	var i int
	for {
		// 3. 循环等待客户端连接
		time.Sleep(time.Second * 10) // 休眠,模拟来不及处理backlog.客户端一旦发送连接请求,就会放入到listen的队列中,
		// accept只是从队列里面获取。所以当客户端发送过多,到达backlog大小后(队列大小),那么客户端就会报错。
		// 这样就能模拟 客户端连接异常-对方服务的listen backlog满的情况。
		if _, err := l.Accept(); err != nil {
			log.Println("accept error:", err)
			break
		}

		i++
		log.Printf("%d: accept a new connection\n", i)
	}
}

客户端代码:

package main

import (
	"log"
	"net"
	"time"
)

// 成功连接到服务器返回通信连接,否则返回nil。
func establishConn(i int) net.Conn {
	conn, err := net.Dial("tcp", ":8888")
	if err != nil {
		log.Printf("%d: dial error: %s", i, err)
		return nil
	}

	log.Println(i, ":connect to server ok")
	return conn
}

func main() {
	// 1. 定义一个切片,用于存放连接服务器成功的客户端。
	var sl []net.Conn

	// 2. 循环像服务器请求连接,模拟大量的客户端像服务器发送连接请求。成功连接会放入切片中,失败这里则不做处理。
	for i := 1; i < 1000; i++ {
		conn := establishConn(i)
		if conn != nil {
			sl = append(sl, conn)
		}
	}

	time.Sleep(time.Second * 10000)
}

结果看到,和服务器没开启或者端口没监听的结果是一样的:
客户端结果:

  • 1)分析它,打connect to server ok的原因是,上面已经讲过了,即便服务端不accept,那么在backlog数量范畴里面,客户端的connect都会是成功的,因为new conn已经加入到server side的listen queue中了,accept只是从queue中取出一个conn而已。
  • 2)重要的是看到那个错误,即“connectex: No connection could be made because the target machine actively refused it. ”,和服务器没开启或者端口没监听的结果是一样的。
2022/02/13 18:26:37 1 :connect to server ok
2022/02/13 18:26:37 2 :connect to server ok
// 中间一直都是 connect to server ok的打印,所以省略了它。
2022/02/13 18:26:39 201 :connect to server ok
2022/02/13 18:26:41 202: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:26:43 203: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:26:45 204: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:26:47 205: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:26:49 206 :connect to server ok
2022/02/13 18:26:51 207: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:26:53 208: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:26:55 209: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:26:57 210: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:26:59 211 :connect to server ok
2022/02/13 18:27:01 212: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:27:03 213: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:27:05 214: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:27:07 215: dial error: dial tcp :8888: connectex: No connection could be made because the target machine actively refused it. 
2022/02/13 18:27:08 216 :connect to server ok

服务器打印:看时间,由于服务器10s处理一个连接,所以打印是非常少的,也没啥好分析的,主要看上面客户端的打印即可。
在这里插入图片描述

5.3 客户端连接异常-网络延迟较大,Dial阻塞并超时
如果网络延迟较大,TCP握手过程将更加艰难坎坷(各种丢包),时间消耗的自然也会更长。Dial这
时会阻塞,如果长时间依旧无法建立连接,则Dial也会返回“ getsockopt: operation timed out”错误
在连接建立阶段,多数情况下,Dial是可以满足需求的,即便阻塞一小会儿。

但对于某些程序而言,需要有严格的连接时间限定,如果一定时间内没能成功建立连接,程序可能会需要执行一段“异常”处理逻辑,为此我们就需要DialTimeout()了。

因为要模拟网络延迟,所以我们将服务器弄到Linux上面运行。
Linux安装go可以参考这篇文章:Linux系统下安装Go语言环境

服务器代码:

package main

import (
	"fmt"
	"net"
	_ "time"
)

func process(conn net.Conn) {
	// 1. 客户端退出要关闭通信连接。
	defer conn.Close()

	// 2. 循环读取客户端的数据。
	for {
		//创建一个新的切片
		buf := make([]byte, 1024)

		// 3. 等待客户端发送信息,如果客户端没发送,协程就阻塞Read()。
		// 问:go中的net.Conn的Read接口与Linux底层的系统调用函数read有何区别?
		// 答:go中的Read内部调用底层的read,双方没数据时都会阻塞,但是阻塞层面不一样。Read是阻塞在go语言层面,不会阻塞在调用read的时候。
		// read阻塞是Linux内核方面的阻塞。面试可能经常会问到。

		// fmt.Printf("服务器在等待客户端%v的输入\n", conn.RemoteAddr().String())
		// 可以设置读超时,但超过这个时间客户端没发送数据,那么会超时返回,我们按需进行处理。
		// conn.SetReadDeadline(time.Now().Add(time.Duration(1) * time.Second))
		n, err := conn.Read(buf) // 默认是阻塞的
		if err != nil {
			fmt.Println("服务器read err=", err)
			fmt.Println("客户端退出了")
			return
		}

		// 4. 显示客户端发送内容到服务器的终端
		fmt.Print(string(buf[:n]) + "\n")

	}
}

func main() {

	ipPort := "0.0.0.0:8888"
	fmt.Println("服务器开始监听在:", ipPort)

	// 1. Linsten监听。
	listen, err := net.Listen("tcp", ipPort)
	if err != nil {
		fmt.Println("监听失败,err=", err)
		return
	}

	// 2. 延时关闭Linsten。
	defer listen.Close() // 函数退出的时候调用

	for {
		// 3. 循环等待客户端连接
		fmt.Println("等待客户端连接...")
		conn, err := listen.Accept() // 与C的accept一样,没有新连接时,会阻塞在这里。
		if err != nil {
			fmt.Println("Accept() err=", err)
		} else {
			fmt.Printf("Accept() success con=%v,客户端Ip=%v\n", conn, conn.RemoteAddr().String())
		}

		// 4. 这里准备起个协程为客户端服务
		go process(conn)
	}

	//fmt.Printf("监听成功,suv=%v\n", listen)
}

客户端代码:

package main

import (
	"log"
	"net"
	"time"
)

func main() {
	log.Println("begin dial...")

	// 1. 连接到服务器
	conn, err := net.DialTimeout("tcp", "192.168.2.132:8888", 2*time.Second) // 设置超时返回。例如当因为网络卡顿超过2秒而连
	// 接不上服务器时,那么会直接返回。
	if err != nil {
		log.Println("dial error:", err)
		return
	}

	// 2. 函数退出时关闭连接。
	defer conn.Close()

	log.Println("dial ok")
}

首先模拟正常的情况下,打印结果:
服务器的打印:
在这里插入图片描述
客户端的打印:
在这里插入图片描述

然后再linux输入命令,模拟网络延迟。

// ens33使用ifconfig查看网卡。延迟不要太卡和最好不要在云服务器上执行,不然服务器会变得很卡,导致命令都执行得很慢。这里延迟3000ms,实际上已经会很卡了。
sudo tc qdisc add dev ens33 root netem delay 3000ms

服务器的打印:
在这里插入图片描述
客户端的打印:
在这里插入图片描述

添加了网络延迟后,可以看到服务器是没有收到客户端的连接请求的,导致客户端DialTimeout超时返回了。这就是服务器网络延迟造成的结果,以后看到i/o timeout要知道大概是什么原因。

6 Socket读写

Dial成功后,方法返回一个net.Conn接口类型变量值。

6.1 conn.Read的行为特点

  • 1 Socket中无数据
    连接建立后,如果对方未发送数据到socket,接收方(Server)会阻塞在Read操作上,这和前面提到的“模型”原理是一致的。执行该Read操作的goroutine也会被挂起。runtime会监视该socket,直到其有数据才会重新调度该socket对应的Goroutine完成read。
  • 2 Socket中有部分数据
    如果socket中有部分数据,且长度小于一次Read操作所期望读出的数据长度,那么Read将会成功读出这部分数据并返回,而不是等待所有期望数据全部读取后再返回。
  • 3 Socket中有足够数据
    如果socket中有数据,且长度大于等于一次Read操作所期望读出的数据长度,那么Read将会成功读出这部分数据并返回。这个情景是最符合我们对Read的期待的了:Read将用Socket中的数据将我们传入的slice填满后返回:n = 10, err = nil。
  • 4 Socket关闭
    1)有数据关闭是指在client关闭时,socket中还有server端未读取的数据。
    当client端close socket退出后,server依旧没有开始Read,10s后第一次Read成功读出了所有的数据,当第二次Read时,由于client端 socket关闭,Read返回EOF error。
    这里可能会有人问,为什么client关闭了,服务器10s后第一次还能从client读取,这是因为TCP连接是全双工的,一端发起关闭连接后,并不是马上就能关闭,有一个2ML时长,正常大约40s左右,具体需要读者自行看TCP四次挥手的知识,看双方是如何通过4次挥手断开连接的。 更完整可以看TCP三次握手、四次挥手的状态时序图。
    2)无数据关闭情形下的结果,那就是Read直接返回EOF error。
  • 5 读取操作超时
    有些场合对Read的阻塞时间有严格限制,在这种情况下,Read的行为到底是什么样的呢?在返回超时错误时,是否也同时Read了一部分数据了呢?
    答:不会出现“读出部分数据且返回超时错误”的情况。因为只有err=nil时才会读取到数据,有错误时不会读取到数据。

6.2 conn.Write的行为特点

  • 1 成功写
    前面例子着重于Read,client端在Write时并未判断Write的返回值。所谓“成功写”指的就是Write调用返回的n与预期要写入的数据长度相等,且error = nil。这是我们在调用Write时遇到的最常见的情形,这里不再举例了。
  • 2 写阻塞
    TCP连接通信两端的OS都会为该连接保留数据缓冲,一端调用Write后,实际上数据是写入到OS的协议栈的数据缓冲的。TCP是全双工通信,因此每个方向都有独立的数据缓冲。当发送方将对方的接收缓冲区以及自身的发送缓冲区写满后,Write就会阻塞。
    即不管服务器还是客户端,在程序调用Write后,都是先写到操作系统的缓冲区后,再发送到对方的。所以缓存满的话,再进行写操作时就需要阻塞。
    关于全双工的概念,可以参考我这篇文章:02LinuxC进程间通信之管道pipe概念和双向半双工概念
  • 3 写入部分数据
    Write操作存在写入部分数据的情况。没有按照预期的写入所有数据。这时候循环写入便是综上例子,虽然Go给我们提供了阻塞I/O的便利,但在调用Read和Write时依旧要综合需要方法返回的n和err的结果,以做出正确处理。net.conn实现了io.Reader和io.Writer接口,因此可以试用一些wrapper包进行socket读写,比如bufio包下面的Writer和Reader、io/ioutil下的函数等。

7 Goroutine safe

基于goroutine的网络架构模型,存在在不同goroutine间共享conn的情况,那么conn的读写是
否是goroutine safe的呢?
答:go的conn.Write、conn.Read 内部是goroutine安全的,内部都有Lock保护。

8 Socket属性

SetKeepAlive
SetKeepAlivePeriod
SetLinger
SetNoDelay (默认no delay)
SetWriteBuffer
SetReadBuffer

要使用上面的Method的,需要type assertion。例如:

tcpConn, ok := conn.(*TCPConn) 
if !ok { //error handle } 
tcpConn.SetNoDelay(true)

9 关闭连接

socket是全双工的,client和server端在己方已关闭的socket和对方关闭的socket上操作的结果有不同。
上面也说过,这涉及到TCP的四次挥手知识。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

go语言基础-----20-----TCP网络编程 的相关文章

  • Google Cloud Kubernetes 上任务队列的替代方案

    我发现任务队列主要用于App Engine标准环境 我正在将现有服务从 App Engine 迁移到 Kubernetes 任务队列的一个好的替代方案是什么 推送队列是当前正在使用的队列 我在线阅读文档并浏览了此链接 何时使用 PubSub
  • 使用cgo时的多重定义

    package main int add int a int b return a b import C import fmt func main func Test1 fmt Println C add 1 3 export Test2
  • exec git 命令拒绝重定向到 Go 中的文件

    我试图从 go 调用 git log 并将输出重定向到给定文件 cmdArgs string log numstat reverse fmt Sprintf s HEAD 89c98f5ec48c8ac383ea9e27d792c3dc77
  • 检查值是否实现接口的说明

    我读过 Effective Go 和其他类似这样的问答 golang接口合规性编译类型检查 https stackoverflow com questions 17994519 golang interface compliance com
  • 在 Go 中跟踪 HTTP 请求时指定超时

    我知道通过执行以下操作来指定 HTTP 请求超时的常用方法 httpClient http Client Timeout time Duration 5 time Second 但是 我似乎不知道在跟踪 HTTP 请求时如何执行相同的操作
  • 是否可以获取有关 Golang 中调用者函数的信息?

    是否可以获取有关 Golang 中调用者函数的信息 例如 如果我有 func foo Do something func main foo 我怎样才能得到那个foo已被呼叫来自main 我可以用其他语言实现这一点 例如在 C 中我只需要使用
  • 如何分析 VSCode 中函数的性能

    我用 C Golang 编写了一个程序 如何找到占用最高 CPU 周期的函数 目的是提高正在执行的程序的性能 2021 年 10 月 金香儿哈娜 https github com hyangah宣布 tweet https twitter
  • 匿名结构和空结构

    http play golang org p vhaKi5uVmm http play golang org p vhaKi5uVmm package main import fmt var battle make chan string
  • 模板中的 bson.ObjectId

    我有一个具有 bson ObjectId 类型的结构 例如如下所示 type Test struct Id bson ObjectId Name string Foo string 我想在 html 模板中呈现它 Name Food a h
  • GoLang ssh:尽管将其设置为 nil,但仍出现“必须指定 HosKeyCallback”错误

    我正在尝试使用 GoLang 连接到远程服务器 在客户端配置中 除了用户和密码之外 我将 HostKeyCallback 设置为 nil 以便它接受每个主机 config ssh ClientConfig User user HostKey
  • go中有memset的类似物吗?

    在 C 中 我可以使用某些值初始化数组memset https msdn microsoft com en us library aa246471 28v vs 60 29 aspx const int MAX 1000000 int is
  • 将 time.Time 转换为字符串

    我正在尝试将数据库中的一些值添加到 string在围棋中 其中一些是时间戳 我收到错误 无法在数组元素中使用 U Created date 类型 time Time 作为类型字符串 我可以转换吗time Time to string typ
  • 为什么 json.Unmarshal 返回映射而不是预期的结构?

    看看这个游乐场 http play golang org p dWku6SPqj5 http play golang org p dWku6SPqj5 基本上 我正在工作的图书馆收到了interface 作为参数 然后需要json Unma
  • 如何仅在测试时允许一个包访问另一个包的未导出数据?

    In Go 编程语言 第 11 2 4 节 有一个外部测试访问的示例fmt isSpace 通过声明IsSpace in fmt s export test go文件 这似乎是完美的解决方案 所以这就是我所做的 a a go package
  • 在 Visual Studio Code 中调试 Go 测试

    在我的 Windows 计算机上 我安装了 Visual Studio Code 要手动运行测试 我进入控制台到项目文件夹并输入 go test main test go 它工作完美 但我遇到一种情况 我需要调试我的测试以了解发生了什么 为
  • 如何从 JWT 令牌中提取声明

    我正在使用 dgrijalva jwt go 包 我想从令牌中提取有效负载 但找不到方法 示例 取自 https jwt io https jwt io 对于编码 eyJhbGciOiJIUZI1NiIsInR5cCI6IkpXVCJ9 e
  • select 语句是否保证通道选择的顺序?

    继从这个答案 https stackoverflow com a 25795236 274460 如果一个 goroutine 在两个通道上进行选择 是否保证通道的选择顺序与其发送的顺序相同 我对发送者是单线程的情况特别感兴趣 例如 是否保
  • 如何确定 go 中当前运行的可执行文件的完整路径?

    我一直在 osx 上使用这个函数 Shortcut to get the path to the current executable func ExecPath string var here os Args 0 if strings H
  • 如何修复“缺少表的 FROM 子句条目”错误

    我正在尝试根据游戏 ID 获取平台名称 我有如下三个表 我正在尝试加入它们以获得所需的结果 Games Id 1 2 3 4 Game Platforms Id game id platform id 1 1 1 2 1 2 3 3 3
  • 是否可以在 C/C++ 中模仿 Go 接口?

    在 Go 中 如果类型具有接口定义的所有方法 则可以将其分配给该接口变量 而无需显式继承它 是否可以在 C C 中模仿此功能 是的 您可以使用纯抽象类 并使用模板类来包装 实现 抽象类的类型 以便它们扩展抽象类 这是一个简单的示例 incl

随机推荐

  • sql server中bit类型在数据库和C#中的表示

    select count 1 from select ROW NUMBER over order by MinuteID as RowId from OA Minutes WHERE 1 1 and DelFlag false as tem
  • BLE 和 Zigbee 肉眼读数(更新ing)

    BLE1M 画相位图 可以读出preamble 01010101 同步码 01101011 通过向下向上的轨迹来肉眼读 原理是需要看如何进行调制的 toolbox中是GMSK调制 所以反着推回去 就知道画相位图能直接肉眼读数了 同理Zigb
  • JTest

    jtest解释 第一章 简介 jtest是parasoft公司推出的一款针对java语言的自动化白盒测试工具 它通过自动实现java的单元测试和代码标准校验 来提高代码的可靠性 Jtest先分析每个java类 然后自动生成junit测试用例
  • 论文精读:FairMOT: On the Fairness of Detection and Re-Identification in MultipleObject Tracking

    1 提出背景 以往的工作通常将re ID视为次要任务 其准确性受到主要检测任务的严重影响 因此 网络偏向于主检测任务 这对re ID任务不公平 2 核心思想 将MOT表示为单个网络中目标检测和reid的多任务学习 因为它允许两个任务的联合优
  • Ubuntu18.04 搭建Hadoop完全分布式环境--踩坑局

    安装Ubuntu18 04 暂时无坑 跟着CSDN教程 坑一 su root 无法切换root用户 提示 Authentication failure 填坑 sudo psswrd 然后当前用户密码 然后输入 root密码 确认 root
  • Linux环境下,通过shell脚本实现一键部署MySQL,并支持多种类型

    Linux环境下一键部署MySQL脚本 支持多种类型 前言 一 使用前须知 二 使用方法 三 shell脚本内容 总结 前言 MySQL是目前最流行的关系型数据库管理系统之一 属于 Oracle 旗下产品 由于它是开源软件 因此很多企业在
  • 业界AI 推理芯片比较

    业界AI 推理芯片比较 公司 创立时间 地点 产品 架构 算力 特色 瀚博半导体 2018年12月 上海 SV02 DSA 200TOPS 手机 12 导管 1
  • 普源示波器 电脑 连接 软件_乐高wedo2.0电脑软件安装及蓝牙连接方法

    先上一段官方视频 wedo2 0的软件下载方法如下 1 ipad端 直接去苹果应用商店下载 2 安卓端 先下载应用宝 再从应用宝里搜索wedo2 0下载安装 3 PC端 https education lego com zh cn down
  • Android:rk3588 kernel单编

    Android12 0不能直接烧写kernel img和resource img Android12 0的kernel img和resource img包含在boot img中 需要使用build sh AK 命令来编译 kernel 编译
  • 【问题解决】Java下载远程服务器资源到本地,本地提供下载服务,解决中文乱码问题

    Java下载远程服务器资源到本地 本地提供下载服务 1 通过远程访问远程URL获取服务资源 从指定URL下载文件并保存到指定目录 param filePath 文件将要保存的目录 param method 请求方法 包括POST和GET p
  • 计算机视觉OpenCV(四):图像梯度处理和边缘检测

    目录 图像梯度处理 1 Sobel算子 2 Scharr算子 3 Laplacian算子 4 不同算子的比较 Canny边缘检测 图像梯度处理 1 Sobel算子 dst cv2 Sobel src ddepth dx dy ksize d
  • windows7 64位机上安装配置CUDA7.5(或8.0)+cudnn5.0操作步骤

    按照官网文档 http docs nvidia com cuda cuda installation guide microsoft windows index html axzz4TpI4c8vf 进行安装 在windows7上安装cud
  • Python中自定义异常

    Python中也有关于异常的处理 导入日志模块 import logging sys 导入logging模块 logger logging getLogger 异常 创建一个logger实例 filehadler logging FileH
  • Spring Security 强制退出指定用户

    应用场景 最近社区总有人发文章带上小广告 严重影响社区氛围 好气 对于这种类型的用户 就该永久拉黑 社区的安全框架使用了 spring security 和 spring session 登录状态 30 天有效 session 信息是存在
  • 聚观早报

    今日要闻 ChatGPT 停止 Plus 付费 李子柒油管广告收益登顶热搜 亚马逊游戏部门百名员工被裁 国内一公司推出太空葬 苹果将在印度国金融中心开设零售店 ChatGPT 停止 Plus 付费 4 月 5 日消息 ChatGPT 目前已
  • 景安服务器可以用小程序吗,原来这些平台都有小程序,你全都用过吗?

    原标题 原来这些平台都有小程序 你全都用过吗 11大平台入局 小程序日活超4 4亿 陶风互联觉得 小程序用数据证明了自己的繁荣与能力 近日 小程序SaaS服务商 即速应用发布的 即速应用2020小程序年中研究分析报告 让我们再窥小程序对于互
  • Java与MySQL时间不一致问题

    文章目录 一 问题情况描述 二 CST时区混乱 1 CST有四种含义 2 什么是时区 三 绝对时间与本地时间 1 绝对时间 2 本地时间 3 时区偏移量 四 MySQL服务端时区 1 system time zone 系统时区 2 time
  • 电路设计中的磁珠作用及如何进行取值!!!!!

    PS 先品尝一下小菜 关于磁珠的使用描述不正确的是 A 磁珠的阻抗频率特性曲线 转换点频率以下 以磁珠体现电阻性 转换点所在频率以上 磁珠体现电感性 电感性的作用是反射噪声 电阻性的作用是吸收噪声并转换成热能 B 磁珠的选择应满足电路噪声的
  • QT生成XML(QXmlStreamWriter或DOM)

    话不多说 直接而看代码 效果可以自己运行看看 记得在Pro文件里加上xml模块 方式一 QXmlStreamWriter QString sXml 存储生成的XML QXmlStreamWriter xswWriter sXml xswWr
  • go语言基础-----20-----TCP网络编程

    1 网络编程介绍 目前主流服务器一般均采用的都是 Non Block I O多路复用 有的也结合了多线程 多进程 不过I O多路复用也给使用者带来了不小的复杂度 以至于后续出现了许多高性能的I O多路复用框架 比如libevent libe