【Go】五、网络编程

这篇具有很好参考价值的文章主要介绍了【Go】五、网络编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

网络编程

1、互联网协议介绍

2、Socket编程

2.1、socket图解

​        Socket是BSD UNIX进程通信机制,通常也称为“套接字”,用于描述IP地址和端口,是一个通信链的句柄。Socket可以理解为TCP/IP网络API,程序猿可以用其来开发TCP/IP网络上的应用。电脑上运行的应用程序通常通过“套接字”向网络发出请求或者应答请求。

1、socket又称套接字,应用程序通过套接字向网络发出请求或者应答网络请求

2、常用的socket类型有两种:流式socket和数据报socket,流式是一种面向连接(TCP)、数据报是一种无连接(UDP)

3、TCP:比较靠谱、面向连接、比较慢

4、UDP:不大靠谱、无连接、比较快

2.2、TCP编程

1、TCP协议:TCP/IP协议即传输控制协议/网络协议,是一种面向连接、可靠的、基于字节流的传输层通信协议,因为是面向连接的协议,数据像流水一样传输,存在粘包的问题

2、TCP服务端:一个TCP服务端可以同时连接多个客户端,例如世界各地的用户使用自己电脑上的浏览器访问淘宝网,因为Go语言中创建多个goroutine实现并发非常方便和高效,所以我们可以每建立一次连接久创建一个goroutine去处理。

处理流程:1)监听端口;2)接收客户端请求建立连接;3)创建goroutine处理链接

使用Go语言的net包实现TCP服务端代码
// tcp/server/main.go
// TCP server端
// 处理函数
func process(conn net.Conn) {
    defer conn.Close() // 关闭连接:为什么要写在前面,避免忘记,defer使得我们可以在方法最后执行关闭链接操作
    for {
        reader := bufio.NewReader(conn)
        var buf [128]byte
        n, err := reader.Read(buf[:]) // 读取数据
        if err != nil {
            fmt.Println("read from client failed, err:", err)
            break
        }
        recvStr := string(buf[:n])
        fmt.Println("收到client端发来的数据:", recvStr)
        conn.Write([]byte(recvStr)) // 发送数据
    }
}

func main() {
	  listen, err := net.Listen("tcp", "127.0.0.1:20000") // 对127.0.0.1:20000地址进行监听
    if err != nil {
        fmt.Println("listen failed, err:", err)
        return
    }
    for { // 保持链接
        conn, err := listen.Accept() // 建立连接
        if err != nil {
            fmt.Println("accept failed, err:", err)
            continue
        }
        go process(conn) // 启动一个goroutine处理连接
    }
}

3、TCP客户端

1)建立与服务端的连接;2)进行数据收发;3)关闭连接
// tcp/client/main.go
// 客户端
func main() {
    conn, err := net.Dial("tcp", "127.0.0.1:20000")
    if err != nil {
        fmt.Println("err :", err)
        return
    }
    defer conn.Close() // 关闭连接
    inputReader := bufio.NewReader(os.Stdin) // 获取键盘输入的数据
    for {
        input, _ := inputReader.ReadString('\n') // 读取用户输入
        inputInfo := strings.Trim(input, "\r\n")
        if strings.ToUpper(inputInfo) == "Q" { // 如果输入q就退出
            return
        }
        _, err = conn.Write([]byte(inputInfo)) // 发送数据
        if err != nil {
            return
        }
        buf := [512]byte{}
        n, err := conn.Read(buf[:])
        if err != nil {
            fmt.Println("recv failed, err:", err)
            return
        }
        fmt.Println(string(buf[:n]))
    }
}

2.3、UDP编程

1、UDP协议:用户数据报协议,是OSI参考模型中一种无连接的传输层协议,不需要建立连接就能直接进行数据发送和接收,属于不可靠,没有时序的通信,但UDP协议的实时性比较好,通常用于视频直播相关领域。

2、UDP服务端

// UDP/server/main.go

// UDP server端
func main() {
    listen, err := net.ListenUDP("udp", &net.UDPAddr{
        IP:   net.IPv4(0, 0, 0, 0),
        Port: 30000,
    })
    if err != nil {
        fmt.Println("listen failed, err:", err)
        return
    }
    defer listen.Close()
    for {
        var data [1024]byte
        n, addr, err := listen.ReadFromUDP(data[:]) // 接收数据
        if err != nil {
            fmt.Println("read udp failed, err:", err)
            continue
        }
        fmt.Printf("data:%v addr:%v count:%v\n", string(data[:n]), addr, n)
        _, err = listen.WriteToUDP(data[:n], addr) // 发送数据
        if err != nil {
            fmt.Println("write to udp failed, err:", err)
            continue
        }
    }
}

3、UDP客户端

// UDP 客户端
func main() {
    socket, err := net.DialUDP("udp", nil, &net.UDPAddr{
        IP:   net.IPv4(0, 0, 0, 0),
        Port: 30000,
    })
    if err != nil {
        fmt.Println("连接服务端失败,err:", err)
        return
    }
    defer socket.Close()
    sendData := []byte("Hello server")
    _, err = socket.Write(sendData) // 发送数据
    if err != nil {
        fmt.Println("发送数据失败,err:", err)
        return
    }
    data := make([]byte, 4096)
    n, remoteAddr, err := socket.ReadFromUDP(data) // 接收数据
    if err != nil {
        fmt.Println("接收数据失败,err:", err)
        return
    }
    fmt.Printf("recv:%v addr:%v count:%v\n", string(data[:n]), remoteAddr, n)
}

2.4、粘包(❌没有详细看代码,但是大概知道怎么实现)

1、举一个例子:

​        1)服务端代码

// go_base/server/main.go
// 同上面TCP编程的服务端代码

func process(conn net.Conn) {
    defer conn.Close()
    reader := bufio.NewReader(conn)
    var buf [1024]byte
    for {
        n, err := reader.Read(buf[:])
        if err == io.EOF {
            break
        }
        if err != nil {
            fmt.Println("read from client failed, err:", err)
            break
        }
        recvStr := string(buf[:n])
        fmt.Println("收到client发来的数据:", recvStr)
    }
}

func main() {

    listen, err := net.Listen("tcp", "127.0.0.1:30000")
    if err != nil {
        fmt.Println("listen failed, err:", err)
        return
    }
    defer listen.Close()
    for {
        conn, err := listen.Accept()
        if err != nil {
            fmt.Println("accept failed, err:", err)
            continue
        }
        go process(conn)
    }
}

        2)客户端代码

// socket_stick/client/main.go

func main() {
    conn, err := net.Dial("tcp", "127.0.0.1:30000")
    if err != nil {
        fmt.Println("dial failed, err", err)
        return
    }
    defer conn.Close()
    for i := 0; i < 20; i++ {
        msg := `Hello, Hello. How are you?` // 往这个俩节写入20条msg
        conn.Write([]byte(msg))
    }
}

​        3)结果:不会按照我们最初的想法,客户端一次发送20条信息,服务端一次性接收,多条数据粘在一起

收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?
收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?

2、为什么会出现粘包?

​        1)tcp数据传输模式是流模式,在保持长连接的时候可以进行多次收和发,可能发生在接收方、也可能发生在接收端

​        2)我们提交一段数据给TCP发送的时候,TCP没有立即发送此段数据,而是等待一小段时间看看是否还有要发送的数据,若有则会把这两段数据发送出去

​        3)接受端不及时接收数据,导致粘包

3、解决办法:关键在于接收方不知道传输的数据包大小,因此我们可以对数据包进行封包和拆包的操作(🌟建议手敲一遍,不要只是看)

​        封包:给一段数据加上包头,这样以来数据包久分为包头和包体两个部分内容,包头长度是固定的,并且存储了包体的长度,根据包头长度以及包头中包体长度的变量就可以正确的拆分出一个完整的数据包。 => 自己定一个协议(数据包前4个字节为包头,里面存储的是发送的数据长度)

// 1、编码和解码
// 文件位置gobase/proto/proto.go
package proto

import (
	"bufio"
	"bytes"
	"encoding/binary"
)

// Encode Encode消息编码
func Encode(message string) ([]byte, error) {
	// 读取消息长度,转换成为int32类型(4个字节)
	var length  = int32(len(message))
	var pkg = new(bytes.Buffer)
	// 写入消息头
	err := binary.Write(pkg, binary.LittleEndian, length)
	if err != nil {
		return nil, err
	}
	// 写入消息实体
	err = binary.Write(pkg, binary.LittleEndian, []byte(message))
	if err != nil {
		return nil, err
	}
	return pkg.Bytes(), nil
}

// Decode 解码消息
func Decode(reader *bufio.Reader) (string, error) {
	// 读取消息长度
	lengthByte, _ := reader.Peek(4)
	lengthBuffer := bytes.NewBuffer(lengthByte)
	var length int32
	err := binary.Read(lengthBuffer, binary.LittleEndian, &length)
	if err != nil {
		return "", err
	}
	// Buffered返回缓冲中现有可读取的字节数
	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
}


// 2、服务端代码
// 文件位置gobase/server/main.go
package main

import (
	"../proto"
	"bufio"
	"fmt"
	"io"
	"net"
)

func process(conn net.Conn) {
	defer conn.Close()
	reader := bufio.NewReader(conn)
	for {
		msg, err := proto.Decode(reader)
		if err == io.EOF {
			return
		}
		if err != nil {
			fmt.Println("decode msg faild, err = ", err)
			return
		}
		fmt.Println("收到client发来的数据", msg)
	}
}

func main() {
	listen, err := net.Listen("tcp", "127.0.0.1:30000")
	if err != nil {
		fmt.Println("listen faild, err = ", err)
		return
	}
	defer listen.Close()
	for {
		conn, err := listen.Accept()
		if err != nil {
			fmt.Println("accept failed, err = ", err)
			continue
		}
		go process(conn)
	}
}

// 3、客户端代码
// 文件位置gobase/client/main.go
package main

import (
	"../proto"
	"fmt"
	"net"
)

func main() {
	conn, err := net.Dial("tcp","127.0.0.1:30000")
	if err != nil {
		fmt.Println("dail failed, err = " , err)
		return
	}
	defer conn.Close()
	for i := 0; i < 6; i++ {
		msg := "hello,go"
		data, err := proto.Encode(msg)
		if err != nil {
			fmt.Println("encode msg failed, err", err)
			return
		}
		conn.Write(data)
	}
}



// 运行结果
收到client发来的数据 hello,go
收到client发来的数据 hello,go
收到client发来的数据 hello,go
收到client发来的数据 hello,go
收到client发来的数据 hello,go
收到client发来的数据 hello,go

3、Http编程

1、web工作流程

1、客户机通过TCP/IP协议建立服务器到TCP连接
2、客户端向服务器发送HTTP协议请求包,请求服务器里的资源文档
3、服务器向客户机发送HTTP协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理“动态内容”,并将处理得到的数据返回给客户端
4、客户机与服务器断开,由客户端解释HTML文档,在客户端屏幕上渲染图形结果

2、HTTP协议

​        称为超文本传输协议,是互联网上应用最为广泛的一种网络协议,它详细的规定了浏览器和万维网服务器之间相互通信的规则,通过因特网传送万维网文档的数据传送协议;HTTP协议通常承载于TCP协议之上

4、WebSocket编程

1、webScoket编程是什么?

1、WebSocket是一种单个TCP连接上进行全双工通信的协议
2、WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据
3、WebSocket API中,浏览器和服务器只需要一次握手,二者之间就可以直接创建持久性连接,并进行简单双向数据传输
4、需要安装第三方包: go get -u -v github.com/gorilla/websocket -> 11之后的版本 使用 go install

2、这个有一个聊天室的小例子,后续可以单独的出一块提供大家理解(todo)文章来源地址https://www.toymoban.com/news/detail-438288.html

到了这里,关于【Go】五、网络编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Go】五、网络编程

    2.1、socket图解 ​        Socket是BSD UNIX进程通信机制,通常也称为“套接字”,用于描述IP地址和端口,是一个通信链的句柄。Socket可以理解为TCP/IP网络API,程序猿可以用其来开发TCP/IP网络上的应用。电脑上运行的应用程序通常通过“套接字”向网络发出请求或者应答请求

    2024年02月03日
    浏览(16)
  • GO语言网络编程(并发编程)runtime包

    1.1.1. runtime.Gosched() 让出CPU时间片,重新等待安排任务(大概意思就是本来计划的好好的周末出去烧烤,但是你妈让你去相亲,两种情况第一就是你相亲速度非常快,见面就黄不耽误你继续烧烤,第二种情况就是你相亲速度特别慢,见面就是你侬我侬的,耽误了烧烤,但是还馋就

    2024年02月09日
    浏览(44)
  • Go语言网络编程(socket编程)TCP

    TCP协议 TCP/IP(Transmission Control Protocol/Internet Protocol) 即传输控制协议/网间协议,是一种面向连接(连接导向)的、可靠的、基于字节流的传输层(Transport layer)通信协议,因为是面向连接的协议,数据像水流一样传输,会存在黏包问题。 TCP服务端 一个TCP服务端可以同时连接很

    2024年02月09日
    浏览(34)
  • Go语言网络编程(socket编程)TCP粘包

    服务端代码如下: 客户端代码如下: 将上面的代码保存后,分别编译。先启动服务端再启动客户端,可以看到服务端输出结果如下: 收到client发来的数据: Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you?Hello, Hello. How are you? 收到client发来的数

    2024年02月09日
    浏览(44)
  • GO语言网络编程(并发编程)并发介绍,Goroutine

    进程和线程 并发和并行 协程和线程 协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。 线程:一个线程上可以跑多个协程,协程是轻量级的线程。 goroutine 只是由官方实现的超级\\\"线程池\\\"。 每个

    2024年02月09日
    浏览(35)
  • Go语言网络编程介绍以及案例运用

    1. 基本概念 TCP 和 UDP : Go语言支持TCP(传输控制协议)和UDP(用户数据报协议)。TCP提供可靠的、面向连接的通信,而UDP提供无连接的快速数据传输。 并发 : Go语言的并发模型是通过goroutines实现的。每个网络请求都可以在自己的goroutine中处理,实现高效的并发。 Channels : 用于

    2024年01月25日
    浏览(49)
  • Go语言的网络编程与HTTP服务

    Go语言(Golang)是Google开发的一种静态类型、垃圾回收、并发简单的编程语言。Go语言的设计目标是让程序员更容易编写并发程序,并在多核处理器上充分发挥性能。Go语言的网络编程和HTTP服务是其核心功能之一,可以轻松地构建高性能、可扩展的网络应用程序。 在本文中,我们

    2024年02月19日
    浏览(64)
  • Go语言实战:网络编程与TCP_UDP

    Go语言是一种现代的编程语言,由Google的Robert Griesemer、Rob Pike和Ken Thompson于2009年开发。Go语言的设计目标是简单、高效、可扩展和易于使用。它具有垃圾回收、类型安全、并发性能等优点。Go语言的网络编程库 net 包提供了TCP/UDP的实现,使得开发者可以轻松地编写网络应用程序

    2024年02月21日
    浏览(44)
  • Go语言的网络编程与TCP_IP

    Go语言是一种现代的编程语言,由Google的Robert Griesemer、Rob Pike和Ken Thompson在2009年开发。Go语言的设计目标是简单、高效、可扩展和易于使用。它具有弱类型、垃圾回收、并发性和原生支持的网络编程。Go语言的网络编程是其强大功能之一,它使得开发者可以轻松地编写高性能的

    2024年02月22日
    浏览(51)
  • 【Java转Go】快速上手学习笔记(六)之网络编程篇一

    go往期文章笔记: 【Java转Go】快速上手学习笔记(一)之环境安装篇 【Java转Go】快速上手学习笔记(二)之基础篇一 【Java转Go】快速上手学习笔记(三)之基础篇二 【Java转Go】快速上手学习笔记(四)之基础篇三 【Java转Go】快速上手学习笔记(五)之Gorm篇 这篇记的是网络

    2024年02月11日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包