我们所学的TCP
TCP
和UDP
,统称为Socker
和UDP
,统称为Socker
编程,也叫做
套接字编程。多台机器要实现互相通讯,其实是一个非常复杂的过程,底层从铺设网线,网线接口, .. Accédez à la couche d'application 如果没有一套标准,每次使用都要自己去实现,可能每个程序员都不是掉头发那么简单了! 有了 🎜if Il n'y a pas d'ensemble de normes. Vous devez l'implémenter vous-même à chaque fois que vous l'utilisez. Peut-être que chaque programmeur n'est pas aussi simple que de perdre ses cheveux ! 🎜🎜 🎜🎜 Compris🎜🎜 TCP est une connexion longue et stable Étant donné que la communication est impliquée, il doit y avoir deux terminaux, au moins un est le serveur , l'un est le client , tout comme notre Taobao. Chaque fois que nous ouvrons Taobao, nous devons créer un lien vers celui-ci. Bien sûr, Taobao n'est pas directement Implémentez le serveur dans Go, et la concurrence sur le serveur est très simple, il vous suffit de laisser une coroutine gérer chaque connexion ! Code Relativement parlant, il ne nécessite pas de concurrence, il nécessite seulement une connexion. 代码 就这样,我们实现了服务端并发的处理所有客户端的请求。 我们先看一下什么是粘包。 注意:18行代码睡眠了1s Si je commente la 18ème ligne de code est directement ajouté à une ligne, quoi ? Que se passe-t-il ? Cela ne devrait-il pas être la même chose qu'avant ? ? ? Chaque fois qu'une valeur est envoyée, elle est reçue là-bas. Comment cela peut-il être intégré en un seul morceau ? ! ! La raison principale est que nous sommes une couche d'applicationun logiciel fonctionnant sur le système d'exploitation Lorsque nous envoyons une donnée à. le serveur, Il est envoyé en appelant l' interface associée du système d'exploitation Le système d'exploitation effectue ensuite diverses opérations complexes et l'envoie à l'autre machine Cependant. , le système d'exploitation dispose d'un tampon d'envoi de données. Par défaut, si le tampon a une certaine taille, les données ne seront pas envoyées si le tampon n'est pas plein. Par conséquent, lorsque le client ci-dessus envoie des données, le tampon système n'est pas plein. et il est toujours enfoncé dans le tampon du système d'exploitation, j'ai finalement découvert qu'il n'y avait pas de données, donc elles ont été envoyées au serveur d'un seul coup Mais pourquoi 我们将解包封包的函数封装一下 🎜Nous encapsulerons la fonction de déballage du paquet🎜🎜< p cid="n50" mdtype="paragraphe" style="box-sizing : border-box;line-height : hériter;orphelins : 4;marge supérieure : 0,8 em;marge inférieure : 0,8em;espace blanc : pre-wrap;font-famille : 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size: 16px;text-align: start;">🎜 这次真的不管执行几次,都是这样的结果 对了,只有 例如直播行业 本次章节我们讲述了什么是TCP,什么是UDP。 并且编写了代码如何实现TCP服务端,TCP客户端,UDP服务端,UDP客户端。 讲述了为什么会出现粘包,该怎么解决粘包。QQ
QQ
,微信
等软件。Socker
之后,Socker
会在应用层之前,将各种繁琐的的底层操作隐藏,我们可能只需要Socker.TCP
就实现了TCP
, WeChat
Socker
Après 🎜🎜, 🎜🎜Socker.TCP
🎜🎜 est atteint🎜🎜TCP
🎜🎜 protocole de communication. 🎜🎜🎜Go langage TCP
TCP
. Serveur
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
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)
}
}
执行结果
le résultat de l'exécution
La raison
sleep(1)
又管用了呢?这是因为缓冲区不止一个程序在用,1s的时间足够其他程序将缓冲区打满,然后各自发各自的数据,这也是为什么第一次操作没问题,第二次有问题,因为第二次全部都是我们客户端打满的解决粘包
工具函数
socker_sitck/stick.go
Ça fonctionne à nouveau ? C'est parce que le tampon est utilisé par plus d'un programme. 1 s est suffisant pour que d'autres programmes remplissent le tampon et envoient ensuite leurs propres données. d'abord Il n'y a pas eu de problème avec la première opération, mais il y a eu un problème la deuxième fois, car la deuxième fois, tout était entièrement chargé sur notre client🎜 Résoudre les sacs collants🎜
🎜Fonction outil🎜
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))
}
}
执行结果
总结
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!