Heim >Backend-Entwicklung >Golang >Ein Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen
我们所学的TCP
TCP
和UDP
,统称为Socker
和UDP
,统称为 Sockker
编程 , 也 叫做 套 接字 编程 编程
。 。 。 。 。 。 编程 , 也 叫做 叫做 套 编程 。
🎜🎜 🎜🎜🎜。🎜🎜🎜🎜多台🎜🎜🎜机器🎜🎜🎜要实现🎜🎜🎜互相通讯🎜🎜🎜底层从🎜🎜🎜铺设网线🎜🎜🎜,🎜🎜🎜网线接口🎜🎜🎜,🎜🎜 🎜交换机🎜🎜🎜,🎜🎜🎜路由器🎜🎜🎜,在到规定🎜🎜🎜各种协议🎜🎜🎜。🎜🎜Gehen Sie zur AnwendungsebeneQQ
QQ
,微信
等软件。
如果没有一套标准,每次使用都要自己去实现,可能每个程序员都不是掉头发那么简单了!
有了Socker
之后,Socker
会在应用层之前,将各种繁琐的的底层操作隐藏,我们可能只需要Socker.TCP
就实现了TCP
, WeChat
🎜if Es gibt keine Reihe von Standards. Sie müssen es jedes Mal selbst implementieren, wenn Sie es verwenden. Vielleicht ist es nicht so einfach, dass jeder Programmierer seine Haare verliert. 🎜🎜
🎜🎜 Verstanden🎜🎜 Socker
Nach 🎜🎜, 🎜🎜Socker
🎜🎜 verbirgt verschiedene mühsame zugrunde liegende Vorgänge vor der Anwendungsschicht. Wir benötigen möglicherweise nur 🎜🎜Socker.TCP
🎜🎜 wird erreicht🎜🎜TCP
🎜🎜 Protokollkommunikation. 🎜🎜🎜
TCP ist eine stabile und zuverlässige lange Verbindung
Da es sich um Kommunikation handelt, müssen zwei Terminals vorhanden sein, mindestens eines ist der Server. Einer ist der Kunde , genau wie unser Taobao. Jedes Mal, wenn wir Taobao öffnen, müssen wir natürlich nicht direkt darauf verlinken TCP
.
Implementieren Sie den Server in Go, und die Parallelität auf dem Server ist sehr einfach, Sie müssen nur jede Verbindung einer Coroutine überlassen!
Code
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) } }
Der Client ist sehr einfach. Relativ gesehen erfordert er keine Parallelität, sondern nur eine Verbindung.
代码
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
Wenn ich die 18. Codezeile auskommentiere
direkt zu einer Zeile hinzugefügt, was? Was passiert? Sollte es nicht dasselbe sein wie zuvor? ? ?
Jedes Mal, wenn ein Wert gesendet wird, wird er dort empfangen. Wie kann dies in einem Stück integriert werden? ! !
Der Hauptgrund ist, dass wir eine AnwendungsschichtSoftware sind, also Software, die auf dem Betriebssystem läuft Der Server wird gesendet, indem die zugehörige Schnittstelle des Betriebssystems aufruft. Das Betriebssystem durchläuft dann verschiedene komplexe Vorgänge und sendet es an die andere Maschine
Wenn der Puffer eine bestimmte Größe hat, werden die Daten standardmäßig nicht gesendet, wenn der obige Client Daten sendet wird immer im Betriebssystempuffer gedrückt, schließlich wurde festgestellt, dass keine Daten vorhanden waren, sodass sie auf einmal an den Server gesendet wurden
Aber warumsleep(1)
sleep(1)
又管用了呢?这是因为缓冲区不止一个程序在用,1s的时间足够其他程序将缓冲区打满,然后各自发各自的数据,这也是为什么第一次操作没问题,第二次有问题,因为第二次全部都是我们客户端打满的
我们将解包封包的函数封装一下
socker_sitck/stick.go
Das liegt daran, dass der Puffer von mehr als einem Programm verwendet wird. 1s ist genug Zeit für andere Programme, um den Puffer zu füllen und dann ihre eigenen Daten zu senden Erstens gab es beim ersten Vorgang kein Problem, aber beim zweiten Mal gab es ein Problem, weil beim zweiten Mal alles vollständig auf unserem Client geladen war
🎜Wir werden die Funktion des Entpackens des Pakets kapseln🎜🎜🎜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
才有粘包
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)) } }
本次章节我们讲述了什么是TCP,什么是UDP。
并且编写了代码如何实现TCP服务端,TCP客户端,UDP服务端,UDP客户端。
讲述了为什么会出现粘包,该怎么解决粘包。
Das obige ist der detaillierte Inhalt vonEin Artikel, der Ihnen hilft, die Grundlagen der Go-Sprachnetzwerkprogrammierung zu verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!