千家信息网

如何理解Go语言基础之网络编程

发表于:2024-11-25 作者:千家信息网编辑
千家信息网最后更新 2024年11月25日,本篇内容主要讲解"如何理解Go语言基础之网络编程",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何理解Go语言基础之网络编程"吧!socker编程我们所学
千家信息网最后更新 2024年11月25日如何理解Go语言基础之网络编程

本篇内容主要讲解"如何理解Go语言基础之网络编程",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何理解Go语言基础之网络编程"吧!

socker编程

我们所学的TCP和UDP,统称为Socker编程,也叫做套接字编程。

多台机器要实现互相通讯,其实是一个非常复杂的过程,底层从铺设网线,网线接口,交换机,路由器,在到规定各种协议。

再到应用层QQ,微信等软件。

如果没有一套标准,每次使用都要自己去实现,可能每个程序员都不是掉头发那么简单了!

有了Socker之后,Socker会在应用层之前,将各种繁琐的的底层操作隐藏,我们可能只需要Socker.TCP就实现了TCP协议的通讯。

Go语言TCP

TCP属于稳定的,可靠的长连接,

既然要涉及通讯,必然有两个终端,最起码一个是服务端,一个是客户端,就像我们的淘宝,我们每次打开淘宝,都要去链接它,当然,淘宝可不直接是TCP。

服务端

在Go中实现服务端,并且在服务端并发很简单,只需要将每个连接让一个协程处理即可!

代码

package main  import (     "bufio"     "fmt"     "net" )  func process(conn net.Conn) {     defer conn.Close()     for {         reader := bufio.NewReader(conn)         buf := make([]byte, 128)         n, err := reader.Read(buf)         if err != nil {             fmt.Println("数据读取失败", err)             return         }         recvStr := string(buf[:n])         fmt.Println("客户端发送过来的值:", recvStr) }  } func main() {     lister, err := net.Listen("tcp", "0.0.0.0:8008")     if err != nil {         fmt.Println("连接失败", err) }     for {         fmt.Println("等待建立连接,此时会阻塞住")         conn, err := lister.Accept() //等待建立连接         fmt.Println("连接建立成功,继续")         if err != nil {             fmt.Println("建立连接失败", err)             //继续监听下次链接             continue         }         go process(conn) } }

客户端

客户端就很简单了,相对来说是不需要并发的,只需要连接就行。

代码

package main  import (     "bufio"     "fmt"     "net"     "os" )  //客户端 func main() {     conn, err := net.Dial("tcp", "192.168.10.148:8008")     if err != nil {         fmt.Println("连接服务器失败",err) }     defer conn.Close()     inputReader:=bufio.NewReader(os.Stdin)     for{         fmt.Println(":")         input,_:=inputReader.ReadString('\n')         _, err = conn.Write([]byte(input))         if err != nil {             fmt.Println("发送成功")         } } }

执行结果

就这样,我们实现了服务端并发的处理所有客户端的请求。


粘包

我们先看一下什么是粘包。

服务端

package main  import (     "bufio"     "fmt"     "io"     "net" )  func process(conn net.Conn) {     defer conn.Close()     reader := bufio.NewReader(conn)     buf := make([]byte, 1024)     for {         n, err := reader.Read(buf)         //读完了         if err == io.EOF {             fmt.Println("读完了")             break         }         //读错了         if err != nil {             fmt.Println("数据读取失败", err)             return         }         recvStr := string(buf[:n])         fmt.Println("客户端发送过来的值:", recvStr) }  } func main() {     lister, err := net.Listen("tcp", "0.0.0.0:8008")     if err != nil {         fmt.Println("连接失败", err)         return }     defer lister.Close()     for {         fmt.Println("等待建立连接,此时会阻塞住")         conn, err := lister.Accept() //等待建立连接         fmt.Println("连接建立成功,继续")         if err != nil {             fmt.Println("建立连接失败", err)             //继续监听下次链接             continue         }         go process(conn) } }

客户端

package main  import (     "fmt"     "net" )  //客户端 func main() {     conn, err := net.Dial("tcp", "192.168.10.148:8008")     if err != nil {         fmt.Println("连接服务器失败", err) }     defer conn.Close()     for i := 0; i < 10; i++ {         sendStr := "hello world ads asdf asd fads fadsf ads ads asd asd ads "         conn.Write([]byte(sendStr))         time.Sleep(time.Second) } }

注意:18行代码睡眠了1s

执行结果


如果我注释了第18行代码


执行结果


直接都淦到一行了,what?这是啥情况,不应该跟原来一样吗???

每发送一个值,那边就接收一下,这怎么整到一块了!!!

原因

主要原因是因为我们是应用层软件,是跑在操作系统之上的软件,当我们向服务器发送一个数据时,是调用操作系统的相关接口发送的,操作系统再经过各种复杂的操作,发送到对方机器

但是操作系统有一个发送数据缓冲区,默认情况如果缓冲区是有大小的,如果缓冲区没满,是不会发送数据的,所以上述客户端在发送数据时,系统的缓冲区都没满,一直压在了操作系统的缓冲区中,最后发现没数据了,才一次都发送到服务端

但是为什么sleep(1)又管用了呢?这是因为缓冲区不止一个程序在用,1s的时间足够其他程序将缓冲区打满,然后各自发各自的数据,这也是为什么第一次操作没问题,第二次有问题,因为第二次全部都是我们客户端打满的


解决粘包

工具函数

我们将解包封包的函数封装一下

socker_sitck/stick.go

package socker_stick  import (     "bufio"     "bytes"     "encoding/binary"     "fmt" )  //Encode 将消息编码 func Encode(message string) ([]byte, error) {     length := int32(len(message))     var pkg = new(bytes.Buffer)     //写入消息头     err := binary.Write(pkg, binary.LittleEndian, length)     if err != nil {         fmt.Println("写入消息头失败", err)         return nil, err }     //写入消息实体     err = binary.Write(pkg, binary.LittleEndian, []byte(message))     if err != nil {         fmt.Println("写入消息实体失败", err)         return nil, err }     return pkg.Bytes(), nil }  //Decode解码消息 func Decode(reader *bufio.Reader) (string, error) {     //读取信息长度     lengthByte, _ := reader.Peek(4)     lengthBuff := bytes.NewBuffer(lengthByte)     var length int32     err := binary.Read(lengthBuff, binary.LittleEndian, &length)     if err != nil {         return "", err }     //BuffRead 返回缓冲区现有的可读的字节数     if int32(reader.Buffered()) < length+4 {         return "", err }     pack := make([]byte, int(4+length))     _, err = reader.Read(pack)     if err != nil {         return "", err }     return string(pack[4:]), nil }

服务端

package main  import (     "a3_course/socker_stick"     "bufio"     "fmt"     "io"     "net" )  func process(conn net.Conn) {     defer conn.Close()     reader := bufio.NewReader(conn)      for {         msg, err := socker_stick.Decode(reader)         //读完了         if err == io.EOF {             fmt.Println("读完了")             break         }         //读错了         if err != nil {             fmt.Println("数据读取失败", err)             return         }          fmt.Println("客户端发送过来的值:", msg) }  } func main() {     lister, err := net.Listen("tcp", "0.0.0.0:8008")     if err != nil {         fmt.Println("连接失败", err)         return }     defer lister.Close()     for {         fmt.Println("等待建立连接,此时会阻塞住")         conn, err := lister.Accept() //等待建立连接         fmt.Println("连接建立成功,继续")         if err != nil {             fmt.Println("建立连接失败", err)             //继续监听下次链接             continue         }         go process(conn) } }

客户端

package main  import (     "a3_course/socker_stick"     "fmt"     "net" )  //客户端 func main() {     conn, err := net.Dial("tcp", "192.168.10.148:8008")     if err != nil {         fmt.Println("连接服务器失败", err) }     defer conn.Close()     for i := 0; i < 10; i++ {         sendStr := "hello world ads asdf asd fads fadsf ads ads asd asd ads "         data, err := socker_stick.Encode(sendStr)         if err != nil {             fmt.Println("编码失败",err)             return         }         conn.Write(data)         //time.Sleep(time.Second) } }

执行结果


这次真的不管执行几次,都是这样的结果

对了,只有TCP才有粘包

Go语言UDP

UDP是一个无连接协议,客户端不会在乎服务端有没有问题,客户端只管发,通常用于实时性比较高的领域

例如直播行业

服务端

package main  import (     "fmt"     "net" )  func main() {     listen, err := net.ListenUDP("udp", &net.UDPAddr{         IP:   net.IPv4(0, 0, 0, 0),         Port: 8009, })     if err != nil {         panic(fmt.Sprintf("udp启动失败,err:%v", err)) }     defer listen.Close()     for{         var data = make([]byte,1024)         n, addr, err := listen.ReadFromUDP(data)         if err != nil {             panic(fmt.Sprintf("读取数据失败,err:%v", err))         }         fmt.Println(string(data[:n]),addr,n) } }

客户端

package main  import (     "fmt"     "net" )  func main() {     socker, err := net.DialUDP("udp", nil, &net.UDPAddr{         IP:   net.IPv4(0, 0, 0, 0),         Port: 8009, })     if err != nil {         panic(fmt.Sprintf("连接服务器失败,err:%v", err)) }     defer socker.Close()     sendStr:="你好呀"     _, err = socker.Write([]byte(sendStr))     if err != nil {         panic(fmt.Sprintf("数据发送失败,err:%v", err)) } }

执行结果


到此,相信大家对"如何理解Go语言基础之网络编程"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0