【七天入门Go语言】 网络编程 | 第六天-阿里云开发者社区

开发者社区> 小生凡一> 正文

【七天入门Go语言】 网络编程 | 第六天

简介: 目录 1. Socket 编程 1.1 Dial()函数 2. HTTP 编程 2.1 HTTP 客户端 2.2 HTTP 服务端 2.2.1 处理 HTTP 请求 3. RPC 编程 3.1 Go 语言中的 RPC 支持与处理 3.2 Gob 简介 3.3 设计优雅的 RPC 接口 最后
+关注继续查看

目录

1. Socket 编程

1.1 Dial()函数

2. HTTP 编程

2.1 HTTP 客户端

2.2 HTTP 服务端

2.2.1 处理 HTTP 请求

3. RPC 编程

3.1 Go 语言中的 RPC 支持与处理

3.2 Gob 简介

3.3 设计优雅的 RPC 接口

最后

1. Socket 编程

在 Go 语言中编写网络程序时,我们将看不到传统的编码形式。以前我们使用 Socket 编程时,会按照如下步骤展开。


建立 Socket:使用 socket()函数。

绑定 Socket:使用 bind()函数。

监听:使用 listen()函数。或者连接:使用 connect()函数。

接受连接:使用 accept()函数。

接收:使用 receive()函数。或者发送:使用 send()函数。

Go 语言标准库对此过程进行了抽象和封装。无论我们期望使用什么协议建立什么形式的连接,都只需要调用 net.Dial()即可。

1.1 Dial()函数

Dial()函数的原型如下:

func Dial(net, addr string) (Conn, error)

其中 net 参数是网络协议的名字,addr 参数是IP 地址或域名,而端口号以:的形式跟随在地址或域名的后面,端口号可选。如果连接成功,返回连接对象,否则返回 error。

我们来看一下几种常见协议的调用方式。

TCP 链接:

conn, err := net.Dial("tcp", "192.168.1.8:3000")

UDP链接:


conn, err := net.Dial("udp", "192.168.1.12:975")


ICMP链接(使用协议名称):


conn, err := net.Dial("ip4:icmp", "www.baidu.com")


ICMP链接(使用协议编号):


conn, err := net.Dial("ip4:1", "10.0.0.3")


在成功建立连接后,我们就可以进行数据的发送和接收。发送数据时,使用 conn 的Write()成员方法,接收数据时使用 Read()方法。


2. HTTP 编程

2.1 HTTP 客户端

Go 内置的 net/http 包提供了最简洁的 HTTP 客户端实现,我们无需借助第三方网络通信库(比如 libcurl)就可以直接使用 HTTP 中用得最多的 GET 和 POST 方式请求数据。


基本方法

net/http 包的 Client 类型提供了如下几个方法,让我们可以用最简洁的方式实现HTTP 请求:

func (c *Client) Get(url string) (r *Response, err error)
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error)
func (c *Client) PostForm(url string, data url.Values) (r *Response, err error)
func (c *Client) Head(url string) (r *Response, err error)
func (c *Client) Do(req *Request) (resp *Response, err error)

下面概要介绍这几个方法。


http.Get()

要请求一个资源,只需调用 http.Get()方法(等价于 http.DefaultClient.Get())即可,示例代码如下:

resp, err := http.Get("http://example.com/")
if err != nil { // 处理错误 ...
    return
}
defer resp.Body.close()
io.Copy(os.Stdout, resp.Body)

上面这段代码请求一个网站首页,并将其网页内容打印到标准输出流中。


http.Post()

要以POST 的方式发送数据,也很简单,只需调用 http.Post()方法并依次传递下面的 3 个参数即可:


请求的目标 URL

将要 POST 数据的资源类型(MIMEType)

数据的比特流([]byte 形式)

下面的示例代码演示了如何上传一张图片:


resp, err := http.Post("http://example.com/upload", "image/jpeg", &imageDataBuf)
if err != nil{ // 处理错误
    return
}
if resp.StatusCode != http.StatusOK {// 处理错误
    return
}


http.PostForm()

http.PostForm()方法实现了标准编码格式为 application/x-www-form-urlencoded的表单提交。下面的示例代码模拟 HTML 表单提交一篇新文章:


resp, err := http.PostForm("http://example.com/posts", url.Values{"title": 
{"article title"}, "content": {"article body"}})
if err != nil{ // 处理错误
    return
}

http.Head()

HTTP 中的 Head 请求方式表明只请求目标 URL 的头部信息,即 HTTP Header 而不返回 HTTP

Body。Go 内置的 net/http 包同样也提供了 http.Head() 方法,该方法同 http.Get() 方法一样,

只需传入目标 URL 一个参数即可。下面的示例代码请求一个网站首页的 HTTP Header 信息:


resp, err := http.Head("http://baidu.com/")


(*http.Client).Do()

在多数情况下,http.Get()和 http.PostForm() 就可以满足需求,但是如果我们发起的

HTTP 请求需要更多的定制信息,我们希望设定一些自定义的 Http Header 字段,比如:


设定自定义的"User-Agent"

传递 Cookie

此时可以使用 net/http 包 http.Client对象的 Do()方法来实现


req, err := http.NewRequest("GET", "http://baidu.com", nil) 
// ...
req.Header.Add("User-Agent", "Gobook Custom User-Agent") 
// ...
client := &http.Client{ //... } 
resp, err := client.Do(req)


2.2 HTTP 服务端

2.2.1 处理 HTTP 请求

使用 net/http 包提供的 http.ListenAndServe() 方法,可以在指定的地址进行监听,开启一个 HTTP,服务端该方法的原型如下:


func ListenAndServe(addr string, handler Handler) error


该方法用于在指定的 TCP 网络地址 addr 进行监听,然后调用服务端处理程序来处理传入的连接请求。该方法有两个参数:第一个参数 addr 即监听地址;第二个参数表示服务端处理程序,通常为空,这意味着服务端调用 http.DefaultServeMux 进行处理,而服务端编写的业务逻辑处理程序 http.Handle() 或 http.HandleFunc() 默认注入 http.DefaultServeMux 中,具体代码如下:


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))


net/http 包还提供 http.ListenAndServeTLS() 方法,用于处理 HTTPS 连接请求:


func ListenAndServeTLS(addr string, certFile string, keyFile string, handler Handler) error

ListenAndServeTLS()和ListenAndServe() 的行为一致,区别在于只处理 HTTPS 请求。此外,服务器上必须存在包含证书和与之匹配的私钥的相关文件,比如 certFile 对应 SSL证书文件存放路径,keyFile 对应证书私钥文件路径。如果证书是由证书颁发机构签署的,certFile 参数指定的路径必须是存放在服务器上的经由 CA认证过的 SSL 证书。


3. RPC 编程

在 Go 中,标准库提供的 net/rpc 包实现了 RPC 协议需要的相关细节,开发者可以很方便地使用该包编写 RPC 的服务端和客户端程序,这使得用 Go 语言开发的多个进程之间的通信变得非常简单。


net/rpc 包允许 RPC 客户端程序通过网络或是其他 I/O 连接调用一个远端对象的公开方法(必须是大写字母开头、可外部调用的)。在 RPC 服务端,可将一个对象注册为可访问的服务,之后该对象的公开方法就能够以远程的方式提供访问。一个 RPC 服务端可以注册多个不同类型的对象,但不允许注册同一类型的多个对象。


一个对象中只有满足如下这些条件的方法,才能被 RPC 服务端设置为可供远程访问:


必须是在对象外部可公开调用的方法(首字母大写);

必须有两个参数,且参数的类型都必须是包外部可以访问的类型或者是 Go 内建支持的类型;

第二个参数必须是一个指针;

方法必须返回一个 error 类型的值。

以上 4 个条件,可以简单地用如下一行代码表示:


func (t *T) MethodName(argType T1, replyType *T2) error


在上面这行代码中,类型 T、T1 和 T2 默认会使用 Go 内置的 encoding/gob 包进行编码解码。


该方法(MethodName)的第一个参数表示由 RPC 客户端传入的参数,第二个参数表示要返回给 RPC 客户端的结果,该方法最后返回一个 error 类型的值。

RPC 服务端可以通过调用 rpc.ServeConn 处理单个连接请求。多数情况下,通过 TCP 或是 HTTP 在某个网络地址上进行监听来创建该服务是个不错的选择。


3.1 Go 语言中的 RPC 支持与处理

在 Go 中,标准库提供的net/rpc包实现了RPC 协议需要的相关细节,开发者可以很方便地使用该包编写 RPC 的服务端和客户端程序,这使得用 Go 语言开发的多个进程之间的通信变得非常简单。


net/rpc 包允许 RPC 客户端程序通过网络或是其他 I/O 连接调用一个远端对象的公开方法(必须是大写字母开头、可外部调用的)。在RPC服务端,可将一个对象注册为可访问的服务,之后该对象的公开方法就能够以远程的方式提供访问。一个RPC服务端可以注册多个不同类型的对象,但不允许注册同一类型的多个对象。


一个对象中只有满足如下这些条件的方法,才能被RPC服务端设置为可供远程访问:


必须是在对象外部可公开调用的方法(首字母大写);

必须有两个参数,且参数的类型都必须是包外部可以访问的类型或者是Go内建支持的类型;

第二个参数必须是一个指针;

方法必须返回一个 error 类型的值。

以上 4 个条件,可以简单地用如下一行代码表示:


func (t *T) MethodName(argType T1, replyType *T2) error


在上面这行代码中,类型 T、T1 和 T2 默认会使用 Go 内置的 encoding/gob 包进行编码解码。关于 encoding/gob 包的内容,稍后我们将会对其进行介绍。

该方法(MethodName)的第一个参数表示由 RPC 客户端传入的参数,第二个参数表示要返回给 RPC 客户端的结果,该方法最后返回一个 error 类型的值。

RPC 服务端可以通过调用 rpc.ServeConn 处理单个连接请求。多数情况下,通过 TCP 或是 HTTP 在某个网络地址上进行监听来创建该服务是个不错的选择。

3.2 Gob 简介

Gob 是 Go 的一个序列化数据结构的编码解码工具,在Go标准库中内置 encoding/gob包以供使用。一个数据结构使用 Gob进行序列化之后,能够用于网络传输。


与JSON或XML这种基于文本描述的数据交换语言不同,Gob是二进制编码的数据流,并且 Gob 流是可以自解释的,它在保证高效率的同时,也具备完整的表达能力。


作为针对 Go的数据结构进行编码和解码的专用序列化方法,这意味着 Gob 无法跨语言使用。在 Go 的 net/rpc 包中,传输数据所需要用到的编码解码器,默认就是 Gob。由于Gob 仅局限于使用 Go 语言开发的程序,这意味着我们只能用 Go 的 RPC 实现进程间通信。


然而,大多数时候,我们用Go编写的 RPC 服务端(或客户端),可能更希望它是通用的,与语言无关的,无论是 Python 、 Java 或其他编程语言实现的 RPC 客户端,均可与之通信。


3.3 设计优雅的 RPC 接口

Go 的 net/rpc 很灵活,它在数据传输前后实现了编码解码器的接口定义。这意味着,开发者可以自定义数据的传输方式以及 RPC 服务端和客户端之间的交互行为。

RPC 提供的编码解码器接口如下:

type ClientCodec interface {
    WriteRequest(*Request, interface{}) error 
    ReadResponseHeader(*Response) error
    ReadResponseBody(interface{}) error
    Close() error
}

type ServerCodec interface {
    ReadRequestHeader(*Request) error 
    ReadRequestBody(interface{}) error 
    WriteResponse(*Response, interface{}) error
    Close() error
}

接口 ClientCodec 定义了 RPC 客户端如何在一个 RPC 会话中发送请求和读取响应。客户端程序通过 WriteRequest() 方法将一个请求写入到 RPC 连接中,并通过ReadResponseHeader()和 ReadResponseBody() 读取服务端的响应信息。当整个过程执行完毕后,再通过 Close()方法来关闭该连接。


接口 ServerCodec 定义了 RPC 服务端如何在一个 RPC 会话中接收请求并发送响应。服务端程序通过ReadRequestHeader()和ReadRequestBody() 方法从一个 RPC 连接中读取 请求信息,然后再通过WriteResponse() 方法向该连接中的 RPC 客户端发送响应。当完成该过程后,通过 Close()方法来关闭连接。

通过实现上述接口,我们可以自定义数据传输前后的编码解码方式,而不仅仅局限于Gob。同样,可以自定义RPC服务端和客户端的交互行为。实际上,Go 标准库提供的 net/rpc/json包,就是一套实现了 rpc.ClientCodec 和rpc.ServerCodec 接口的 JSON-RPC 模块。


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Linux 基本命令2|学习笔记
快速学习Linux 基本命令2
4 0
如何使用 Arthas 定位 Spring Boot 接口超时
公司有个渠道系统,专门对接三方渠道使用,没有什么业务逻辑,主要是转换报文和参数校验之类的工作,起着一个承上启下的作用。
3 0
LINUX基本命令3|学习笔记
快速学习LINUX基本命令3
3 0
Spring Boot 集成 JUnit5,更优雅单元测试!
JUnit4被广泛使用,但是许多场景下使用起来语法较为繁琐,JUnit5中支持lambda表达式,语法简单且代码不冗余。
3 0
Spring Cloud 终于大改版?为什么要用日期来做版本号?
Spring Cloud终于改了 最近Spring Cloud把版本号从A到Z的伦敦地铁站,改为以日期命名了。
4 0
JDK安装配置|学习笔记
快速学习JDK安装配置
4 0
文本效果|学习笔记
快速学习文本效果
3 0
别说,Cerebro 还真好用!老板再也不用担心 Elasticsearch 集群了
Cerebro 是以前的 Elasticsearch 插件 Elasticsearch Kopf 的演变(https://github.com/lmenezes/elasticsearch- kopf) – 这不适用于 Elasticsearch 版本5.x或更高版本。它是查看分片分配和最有用的界面之一,通过图形界面执行常见的索引操作,并且允许您添加用户,密码或 LDAP 身份验证问网络界面。它对先前插件的部分重写,并且可以作为自运行工具使用应用程序服务器。
3 0
+关注
小生凡一
你好呀!
157
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载