Go——网络编程

一. 互联网协议介绍

网络基础——网络传输基本流程_网络传输过程-CSDN博客

应用层HTTP协议-CSDN博客

传输层UDP/TCP协议_udp报文提供的确认号用于接收方跟发送方确认-CSDN博客

网络层IP协议-CSDN博客

链路层以太网详解_以太网数据链路层-CSDN博客

二. Socket编程

        Socket是BSD UNIX的进程通信机制,通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。Socket可以理解为TCP/IP网络API。它定义了许多函数或例程,程序员可以用来开发TCP/IP网络上的应用程序。电脑上运行的应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

        2.1 Socket图解

        Socket是应用层与TCP/IP协议簇通信的中间软件抽象层。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议簇隐藏在Socket后面,对用户来说只需要调用Socket规定的相关函数,让Socket去组织符合指定的协议数据然后进行通信。

  • Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
  • 常用的Socket类型有两种:流式Socket和数据报式Socket,流式是一种面向连接的Socket,针对于面向连接的TCP服务应用,数据报式Socket是一种无连接的Socket,针对于无连接的UDP服务应用。
  • TCP:比较靠谱,有连接,慢
  • UDP:不靠谱,比较快

        2.2 Go语言net包Socket接口

        Go语言提供了对Socket编程的良好支持,开发人员可以使用内置的net包进行Socket编程。net包中包含了许多类型和函数,可用于创建和管理Socket连接,网络数据传输,处理信息等操作。

  • 创建Socket
//创建TCP socket
conn, err := net.Dial("tcp", "127.0.0.1:8080")//创建UDP Socket
conn, err := net.Dial("udp", "127.0.0.1:8080")
  • 监听Socket

        为了处理来自其它计算机的连接请求,服务器必须在Socket上监听,以下是在Go语言中监听TCP和UDP Socket的代码示例:

//监听TCP socket
listener, err := net.Listen("tcp", "127.0.0.1:8080")
// TCP 建立连接
conn, err := listener.Accpet()//监听UDP Socket
listener, err := net.ListenPacket("udp", "127.0.0.1:8080")
  • 数据传输

        一旦创建Socket连接,可以使用传输数据。Go语言中提供常见的TCP和UDP Socket数据传输函数。

//TCP Socket发送和接收数据
conn, err := net.Dial("tcp", "127.0.0.1:8080")
conn.Write([]byte("GET /HTTP/1.0"))
buffer := make([]byte, 1024)
conn.Read(buffer)//UDP Socket发送和接收数据
conn, err := net.Dial("udp", "127.0.0.1:8080")
conn.Write([]byte("hello world"))
buffer := make([]byte, 1024)
conn.Read(buffer)
  • 关闭Socket 
//关闭tcp Socket
conn, err := net.Dial("tcp", "127.0.0.1:8080")
defer conn.Close()//关闭udp Socket
conn, err := net.Dial("udp", "127.0.0.1:8080")
defer conn.Close()

        其它接口可以查看:Go语言标准库文档中文版 

        2.3 Go语言实现TCP通信

  • TCP协议

        TCP/IP协议即传输控制协议/网间协议,是一种面向连接(连接导向)的,可靠的,基于字节流的传输层通信协议。因为是面向连接和字节流的协议,数据会存在粘包问题。

  • TCP服务器端

        一个TCP服务器端可以同时连接多个客户端。而Go语言中创建多个goroutine实现并发非常方便和高效,所以我们可以每创建一个连接就创建一个goroutine去处理。

        TCP服务器端程序的处理流程:

  • 监听端口
  • 接收客户端请求建立连接
  • 创建goroutine处理连接

        使用Go语言的net包实现的TCP服务器代码如下:

        创建套接字,绑定套接字,监听套接字,监听到连接后,获取到监听套接字,然后往监听套接字中收发数据。

package mainimport ("bufio""fmt""net"
)func process(conn net.Conn) {defer conn.Close()for {reader := bufio.NewReader(conn)var buf [128]byte//读取数据n, err := reader.Read(buf[:]) //参数是切片 n返回的是长度if err != nil {fmt.Println("read from client failed err: ", err)break}recvStr := string(buf[:n])fmt.Println("收到客户端数据:", recvStr)//返回响应conn.Write([]byte(recvStr)) //写数据到socket}
}func main() {//获得监听套接字,直接完成创建套接字,绑定和设置监听listen, err := net.Listen("tcp", "127.0.0.1:8080")if err != nil {fmt.Println("listen fail")return}defer listen.Close()for {//监听连接conn, err := listen.Accept() //建立连接if err != nil {fmt.Println("Accept fail")continue}//指向请求go process(conn)}
}
  • TCP客户端

        一个TCP客户端进行TCP通信的流程如下:

  • 建立于服务器的连接
  • 进行数据收发
  • 关闭连接

        使用Go语言的net包实现的TCP客户端代码如下:

        创建好套接字后,连接套接字,然后发收数据。

package mainimport ("bufio""fmt""net""os""strings"
)func main() {//连接服务器,获得连接conn, err := net.Dial("tcp", "127.0.0.1:8080")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 len(inputInfo) == 0 {continue}//退出条件if strings.ToUpper(inputInfo) == "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 err: ", err)return}fmt.Println(string(buf[:n]))}
}

        演示:

        2.4 Go语言实现UDP通信

  • UDP协议

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

  • UDP服务端

        使用Go语言net包实现UDP服务器代码:

        创建和绑定好套接字后,直接收发数据,因为UDP无连接。

package mainimport ("fmt""net"
)func main() {//得到套接字 直接完成了套接字生成和绑定套接字listen, err := net.ListenUDP("udp", &net.UDPAddr{IP:   net.IPv4(0, 0, 0, 0),Port: 8080,})if err != nil {fmt.Println("listen fail err: ", err)return}//关闭监听套接字defer listen.Close()for {var data [1024]byte//addr为客户端信息 ip+port等//读取套接字中的数据n, addr, err := listen.ReadFromUDP(data[:])if err != nil {fmt.Println("read udp fail err: ", err)return}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 udp fail err: ", err)return}}
}
  •  UDP客户端

        使用Go语言的net包来实现UDP客户端。

        创建好套接字后,直接从套接字中发收数据。

package mainimport ("fmt""net"
)func main() {//获得udp服务器套接字socket, err := net.DialUDP("udp", nil, &net.UDPAddr{IP:   net.IPv4(0, 0, 0, 0),Port: 8080,})if err != nil {fmt.Println("udp link fail, err: ", err)return}//关闭套接字defer socket.Close()//fmt.Println(string(net.IPv4(0, 0, 0, 0)))//发送数据,短连接var data = []byte("hello world")//向套接字发送数据_, err = socket.Write(data)if err != nil {fmt.Println("udp write fail, err: ", err)return}buf := make([]byte, 1024)//从套接字中读数据n, addr, err := socket.ReadFromUDP(buf)if err != nil {fmt.Println("udp Read fail, err: ", err)return}fmt.Printf("服务端响应:data:%v, addr:%v, count:%v", string(buf[:n]), addr, n)
}

        演示:

        2.5 TCP粘包

  • 发现问题

        输出:

        我们发现客户端分50次向服务器发送数据,在服务器并没有成功的输出50次,而是多条数据粘到一块了。

        对比UDP问题就很明显了:

         输出:

  • 为什么会出现粘包

        主要的原因是tcp数据传递模式是字节流的,在保持长连接的时候可以进行多次收发。

        粘包问题可发生在发送端,也可发生在接收端。

        由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法。简单来说就是当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一段时间看看在等待期间是否还有要发送的数据,若有则会一次把这两段数据发送出去。

         接收端接收不及时造成的接收端粘包:TCP会把接收到的数据存在自己的缓冲区中,然后通知应用层取数据。当应用层由于某些原因不能及时的把TCP的数据取出来,会照成TCP缓冲区中存放了几段数据。

  • 解决办法

        出现粘包的关键在于接收方不确定将要传输的数据包的大小,因此我们可以对数据包进行封包和拆包的操作。

        封包:就是给一段数据加上包头,这样一来数据包就分为包头和包体两部分内容了(过滤非法包时封包会加入"包尾"内容)。包头部分的长度是固定的,并且他存储了包体的长度,根据包体长度固定以及包头中含有包体长度的变量就能拆分出一个完整的包。

        我们可以定义一个协议,比如数据包的前4个字节为包头,里面存储的是发送的数据的长度。

        加报头和解析报头代码:

//文件目录src/proto
package protoimport ("bufio""bytes""encoding/binary""errors""fmt"
)func EnCode(message string) ([]byte, error) {//读取消息长度var length = int32(len(message))//创建包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
}func Decode(reader *bufio.Reader) (string, error) {//读取消息长度lengthByte, _ := reader.Peek(4) //读取前4字节数据lengthBuff := bytes.NewBuffer(lengthByte)var length int32err := binary.Read(lengthBuff, binary.LittleEndian, &length)if err != nil {fmt.Println("binary.Read")return "", err}//buffered返回缓冲中现有的可读取的字节数,包括报头if int32(reader.Buffered()) < length+4 {return "", errors.New("没有有效数据")}//读取全部数据pack := make([]byte, int(length+4))_, err = reader.Read(pack)if err != nil {return "", err}//去掉报头return string(pack[4:]), nil
}

        服务端代码:

package mainimport ("bufio""fmt""io""net""proto"
)func process(conn net.Conn) {defer conn.Close()reader := bufio.NewReader(conn)for {//读取数据,解码data, err := proto.Decode(reader)//最后解码没有数据了 报EOF错if err == io.EOF {break}if err != nil {fmt.Println("decode err: ", err)return}fmt.Println("收到客户端的数据:", data)}
}func main() {//获得监听套接字,直接完成创建套接字,绑定和设置监听listen, err := net.Listen("tcp", "127.0.0.1:8080")if err != nil {fmt.Println("listen fail")return}defer listen.Close()for {//监听连接conn, err := listen.Accept() //建立连接if err != nil {fmt.Println("Accept fail")continue}//指向请求go process(conn)}
}

        客户端代码:

package mainimport ("fmt""net""proto"
)func main() {//连接服务器,获得连接conn, err := net.Dial("tcp", "127.0.0.1:8080")if err != nil {fmt.Println("err: ", err)return}//关闭连接defer conn.Close()for i := 0; i < 20; i++ {msg := "hello, hello. how are you"//编码msgbyte, err := proto.EnCode(msg)if err != nil {return}conn.Write(msgbyte)}
}

 三.http编程

        应用层HTTP协议-CSDN博客

        3.1 http协议

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

        3.2 net/http包接口

        http包提供了HTTP客户端和服务端的实现:

  • Get,Head,Post和PostFrom函数发出HTTP/HTTPS请求
resp, err := http.Get("http://example.com")
...
resp, err := http.Post("http://example.com", "image/jpeg", &buf)
...
resp, err := http.PostForm("http://example.com", url.Values{"Key": {"Value"}, "id": {"123"}})
  • 程序在使用完回复必须关闭回复主体
resp, err := http.Get("http://example.com")
if err != nil{//handle error
}
defer resp.Body.Close()body, err := ioutil.ReadAll(resp.Body)
//...
  • 要管理HTTP客户端的头域,重定向策略和其它设置,创建一个Client
Client := &http.Client{CheckRedirect : redirectPolicyFunc,
}
resp, err := client.Get("http://example.com")
//...req, err := http.NewRequest("Get", "http://example.com", nil)
//...
req.Header.Add("If-None-Match", `W/"wyzzy"`)
resp, err := client.Do(req)
//...
  •  要管理代理,TLS配置,keep-live,压缩和其它设置,创建一个Transport:
tr := &http.Transport{TLSClientConfig:    &tls.Config{RootCAs: pool},DisableCompression: true,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://example.com")

         Client和Transport类型都可以安全的被多个go协程同时使用,处于效率考虑,应该一次建立,尽量重用。

  • ListenAndServer使用指定的监听地址和处理启动一个HTTP服务器端。处理器参数通常是nil,这表示采用包变量DefaultServerMux作为处理器。Handle和HandleFunc函数可以向DefaultServerMux添加处理器。
http.Handle("/foo", fooHandler)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
log.Fatal(http.ListenAndServe(":8080", nil))
  • 要管理服务器行为,可以创建一个自定义server:
s := &http.Server{Addr:           ":8080",Handler:        myHandler,ReadTimeout:    10 * time.Second,WriteTimeout:   10 * time.Second,MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())

         其它接口可以通过查看Go语言标准库文档中文版 

        3.3 HTTP服务端

package mainimport ("fmt""net/http"
)func myHandler(w http.ResponseWriter, r *http.Request) {fmt.Println(r.RemoteAddr, "连接成功")//打印状态行和报头fmt.Println("method: ", r.Method)fmt.Println("url: ", r.URL)fmt.Println("header: ", r.Header)fmt.Println("body: ", r.Body)//发送响应w.Write([]byte("www.baidu.com"))
}func main() {http.HandleFunc("/go", myHandler)http.ListenAndServe("127.0.0.1:8080", nil)
}

        3.4 HTTP客户端

package mainimport ("fmt""io""net/http"
)func main() {//发送Get请求resp, err := http.Get("http://127.0.0.1:8080/go")if err != nil {fmt.Println(err)return}defer resp.Body.Close()//打印状态和报头fmt.Println(resp.Status)fmt.Println(resp.Header)//收响应buf := make([]byte, 1034)for {n, err := resp.Body.Read(buf)if err != nil && err != io.EOF {fmt.Println(err)return} else {fmt.Println("读取完毕")res := string(buf[:n])fmt.Println(res)break}}
}

        演示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/308566.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中国省级人口结构数据集(2002-2022年)

01、数据简介 人口结构数据不仅反映了地域特色&#xff0c;更是预测地区未来发展趋势的重要工具。在这些数据中&#xff0c;总抚养比、少年儿童抚养比和老年人口抚养比是三大核心指标。 少儿抚养比0-14周岁人口数/15-64周岁人口数 老年抚养比65周岁及以上人口数/15-64周岁人…

SpringBoot编写一个SpringTask定时任务的方法

1&#xff0c;在启动类上添加注解 EnableScheduling//开启定时任务调度 2&#xff0c; 任务&#xff08;方法&#xff09;上也要添加注解&#xff1a; Scheduled(cron " 0 * * * * ? ") //每分钟执行一次 域&#xff1a; 秒 分 时 日 月 周 &#xff08;年&#…

03-JAVA设计模式-外观模式

外观模式 什么是外观模式 外观模式&#xff08;Facade Pattern&#xff09;是面向对象设计模式中的一种&#xff0c;它为子系统中的一组接口提供了一个统一的高级接口&#xff0c;使得子系统更容易使用。外观模式定义了一个高层接口&#xff0c;让子系统更容易使用。子系统中…

【JS进阶】第四天

JavaScript 进阶 - 第 4 天 深浅拷贝 浅拷贝 首先浅拷贝和深拷贝只针对引用类型&#xff0c;因为简单类型直接拷贝值了 浅拷贝&#xff1a;拷贝的是地址&#xff0c;只拷贝一层 常见方法&#xff1a; 拷贝对象&#xff1a;Object.assgin() / 展开运算符 {…obj} 拷贝对象…

Linux_ubuntu使用常见问题解决

文章目录 1.安装好了搜狗输入法却只能输出英文&#xff1a; 1.安装好了搜狗输入法却只能输出英文&#xff1a; 1.浏览器搜索搜狗输入法&#xff0c;下载好安装包 终端输入下列命令安装&#xff0c;找不到文件可以cd到该安装包的目录文件下&#xff1a; sudo dpkg -i sogoupin…

自定义vue-cli 实现预设模板项目

模板结构 主要包括四个部分&#xff1a; preset.jsonprompts.jsgenerator/index.jstemplate/ 项目最终结构 preset.json preset.json 中是一个包含创建新项目所需预定义选项和插件的 JSON 对象&#xff0c;让用户无需在命令提示中选择它们&#xff0c;简称预设&#xff1b;…

从 0 搭建公司Jenkins服务 Centos7

从 0 搭建公司Jenkins服务 Centos7 安装 (运维人员) 安装环境 配置DNS安装JDK17安装Jenkins安装Docker安装GIT安装Ansible启动Jenkins安装插件配置凭据配置共享库配置 (开发经理)使用 (开发、测试人员) 安装 (运维人员) 安装环境 配置DNS 新安装系统的服务器无法解析域名&a…

云计算重要概念之:虚拟机、网卡、交换机、路由器、防火墙

一、虚拟机 (Virtual Machine, VM) 1.主流的虚拟化软件&#xff1a; 虚拟化软件通过在单个物理硬件上创建和管理多个虚拟环境&#xff08;虚拟机&#xff09;&#xff0c;实现资源的高效利用、灵活部署、隔离安全以及便捷管理&#xff0c;是构建云计算和现代化数据中心的核心…

B端:弹窗的场景、类型、 选择策略串讲,让你的设计有理有据。

B端产品在什么情况下使用弹窗&#xff0c;弹窗又分为哪些类型&#xff0c;该如何选择合理的弹窗形式&#xff0c;很多小伙伴都是跟着感觉走&#xff0c;本文告诉你依据。 一、弹窗及其场景 在B端系统中&#xff0c;"B端"通常指的是面向企业&#xff08;Business&am…

VMware Workstation部署最新版OpenWrt 23.05.3

正文共&#xff1a;1456 字 51 图&#xff0c;预估阅读时间&#xff1a;2 分钟 我们之前介绍了如何在VMware Workstation上安装OpenWrt&#xff08;软路由是啥&#xff1f;OpenWrt又是啥&#xff1f;长啥样&#xff1f;在VMware装一个瞅瞅&#xff09;&#xff0c;也介绍了如何…

十分钟学懂Java并发

并发简介 我们学到的基本上都是有关顺序编程的知识&#xff0c;即程序中所有事物在任意时刻都只能执行一个步骤。 编程问题中相当大的一部分都可以通过使用顺序编程来解决。然而&#xff0c;对于某些问题&#xff0c;如果能够并发地执行程序中的多个部分&#xff0c;则会变得非…

lua学习笔记19(面相对象学习的一点总结)

print("*****************************面相对象总结*******************************") object{} --实例化方法 function object:new()local obj{}self.__indexselfsetmetatable(obj,self)return obj end-------------------------如何new一个对象 function object:…

1688商品详情接口技术深探:解锁电商数据新纪元,实现业务自动化飞跃

1688商品详情接口技术解析 一、引言 随着电子商务的快速发展&#xff0c;越来越多的企业开始关注如何利用API接口获取商品详情信息&#xff0c;以实现数据的自动化处理和业务的快速拓展。1688作为国内知名的B2B电商平台&#xff0c;其商品详情接口成为了众多企业关注的焦点。…

三小时使用鸿蒙OS模仿羊了个羊,附源码

学习鸿蒙arkTS语言&#xff0c;决定直接通过实践的方式上手&#xff0c;而不是一点点进行观看视频再来实现。 结合羊了个羊的开发思路&#xff0c;准备好相应的卡片素材后进行开发。遇到了需要arkTS进行解决的问题&#xff0c;再去查看相应的文档。 首先需要准备卡片对应的图片…

什么是JAVA面向对象

一&#xff0c;什么是面向对象&#xff1a; 我们以前的项目都是面向过程的&#xff0c;一个完整的项目所有的代码都写在一个类里 这就叫面向过程。 面向对象&#xff0c;是指在写大型项目时&#xff0c;多人分工合作&#xff0c;为了代码看上去简洁美观&#xff0c;会将不同的…

2023-2024年科技行业主要变化趋势梳理与总结

一、主要大额并购案例并购原因 &#xff08;一&#xff09;产品线补充与业务市场拓展&#xff08;如VMware、Activision Blizzard并购案&#xff09; &#xff08;二&#xff09;技术整合&#xff08;如Spunk并购案&#xff09;、 &#xff08;三&#xff09;优化运营以期溢…

MFC:手写一个模态对话框程序

我们知道&#xff0c;在MFC中&#xff0c;要生成一个模态对话框程序&#xff0c;只要按着VS的提示&#xff0c;拖拖拽拽就能生成一个这样的程序&#xff0c;效率非常高。这里&#xff0c;我们反其道而行之&#xff0c;自己写一个这样的程序&#xff0c;这个程序与自动生成的程序…

机器学习和深度学习 --李宏毅(笔记与个人理解)Day 18

Day 18 Spatial Transformer Layer 因为单纯的cNN无法做到scaling&#xff08;放大&#xff09;and rotation&#xff08;转&#xff09;&#xff0c;所以我们引入&#xff1b; 实战中也许我们可以做到 是因为 我们的training data 中包含了对data 的augmentation&#xff1b; …

设计模式——外观(门面)模式10

外观模式&#xff1a;能为系统框架或其他复杂业务流程封装提供一个简单的接口。 例如抽奖过程中 设计模式&#xff0c;一定要敲代码理解 调用1&#xff08;抽奖系统&#xff09; /*** author ggbond* date 2024年04月08日 10:34*/ public class Lottery {public String getId…

帝国cms仿《鳄鱼下载站》网站源码

仿《鳄鱼下载站》网站源码手机安卓软件网站模版 PHP网站源码 帝国cms内核 采用帝国cms7.5 环境PHPmysql 恢复数据库后如何修改密码: 双击表&#xff0c;进入对应的详细数据表&#xff0c;然后找到&#xff1a;www_96kaifa_com_enewsuser这个表&#xff0c;双击打开修改&…