


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. 🎜🎜🎜
Go-Sprache TCP
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
.
Server
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) } }
Client
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
wird das Ausführungsergebnis
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 Grund
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
🎜 Klebetaschen lösen🎜
🎜Tool function🎜
🎜Wir werden die Funktion des Entpackens des Pakets kapseln🎜🎜< p cid="n50" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;Schriftfamilie: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size: 16px;text-align: start;">🎜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)) } }
执行结果
总结
本次章节我们讲述了什么是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!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen



In der Bibliothek, die für den Betrieb der Schwimmpunktnummer in der GO-Sprache verwendet wird, wird die Genauigkeit sichergestellt, wie die Genauigkeit ...

Das Problem der Warteschlange Threading In Go Crawler Colly untersucht das Problem der Verwendung der Colly Crawler Library in Go -Sprache. Entwickler stoßen häufig auf Probleme mit Threads und Anfordern von Warteschlangen. � ...

Welche Bibliotheken in GO werden von großen Unternehmen oder bekannten Open-Source-Projekten entwickelt? Bei der Programmierung in Go begegnen Entwickler häufig auf einige häufige Bedürfnisse, ...

Zwei Möglichkeiten, Strukturen in der GO -Sprache zu definieren: Der Unterschied zwischen VAR- und Typ -Schlüsselwörtern. Bei der Definition von Strukturen sieht die Sprache oft zwei verschiedene Schreibweisen: Erstens ...

Der Unterschied zwischen Stringdruck in GO -Sprache: Der Unterschied in der Wirkung der Verwendung von Println und String () ist in Go ...

Go Zeigersyntax und Probleme bei der Verwendung der Viper -Bibliothek bei der Programmierung in Go -Sprache. Es ist entscheidend, die Syntax und Verwendung von Zeigern zu verstehen, insbesondere in ...

Das Problem der Verwendung von RETISTREAM zur Implementierung von Nachrichtenwarteschlangen in der GO -Sprache besteht darin, die Go -Sprache und Redis zu verwenden ...

Warum bewirkt die Kartendiseration in Go alle Werte zum letzten Element? In Go -Sprache begegnen Sie, wenn Sie einige Interviewfragen konfrontiert sind, häufig Karten ...
