一篇文章带你了解Go语言基础之网络编程

mb6066e504cce6f · · 508 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

前言

Hi,大家好呀,我是码农,星期八,我们身处21世纪,我们的世界已经在不知不觉中,就像很多网一样在互联互通。

互联网是一个统称,目前比较常用的有TCPUDP协议。

当然,还有很多其他的协议,但是本次主要讲最常用的TCPUDP协议。

socker编程

我们所学的TCPUDP,统称为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([]byte128)        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([]byte1024)    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([]byteint(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(0000),        Port: 8009})    if err != nil {        panic(fmt.Sprintf("udp启动失败,err:%v", err))}    defer listen.Close()    for{        var data = make([]byte1024)        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(0000),        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客户端

讲述了为什么会出现粘包,该怎么解决粘包

逆水行舟,不进则退!

如果在操作过程中有任何问题,记得下面留言,我们看到会第一时间解决问题。

我是码农星期八,如果觉得还不错,记得动手点赞一下哈。

感谢你的观看。

如果你觉得文章还可以,记得点赞留言支持我们哈。感谢你的阅读,有问题请记得在下方留言噢~

想学习更多关于Go的知识,可以参考学习网址:http://pdcfighting.com/,点击阅读原文,可以直达噢~



有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:mb6066e504cce6f

查看原文:一篇文章带你了解Go语言基础之网络编程

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

508 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传