第六章 GO语言网络编程

这一章我们主要说一下http编程
 
一:在GO语言中我们提供了net包,支持基于ip层,tcp/udp层以及更高层如(http,ftp ,smtp)的网络封装,其中ip层的称谓为Raw Socket
在Go中对网络请求种了封装,使用的时候直接net.Dial()就可以啦
 
例如:
conn,err:=net.Dial("tcp","http://www.baidu.com/")
conn,err:=net.Dial("tcp","http://www.baidu.com/")
下面是一个 tcp使用的实例:
 
              
package main
 
import (
 
"fmt"
"os"
"net"
"bytes"
"io"
)
 
func main() {
 
conn,err:=net.Dial("tcp","192.168.22.79:80")
checkError(err)
 
_,errr:=conn.Write([]byte("HEAD /HTTP/1.0\r\n\r\n"))
checkError(errr)
 
result,err :=readFully(conn)
checkError(err)
 
fmt.Println(string(result))
}
 
func checkError(err error) {
if err!=nil{
fmt.Println("链接错误",err.Error())
os.Exit(1)
}
}
 
func readFully(conn net.Conn) ([]byte,error) {
defer conn.Close()
result:=bytes.NewBuffer(nil)
var buf [512]byte
for{
n,err :=conn.Read(buf[0:])
result.Write(buf[0:n])
if err !=nil{
if err ==io.EOF{
break
}
return nil,err
}
}
return result.Bytes(),nil
}
二:HTTP编程
    Go中提供了net/http包,涵盖了http客户端和服务端的具体实现,例如net/http的client包提供了如下几个方法
func(c *client) Get(url string)
func(c *client) Post(url string)
当我们要使用的时候就可以很方便的调用  例如get请求
defer resp.Body.close()
is.Copy(os.Stdout,resp.Body)
而我们要 发送Post请求的时候也是很简单 如下
我们需要传递三个参数
   1:目标url
    2:Post数据的资源形式
    3:数据的比特流
resp,err:=http.Post("http://www.autohome.com.cn/beijing/","text/http",[]byte)
if resp.StatusCode!=http.StatusOk{
}
 
三:模拟http提交表单
http.PostFrom()提供了实现标准编码的application/x-www-form-urlencoded的表单提交
 例如我们要模拟表单提交可以使用一下方法 如:
 resp,err:=http.PostForm("http://www.autohome.com.cn/beijing/","text/http",url.Values("key":{"article title"},"content":{"art body"}))
 
四:http.Head()
 http.Head()表明值请求url的头部信息,就是http Header而不会反悔http body
例如:
resp,err :=http.Head("http://www.autohome.com.cn/beijing/")
 
五:如果我们需要传递个性化的http信息,比如修改http请求头,就需要使用另一种方法了那就是Go语言提供的 client.Do(Req)
req,err :=http.NewRequest("GET","http://www.autohome.com.cn/beijing/",nil)
req.Header.Add("User-Agent","autohome-zzl")
client :=&http.Client{}
resp,err :=client.Do(req)
 
五:Go除了提供基础的HTTp操作外,还暴露了比较底层的HTTP相关的库,然开发者可以基于这些底层的库更加灵活的定制HTTP
 
之前我们使用的 http.get(),  http.Post,http.PostFrom, http.Head等等都是在http.defaultclient的基础上进行调用的,比如:http.Get等价于http.defaultclient.Get  
这个通过名称我们就可以看得出 http.defaultclient.Get  是默认的client,那么HTTP  client大概可以自定义,实际上也确实如此,在net/http包里面的确提供了client类型
 
在Go标准库中 http.client类型包含了3个公开数据成员
   1》Transport类型,实现了http.ROundTripper接口,Transport指定了执行一个http请求的运行机制,如果不指定Transport,默认会使用http.defaultTransport这就意味着http.Transport是可以自定义的
   2》CheckRedirect函数指定处理重定向的策略,当使用HTTP  Client的get或者是Head发送Http请求时,如果响应返回的状态码是30x(如301/302),http  client会在遵循跳转之前调用这个CheckRedirect
   3》Jar 可以用在Http  client中设定cookie,  JAr类型必须实现了http.CookieJar接口, 该接口预定义了 SetCookies和Cookies两个方法,如果 Http  client中没有设定Jar  Cookie将会被忽略而不会发送到客户端,实际上我们一般都是用http.setcookies来设置cookie
 
使用自定义httl.client和Do方法,我们可以更加灵活的控制Http请求,比如
client :=&http.Client{
CheckRedirect:redirectPolicyFunc,
}
resp,err:=client.Get("http://www.autohome.com.cn")
//或者是
 
req,err:=http.NewRequest("GET","http://www.autohome.com.cn",nil)
req.Header.Add("User-Agent","zzlautohome")
resp,err :=client.Do(req)
 
自定义http.Transport
 
tr :=&http.Transport{
TLSClientConfig: &tls.Config{RootCAs:pool},
DisableCompression:true
}
 
client :=&http.Client{Transport:tr}
resp,err:=client.Get("http://www.baidu.com")
 
 
 
 
 
六:灵活的http.RoundTripper接口
   我们在前面说过 http client是可以自定义的,而http  client定义的第一个公开的成员就是http.Transport类型的实例,而且该成员对应的类型必须实现http.RoundTripper接口,那我们就有必要看看http.RoundTripper接口的定义
从上面代码我们可以看得出来 http.RoundTripper接口很简单,值定义了一个RoundTrip的方法,任何实现了RoundTrip的方法的类型既可以实现http.RoundTripper接口,
 
http.RoundTrippe接口定义的RoundTrip方法用于执行一个独立的http事务,接口传入的事 *request而返回值是*response,以及一个error,我们不应该在RoundTrip中解析http响应信息,也不应该处理协议层的相关细节,比如重定向,认证或者coolie
 
package main
 
import "net/http"
 
type OurCustomTransport struct {
Transport http.RoundTripper
}
 
func (t *OurCustomTransport) transport() http.RoundTripper {
if t.Transport !=nil{
return t.Transport
}
return http.DefaultTransport
}
 
func (t *OurCustomTransport) RoundTrip(req *http.Request) (*http.Response,error) {
//处理一些事情
//发起http请求
return t.transport().RoundTrip(req)
}
 
func (t *OurCustomTransport) Client() *http.Client {
return &http.Client{Transport:t}
}
 
func main() {
t :=&OurCustomTransport{}
c :=t.Client()
resp,err :=c.Get("http://www.baidu.com")
}
 
 
七:HTTP服务端
    下面我们要说一下Http的服务端技术,包括HTTP请求和HTTPs请求
使用net/http包提供的 http.ListenAndServe()方法可以对指定的地址进行监听开启http
这个方法的原型是 
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
 
可以看到该方法用于指定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.Fprint(w,"hello,%q",html.EscapeString(r.URL.Path))
})
log.Fatal(http.ListenAndServe(":8080",nil))
 
如果想要更多的控制服务端,可以自定义 http.server  如下
s :=&http.Server{
Addr:":8080",
Handler:nil,
ReadTimeout:10*time.Second,
}
log.Fatal(s.ListenAndServe())
}
 
八:RPC编程
 
     在Go中提供了net/rpc包的实现,开发者可以很方便的使用该包编写rpc的服务端和客户端
   Go中提供了便利的rpc.Dial和rpc.DialHTTp来于指定的RPC服务器建立连接,在链接建议以后,我们可以使用 Call方法同步处理或者使用Go方法异步处理,
无论是同步调用(call)或者是异步调用(Go)都必须指定调用的服务以及其方法名,以及一个客户端传入的参数引用,还有一个用于接收处理结果的参数指针
 
package server
 
import (
"errors"
"net/rpc"
"net"
"log"
"net/http"
"fmt"
)
 
type Args struct {
A, B int
}
type Quotient struct {
Quo, Rem int
}
type Arith int
 
func (t *Arith) Multiply(args *Args,reply *int) error {
*reply=args.A*args.B
return nil
}
 
func (t *Arith) Divide(args *Args,quo *Quotient) error {
if args.B==0{
return errors.New("数据不对")
}
quo.Quo=args.A/args.B
quo.Rem=args.A%args.B
return nil
}
 
func main() {
//注册服务对象并开启该RPC服务的代码如下
arith :=new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l,e:=net.Listen("tcp",":1234")
if e!=nil{
log.Fatal("错误",e)
}
go http.Serve(l,nil)
}
 
func cliente() {
//客户端建立rpc链接
client,err :=rpc.DialHTTP("tcp","127.0.0.1:1234")
if err !=nil {
log.Fatal("dialong",err)
}
//建立完链接就可以调用服务器提供的方法了
//同步调用
args :=&Args{7,8}
var reply int
err:=client.Call("Arith.Multiply",args,&reply)
if err !=nil {
log.Fatal("同步调用失败",err)
}
fmt.Println(args.B,args.B,reply,"同步调用数据")
 
//下面是异步调用
quotient :=new (Quotient)
divcall :=client.Go("Arith.Divide",args,&quotient,nil)
replycall :=divcall.Done
}
 
 
九:Gob介绍
       Gob是go的一个序列化数据结构的编码解码工具,在Go标准库中内置encoding/gob包以供使用,和json和xml不一样 gob是一种二进制编码的数据流,由于gob是go专用的序列化工具,这就意味着gob没有办法跨平台,在go的net/rpc包中,默认的编码解码器就是gob,但是在Go的net.rpc中,他在数据传输前后实现啦编码解码的接口定义,这就意味着开发者可以自定义数据传输方式,
 
GO中大多数数据类型都可以转换为json,但是channel,complex和函数这几种类型除外
 
 
结构转换为Json时只有结构里面一大写字母开头的字段可以被导出
 
package main
 
import (
"encoding/json"
"fmt"
)
 
type Zzl struct {
Title string
Name string
Age int
IsTrue bool
zzlp string
}
 
var tty Zzl
var r interface{}
func main() {
//这是json编码
zzl :=Zzl{"赵占龙","汽车之家",30,true,"不导出"}
b,err :=json.Marshal(zzl)
if err ==nil{
fmt.Println("Json输出:",string(b))
}else {
fmt.Println("出错了",err)
}
//这是json解码
er := json.Unmarshal(b,&tty)
if er ==nil{
fmt.Println("解码成功:",tty.Title)
}
 
//解析位置类型json,我们知道空接口是所有类型的父类型,也就是说空接口可以表示为所有类型,所以我们可以使用空接口来做json动态解析
//并且json动态解析最终是解析成了map[string] interface
errr :=json.Unmarshal(b,&r)
if errr==nil{
gzzp,ok := r.(map[string]interface{})
if ok {
for k,v :=range gzzp{
switch v2 :=v.(type) {
case string:
fmt.Println(k,"is string :",v2)
case int:
fmt.Println(k,"is int :",v2)
default:
fmt.Println(k,"suo you :",v2)
 
}
}
}
 
}
}
 
json的流试读写
  在Go中内建了encoding/json包还提供了Decode和Encoder两个类型,用于支持json数据的流试读写,并提供了NewDecoder和NewEncoder两个函数来实现
 
 
 
 
 
 
 
 
posted @ 2018-08-03 18:11  瀚海行舟  阅读(224)  评论(0编辑  收藏  举报