从核心原理理解网关的本质
网关具备的基本功能:
借助网关处理高可用,高并发
经典协议与数据包
http 协议
GET/HTTP/1.1 Host:www.baidu.com User-Agent:curl/7.55.1 Accept:*/*
Websocket握手协议
三次握手的最主要的目的是保证连接是全双工的,可靠更多的是通过重传机制来保证的
因为连接是全双工的,双方必须都收到对方的FIN包及确认才可关闭
TCP报文格式:
其中比较重要的字段有:
(1)序号(sequence number):Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
(2)确认号(acknowledgement number):Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。
(3)标志位(Flags):共6个,即URG、ACK、PSH、RST、SYN、FIN等。具体含义如下:
URG:紧急指针(urgent pointer)有效。ACK:确认序号有效。PSH:接收方应该尽快将这个报文交给应用层。RST:重置连接。SYN:发起一个新连接。FIN:释放一个连接。
需要注意的是:
不要将确认序号Ack与标志位中的ACK搞混了。确认方Ack=发起方Seq+1,两端配对。
三次握手连接:
(1)首先客户端向服务器端发送一段TCP报文,其中:
(2)服务器端接收到来自客户端的TCP报文之后,结束LISTEN阶段。并返回一段TCP报文,其中:
(3)客户端接收到来自服务器端的确认收到数据的TCP报文之后,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段。并返回最后一段TCP报文。其中:
服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。结束SYN-SENT阶段,进入ESTABLISHED阶段。
在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性。一旦出现某一方发出的TCP报文丢失,便无法继续"握手",以此确保了"三次握手"的顺利完成。
四次挥手:
(1)首先客户端想要释放连接,向服务器端发送一段TCP报文,其中:
(2)服务器端接收到从客户端发出的TCP报文之后,确认了客户端想要释放连接,随后服务器端结束ESTABLISHED阶段,进入CLOSE-WAIT阶段(半关闭状态)并返回一段TCP报文,其中:
前"两次挥手"既让服务器端知道了客户端想要释放连接,也让客户端知道了服务器端了解了自己想要释放连接的请求。于是,可以确认关闭客户端到服务器端方向上的连接了
(3)服务器端自从发出ACK确认报文之后,经过CLOSED-WAIT阶段,做好了释放服务器端到客户端方向上的连接准备,再次向客户端发出一段TCP报文,其中:
随后服务器端结束CLOSE-WAIT阶段,进入LAST-ACK阶段。并且停止在服务器端到客户端的方向上发送数据,但是服务器端仍然能够接收从客户端传输过来的数据。
(4)客户端收到从服务器端发出的TCP报文,确认了服务器端已做好释放连接的准备,结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务器端发送一段报文,其中:
为什么要客户端要等待2MSL呢?见后文。
服务器端收到从客户端发出的TCP报文之后结束LAST-ACK阶段,进入CLOSED阶段。由此正式确认关闭服务器端到客户端方向上的连接。
客户端等待完2MSL之后,结束TIME-WAIT阶段,进入CLOSED阶段,由此完成“四次挥手”。
后“两次挥手”既让客户端知道了服务器端准备好释放连接了,也让服务器端知道了客户端了解了自己准备好释放连接了。于是,可以确认关闭服务器端到客户端方向上的连接了,由此完成“四次挥手”。
与“三次挥手”一样,在客户端与服务器端传输的TCP报文中,双方的确认号Ack和序号Seq的值,都是在彼此Ack和Seq值的基础上进行计算的,这样做保证了TCP报文传输的连贯性,一旦出现某一方发出的TCP报文丢失,便无法继续"挥手",以此确保了"四次挥手"的顺利完成。
为什么客户端在TIME-WAIT阶段要等2MSL?
当客户端发出最后的ACK确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完ACK确认报文之后,会设置一个时长为2MSL的计时器。MSL指的是(最大的生命周期)Maximum Segment Lifetime:(30秒–1分钟)一段TCP报文在传输过程中的最大生命周期。2MSL即是服务器端发出为FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长。
服务器端在1MSL内没有收到客户端发出的ACK确认报文,就会再次向客户端发出FIN报文;
如果客户端在2MSL内,再次收到了来自服务器端的FIN报文,说明服务器端由于各种原因没有接收到客户端发出的ACK确认报文。客户端再次向服务器端发出ACK确认报文,计时器重置,重新开始2MSL的计时;否则客户端在2MSL内没有再次收到来自服务器端的FIN报文,说明服务器端正常接收了ACK确认报文,客户端可以进入CLOSED阶段,完成“四次挥手”。
所以,客户端要经历时长为2SML的TIME-WAIT阶段;这也是为什么客户端比服务器端晚进入CLOSED阶段的原因
func main() { //1、监听端口 listener, err := net.Listen("tcp", "0.0.0.0:9090") if err != nil { fmt.Printf("listen fail, err: %v\n", err) return } //2.建立套接字连接 for { conn, err := listener.Accept() if err != nil { fmt.Printf("accept fail, err: %v\n", err) continue } //3. 创建处理协程 go func(conn net.Conn) { defer conn.Close() //思考题:这里不填写会有啥问题? //服务端就有一个close,wait状态,客户端就有一个finally 状态 for { var buf [128]byte n, err := conn.Read(buf[:]) if err != nil { fmt.Printf("read from connect failed, err: %v\n", err) break } str := string(buf[:n]) fmt.Printf("receive from client, data: %v\n", str) } }(conn) } }
TCP为啥需要流量控制
优化步骤3到步骤4:因为网络拥塞,有24直接降到1 ,会造成堵塞
粘包、拆包表现形式
现在假设客户端向服务端连续发送了两个数据包,用packet1和packet2来表示,那么服务端收到的数据可以分为三种,现列举如下:
第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象,此种情况不在本文的讨论范围内。
第二种情况,接收端只收到一个数据包,由于TCP是不会出现丢包的,所以这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。这种情况由于接收端不知道这两个数据包的界限,所以对于接收端来说很难处理。
第三种情况,这种情况有两种表现形式,如下图。接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。这两种情况如果不加特殊处理,对于接收端同样是不好处理的。
我们知道tcp是以流动的方式传输数据,传输的最小单位为一个报文段(segment)。tcp Header中有个Options标识位,常见的标识为mss(Maximum Segment Size)指的是,连接层每次传输的数据有个最大限制MTU(Maximum Transmission Unit),一般是1500比特,超过这个量要分成多个报文段,mss则是这个最大限制减去TCP的header,光是要传输的数据的大小,一般为1460比特。换算成字节,也就是180多字节。
tcp为提高性能,发送端会将需要发送的数据发送到缓冲区,等待缓冲区满了之后,再将缓冲中的数据发送到接收方。同理,接收方也有缓冲区这样的机制,来接收数据。
发生TCP粘包、拆包主要是由于下面一些原因:
既然知道了tcp是无界的数据流,且协议本身无法避免粘包,拆包的发生,那我们只能在应用层数据协议上,加以控制。通常在制定传输数据时,可以使用如下方法:
如何获取完整的数据报文
func main() { //类比接收缓冲区 bytesBuffer := bytes.NewBuffer([]byte{}) // 发送 if err := Encode(bytesBuffer, "hello world 0 !!"); err != nil { panic(err) } if err := Encode(bytesBuffer, "hello world 1 !!"); err != nil { panic(err) } //读取 for { if bt, err := Decode(bytesBuffer); err == nil { fmt.Println(string(bt)) continue } break } }
如何获取完整的数据报文
tcp_server
func main() { //simple tcp server //1.监听端口 listener, err := net.Listen("tcp", "127.0.0.1:9090") if err != nil { fmt.Printf("tcp Listen fail,err: %v\n", err) return } //2.接受请求 for { conn, err := listener.Accept() if err != nil { fmt.Printf("tcp Accept fail,err: %v\n", err) continue } //3.创建协程 go process(conn) } } //4.创建的协程里面实现解码的功能 func process(conn net.Conn) { defer conn.Close() for { bt, err := unpack.Decode(conn) if err != nil { fmt.Printf("read from connect failed, err: %v\n", err) break } str := string(bt) fmt.Printf("receive from client, data: %v\n", str) } }
tcp_client
func main() { //1.连接tcp服务器 conn, err := net.Dial("tcp", "localhost:9090") defer conn.Close() if err != nil { fmt.Printf("connect failed, err : %v\n", err.Error()) return } //2.实现编码 unpack.Encode(conn, "hello world 0!!!") }
**unpack ** : 实现编码(encode)和解码(docode)功能
const Msg_Header = "12345678" // 编码 func Encode(bytesBuffer io.Writer, content string) error { //msg_header+content_len+content //8+4+content_len if err := binary.Write(bytesBuffer, binary.BigEndian, []byte(Msg_Header)); err != nil { return err } clen := int32(len([]byte(content))) // binary.BigEndian 大端字节实现的加密 , if err := binary.Write(bytesBuffer, binary.BigEndian, clen); err != nil { return err } if err := binary.Write(bytesBuffer, binary.BigEndian, []byte(content)); err != nil { return err } return nil } // 解码 func Decode(bytesBuffer io.Reader) (bodyBuf []byte, err error) { MagicBuf := make([]byte, len(Msg_Header)) //先读取header的大小 if _, err = io.ReadFull(bytesBuffer, MagicBuf); err != nil { return nil, err } //比较得到的header和实际的Msg_Header 是否相同 if string(MagicBuf) != Msg_Header { return nil, errors.New("msg_header error") } lengthBuf := make([]byte, 4) if _, err = io.ReadFull(bytesBuffer, lengthBuf); err != nil { return nil, err } // binary.BigEndian 大端字节实现的解密 ,得到实际数据的长度 length := binary.BigEndian.Uint32(lengthBuf) bodyBuf = make([]byte, length) if _, err = io.ReadFull(bytesBuffer, bodyBuf); err != nil { return nil, err } return bodyBuf, err }
golang 实现UDP 服务端与客户端
UDP服务端:
func main() { //1.监听端口 listen, err := net.ListenUDP("udp", &net.UDPAddr{ IP: net.IPv4(0, 0, 0, 0), Port: 9090, }) if err != nil { fmt.Printf("listen udp failed ,err:%v\n", err) return } //2.循环读取消息内容 for { var data [1024]byte n, addr, err := listen.ReadFromUDP(data[:]) if err != nil { fmt.Printf("read failed from addr :%v,err%v\n", addr, err) break } go func() { //3.回复数据 fmt.Printf("addr:%v data:%v count:%v\n", addr, string(data[:n]), n) _, err = listen.WriteToUDP([]byte("received success!"), addr) if err != nil { fmt.Printf("write failed,err :%v\n", err) return } }() } }
udp客户端
func main() { //1. 连接udp服务器 conn, err := net.DialUDP("udp", nil, &net.UDPAddr{ IP: net.IPv4(127, 0, 0, 1), Port: 9090, }) if err != nil { fmt.Printf("connect failed ,err %v\n", err) return } for i := 0; i < 100; i++ { // 2.发送数据 _, err := conn.Write([]byte("hello " + "server")) if err != nil { fmt.Printf("send data failed,err: %v\n", err) return } // 3. 接收数据 result := make([]byte, 1024) n, remoteAddr, err := conn.ReadFromUDP(result) if err != nil { fmt.Printf("read data failed,err:%v\n", err) return } fmt.Printf("receive from addr:%v data:%v\n", remoteAddr, string(result[:n])) } }
golang实现tcp的服务端和客户端
tcp 服务端
func main() { //1、监听端口 listener, err := net.Listen("tcp", "0.0.0.0:9090") if err != nil { fmt.Printf("listen fail, err: %v\n", err) return } //2.建立套接字连接 for { conn, err := listener.Accept() if err != nil { fmt.Printf("accept fail, err: %v\n", err) continue } //3. 创建处理协程 go process(conn) } } func process(conn net.Conn) { defer conn.Close() //思考题:这里不填写会有啥问题? for { var buf [128]byte n, err := conn.Read(buf[:]) if err != nil { fmt.Printf("read from connect failed, err: %v\n", err) break } str := string(buf[:n]) fmt.Printf(" from client, data: %v\n", str) } }
tcp客户端
golang实现Http的服务端和客户端
http服务端
var ( Addr = ":8000" ) // http的服务器 func main() { //1.创建路由器 mux := http.NewServeMux() // 2. 设置路由规则 mux.HandleFunc("/bye", sayBye) // 3.创建服务器 server := &http.Server{ Addr: Addr, WriteTimeout: time.Second * 3, Handler: mux, } // 4. 监听端口并提供服务 log.Println("starting httpServer at" + Addr) log.Fatal(server.ListenAndServe()) } func sayBye(w http.ResponseWriter, r *http.Request) { time.Sleep(1 * time.Second) w.Write([]byte("bye bye,this is httpserver")) }
http客户端
func main() { //1. 创建连接池 transport := &http.Transport{ DialContext: (&net.Dialer{ Timeout: 30 * time.Second, // 超时时间 KeepAlive: 30 * time.Second, //长连接时间 }).DialContext, MaxIdleConns: 100, //最大空闲连接数 IdleConnTimeout: 90 * time.Second, // 空闲超时时间 TLSHandshakeTimeout: 10 * time.Second, // tls握手超时时间 ExpectContinueTimeout: 1 * time.Second, // 100-continue 状态码超时时间 } //2. 创建客户端 client := &http.Client{ Timeout: 30 * time.Second, Transport: transport, } //3.请求数据 resp, err := client.Get("http://127.0.0.1:8000/bye") if err != nil { fmt.Println("client get url failed ", err) return } defer resp.Body.Close() //4.读取内容 b, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println("Read body failed ", err) return } fmt.Println(string(b)) }
Http 服务器源码解读
函数是一等公民
type HandleFunc func(http.ResponseWriter, *http.Request) func (f HandleFunc) ServerHTTP(w http.ResponseWriter, r *http.Request) { f(w, r) } //函数是一等公民 func main() { hf := HandleFunc(HelloHandler) resp := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", bytes.NewBuffer([]byte("test"))) hf.ServerHTTP(resp, req) b, _ := ioutil.ReadAll(resp.Body) fmt.Println(string(b)) } func HelloHandler(res http.ResponseWriter, req *http.Request) { res.Write([]byte("hello youMe ")) }