我们所학적인TCP
TCP
和UDP
,统称为Socker
和UDP
,统称为Socker
编程,也叫做
套接字编程。多台机器要实现互상통讯,其实是一个不常复杂的过程,底层从 铺设网线,网线接口, ㅋㅋㅋㅋㅋㅋㅋㅋㅋ 응용 프로그램 레이어로 이동 如果没有一套标准,每次使用都要自己去实现,可能每个程序员都不是掉头发那么简单了! 有了 🎜if 정해진 표준은 없습니다. 사용할 때마다 직접 구현해야 합니다. 어쩌면 모든 프로그래머는 머리가 빠지는 것만큼 간단하지 않을 수도 있습니다. 🎜🎜 🎜🎜 알겠습니다🎜🎜 TCP는 안정적이고 신뢰할 수 있는 긴 연결입니다. 통신이 필요하므로 두 개의 터미널이 있어야 하며 적어도 하나는 server입니다. 하나는 클라이언트 입니다. Taobao를 열 때마다 연결해야 합니다. 물론 Taobao가 직접 Go에서 서버를 구현하면 서버에서의 동시성은 매우 간단합니다. 코루틴이 각 연결을 처리하도록 하면 됩니다! Code 클라이언트는 상대적으로 매우 간단하며 동시성이 필요하지 않고 연결만 필요합니다. 代码 就这样,我们实现了服务端并发的处理所有客户端的请求。 我们先看一下什么是粘包。 注意:18行代码睡眠了1s 코드 18번째 줄을 주석 처리하면 이 한 줄에 바로 추가되는데 뭐죠? 무슨 일이 일어나고 있는 걸까요? 이전과 같아야 하지 않나요? ? ? 값이 전송될 때마다 그곳에서 수신됩니다. 이것을 어떻게 하나로 통합할 수 있나요? ! ! 가장 큰 이유는 우리가 운영 체제에서 실행되는 소프트웨어인 애플리케이션 레이어소프트웨어이기 때문입니다. 운영 체제 의 관련 인터페이스 를 호출하여 전송됩니다. 그런 다음 운영 체제는 여러 가지 복잡한 작업을 거쳐 다른 시스템 으로 보냅니다. , 운영 체제에는 기본적으로 버퍼가 특정 크기인 경우 버퍼가 가득 차지 않으면 데이터가 전송되지 않습니다. 따라서 위의 클라이언트가 데이터를 보낼 때 시스템 버퍼가 가득 차지 않습니다. 그리고 항상 운영체제 버퍼에 눌려져 있는데, 결국 데이터가 없다는 걸 발견해서 한꺼번에 서버로 보냈습니다 그런데 왜 我们将解包封包的函数封装一下 🎜패킷 풀기 기능을 캡슐화하겠습니다🎜🎜< p cid="n50" mdtype="paragraph" style="box-sizing: border-box;line-height: 상속;고아: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font- family: 'Open Sans', 'Clear Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size: 16px;text-align: start;">🎜<코드 스타일= "상자 크기: 테두리 -상자; 글꼴 계열: var(--monospace); 수직 정렬: 초기; 테두리 너비: 1px; 테두리 스타일: 솔리드; 테두리 색상: rgb(231, 234, 237) ;배경색: rgb (243, 244, 244);경계 반경: 3px;패딩 오른쪽: 2px;패딩 왼쪽: 2px;글꼴 크기: 0.9em;">socker_sitck/stick.go 🎜🎜🎜🎜🎜 这次真的不管执行几次,都是这样的结果 对了,只有 例如直播行业 本次章节我们讲述了什么是TCP,什么是UDP。 并且编写了代码如何实现TCP服务端,TCP客户端,UDP服务端,UDP客户端。 讲述了为什么会出现粘包,该怎么解决粘包。QQ
QQ
,微信
等软件。Socker
之后,Socker
会在应用层之前,将各种繁琐的的底层操作隐藏,我们可能只需要Socker.TCP
就实现了TCP
, WeChat
소커
🎜🎜 이후 🎜🎜Socker
🎜🎜는 애플리케이션 계층 이전에 다양한 지루한 기본 작업을 숨길 것입니다. 우리는 🎜🎜Socker.TCP
🎜🎜가 달성되었습니다🎜🎜TCP
🎜🎜 프로토콜 통신. 🎜🎜🎜Go 언어 TCP
TCP
은 아닙니다. Server
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)
}
}
执行结果
실행 결과
이유
sleep(1)
sleep(1)
又管用了呢?这是因为缓冲区不止一个程序在用,1s的时间足够其他程序将缓冲区打满,然后各自发各自的数据,这也是为什么第一次操作没问题,第二次有问题,因为第二次全部都是我们客户端打满的解决粘包
工具函数
socker_sitck/stick.go
다시 작동하나요? 이는 버퍼가 두 개 이상의 프로그램에서 사용되기 때문입니다. 1초는 다른 프로그램이 버퍼를 채우고 자체 데이터를 보내는 데 충분한 시간입니다. 첫 번째 작업에는 문제가 없었지만 두 번째에는 문제가 발생했습니다. 두 번째에는 클라이언트에 모든 것이 완전히 로드되었기 때문입니다🎜 끈끈한 가방 해결🎜
🎜도구 기능🎜
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 언어 네트워크 프로그래밍의 기본을 이해하는 데 도움이 되는 기사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!