Go语言学习网络编程与Http教程示例
作者:香香编程喵喵喵 发布时间:2024-02-08 10:33:12
前言
Go语言做网络开发是非常容易的一件事,它已经为我们封装好了Http包,开箱即用。除此之外,我们也可以用Gin框架或者使用fasthttp等三方包,快速搭建一个Web服务。但是,越是封装的方便,我们越是容易忽略底层的一些知识点。 我们这里先补充两个必要的知识:网络分层和进程通信。
网络分层
这块知识属于计算机网络,可以直接去看书。
我们这里直接上图:
我们最常讲的是五层协议,最重要的是运输层和应用层,这两层是大多数情况下,工程师可以在代码中可以直接干预的模块,我们大多数的网络编程调优,就是在调这些协议的一些参数和细节。这两层的情况:
运输层协议:TCP和UDP。
应用层协议:Http,SMTP,FTP,WebSocket等等,这些协议需要使用运输层协议作为依托。
引申,需要注意TCP和UDP的区别,和他们具体的使用场景。
顺便提一句,网络分层本质上也是我们反复提过得加一层的思想,也是高内聚低耦合的一种具体的实现。
进程间通信(IPC)
这块知识属于操作系统,注意不是Linux操作系统,还牵扯一点计算机组成原理的知识。
IPC 是 Inter-Process Communication 的缩写,可以被翻译为进程间通信。主要方法有: 系统信号(signal)、管道(pipe)、套接字 (socket)、文件锁(file lock)、消息队列(message queue)、信号量(semaphore)等。最常用的是系统信号,套接字,还有一个叫共享内存的,能实现,但不提倡。Go底层的os包里也包含着这些常用的方法。
这里需要再引申下,操作系统中进程和线程是什么,协程又是什么。进程间是如何通信的,线程间又是如何通信的。
我们单独把socket拎出来说,因为在众多方案中,就属它比较通用,比较灵活:使用socket可以跨机器进行通讯。
Socket
实际上,现代操作系统的内核都会带有socket相关的API,我们的代码在运行时,只需要调用操作系统提供的接口,就可以轻松建立网络连接,这也是我们之前讲过的面向接口编程的具体场景之一。
Socket网络编程的内容非常多,我们这里肯定是没法展开的,推荐大家直接看下:
https://zhannei.baidu.com/cse/site?q=go&cc=jb51.net&ie=utf
我们这里直接讲Go语言中的Socket。在GO语言中有一个叫做syscall的包,里面有对应的一整套的socket的方法,并且这些方法是做过跨平台处理的,我们最常用的Http包里的许多建立连接,接收内容的方法都直接或者间接的用了syscall包。
总而言之,我们常用的Http包在建立链接时需要使用到socket,socket建立连接时需要具体的传输层协议。
Http
基础知识
HTTP属于应用层协议,也就是最顶层协议。目前他有三个版本:
HTTP1.1 最常用的版本,使用TCP作为运输层协议。
HTTP2 一个升级版本,用的不多。同样使用TCP作为运输层协议。
HTTP3 设计了一个新的传输层协议QUIC,可以选择TCP或者UDP来传输数据。
注意,HTTP协议诞生的年代相当久远,它是一个无状态的协议。
一个HTTP的请求有两部分组成:头部header和主体body。
//这是一个GET请求的头部。
:authority: api.bilibili.com
:method: GET
:path: /x/web-interface/bgroup/member/in?business=MGR&name=PCQoE%E4%BA%BA%E7%BE%A41&dimension=1
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9,sm;q=0.8,en;q=0.7
cache-control: no-cache
cookie:
origin: https://www.bilibili.com
pragma: no-cache
referer: https://www.bilibili.com/?utm_source=gold_browser_extension
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
头部中有几个特别的字段需要关注下。origin,referer, user-agent, accept。另外,还有几个特别的字段:Content-Length,Connection。TCP协议本身是基于字节流的,它无法区分消息边界,需要应用层协议自己来实现。
可以详细看下Response返回的头部中都有哪些字段。另外,一些常见的字段我们经常在Postman中使用。
客户端
在Go语言中启动一个客户端是相当简单的一件事,Go为HTTP提供了大量的开箱即用的工具。
url := "https://www.bilibili.com" //我们要请求的地址
resp, err := http.Get(url) //get请求,经典返回:内容和一个ERR
defer func() {
_ = resp.Body.Close() //通常我们需要及时关闭掉返回内容。
}()
if err != nil {
fmt.Printf("请求错误: %v\n", err)
}
fmt.Printf("返回状态:\n%s\n", resp.Status)
但是,我们通常不会这样直接调用。http.Get的底层调用的是http.Client,返回的是http.Response。通常情况下,我们会使用http.Client结合业务场景来构造一些请求:
url := "https://www.bilibili.com"
req, _ := http.NewRequest(http.MethodGet, url, nil) //req 是一个Request结构,它有大量的方法的熟悉 可以自定义。
req.Form.Add("test", "1231") //构造一个表单提交
req.Header.Set("Cookie", "123") //设置Cookie
resp, err := http.DefaultClient.Do(req) //这里使用的依然是默认的DefaultClient
if err != nil {
fmt.Printf("请求错误: %v\n", err)
}
defer func() {
_ = resp.Body.Close()
}()
fmt.Printf("返回状态:\n%s\n", resp.Status)
正常情况下,我们使用http.DefaultClient.Do,直接调用默认的http.Client就可以正常发起请求。在某些情况下,公司内部会封装一个统一的http.Client,里面会集成一些公司内统一的调用标识,服务请求方,提供方,trace,机器编码,统一的过期时间等配置信息。
http.Client的结构非常简单:
type Client struct {
Transport RoundTripper //真正干活的结构体
CheckRedirect func(req *Request, via []*Request) error //一个重定向校验方法,用的比较少
Jar CookieJar //Cookie包,我们常用的方法都在这个接口中
Timeout time.Duration //单次完整HTTP请求的超时时间,0代表没有设置。
}
如果有时间,可以看下 DefaultTransport的源码,通过简单配置,进而理解Http与TCP的一些关键配置项的含义。
最后,如果你愿意也可以自己造个轮子,但是我们决不提倡这种行为。
conn, err := net.Dial("tcp", "bilibili.com:80")
if err != nil {
fmt.Printf("connect err => %s\n", err.Error())
}
buf := bytes.Buffer{}
buf.WriteString("GET / HTTP/1.1\r\n")
buf.WriteString("Host: baidu.com\r\n")
buf.WriteString("USer-Agent: Go-http-client/1.1\r\n")
// 请求头结束
buf.WriteString("\r\n")
// 请求body结束
buf.WriteString("\r\n\r\n")
_, _ = conn.Write(buf.Bytes())
// 获取响应信息
resp, _ := io.ReadAll(conn)
fmt.Printf("响应信息\n%q", resp)
http.Client的底层是基于net.Dial实现的,net.Dial底层又调用了操作系统的Socket相关接口。
可以尝试实现一个Post方法。
服务端
Go语言搭建一个服务器非常简单,只需要用到几个方法:
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
_, _ = fmt.Fprintf(writer, "关注 香香编程喵喵喵,关注香香编程谢谢喵喵喵!")
})
panic(http.ListenAndServe(":8080", nil))
http.HandleFunc用来注册一个处理器。其内部会持有一个哈希,用来存储路径与处理器的映射关系。注意,这里和Gin框架就有区别了。
http.ListenAndServe用来监听一个端口上的TCP链接,并处理后续的请求。它的底层调用的是net.Listen,同样也是基于Socket的方法,我们这里不做展开。
引申
可以这么说,整个Go的HTTP服务,具体使用上可以直接采用官方包里的方法,其底层细节基本上都是Socket的知识。后续的学习路线可以这样安排:
学习一下HTTP协议的具体内容,关键的配置信息,再看Go的net/http官方包。
学习一些网络编程的知识,主要是协议和Socket编程的基础知识,再看GO的net下的其他包。
注意区别这些内容中容易混淆的概念。
参考 https://zhannei.baidu.com/cse/site?q=go&cc=jb51.net&ie=utf
来源:https://juejin.cn/post/7215095268609671224


猜你喜欢
- 一、问题描述当用JS调用form的方法submit直接提交form的时候,submit事件不响应。为什么?知道的请回复。类比一下,我用inp
- 这篇文章主要介绍了Python中使用gflags实例及原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,
- 1. Callbacks您可以将回调方法定义为模型结构的指针,在创建,更新,查询,删除时将被调用,如果任何回调返回错误,gorm将停止未来操
- 我有的时候写程序要用到当前时间,我就想用python去取当前的时间,虽然不是很难,但是老是忘记,用一次丢一次,为了能够更好的记住,我今天特意
- 目录前言创建组件总结前言Vue3 增加了很多让人眼前一亮的特征,suspense 组件就是其中之一,对处理异步请求数据非常实用,本文通过简单
- 跨域当我们遇到请求后台接口遇到 Access-Control-Allow-Origin 时,那说明跨域了。跨域是因为浏览器的同源策略所导致,
- python __init__.py 和 __all__作用一、__init__.py1、导入文件夹包的时候,会运行写在该文件夹包下的__i
- 如下代码会将npy的格式数据读出,并且输出来到控制台:import numpy as np##设置全部数据,不输出省略号 import sy
- get如果请求url没有变化,取出缓存,提高效率;请求会缓存到浏览器中,可以通过历史记录查看用户信息,安全性低;post传送变化的数据显示,
- 本文实例为大家分享了python学生管理系统开发的具体代码,供大家参考,具体内容如下学生管理系统(基础版)#定义一个函数,显示可以使用的功能
- 使用Numpy创建三维矩阵创建语句#创建形式有两种#1 随机数形式np.random.random((x,y,z))#2 0或1形式np.o
- Python的全局变量:int string, list, dic(map) 如果存在global就能够修改它的值。而不管这个global是
- 本次爬虫用到的网址是:http://www.netbian.com/index.htm: 彼岸桌面.里面有很多的好看壁纸,而且都是可以下载高
- 声明定位元素:position属性值设置除默认值static以外的元素,包括relative,absolute,fixed。平台:win/I
- 作者:Jim Ley(主页)译者:Sheneyan(子乌)时间:2006.1.29英文原文:http://jibbering.com/200
- 一直以来,ACCESS数据库中的申报数据在分公司与总公司之间传递,用EXCEL或DBASE、TXT甚至ACCESS等格式,我总觉得不太理想。
- 本文为大家分享了mysql 8.0.12 安装详细教程,供大家参考,具体内容如下一、安装 1.从官网上下载MySQL8.0.12版本,下载链
- 本文实例讲述了Python 操作mysql数据库查询之fetchone(), fetchmany(), fetchall()用法。分享给大家
- 我们可以通过这样子的方式去理解apache的工作原理1 单进程TCP服务(堵塞式)这是最原始的服务,也就是说只能处理个客户端的连接,等当前客
- 1.过滤器的使用1.过滤器和测试器在Python中,如果需要对某个变量进行处理,我们可以通过函数来实现。在模板中,我们则是通过过滤器来实现的