参考书:Go语言编程
第五章 网络编程
1 以前其他的Socket编程步骤
- 建立Socket:使用socket( )函数
- 绑定Socket:使用bind( )函数
- 监听使用listen( )函数,或者连接使用connect( )函数
- 接受连接:使用accept( )函数
- 接收使用receive( )函数,或者发送使用send( )函数
2 无论使用什么协议建立什么形式的连接,都只需要调用net.Dial( )函数,函数原型如下
func Dial( net , addr string ) ( Conn , error )
net参数是网络协议的名字,addr参数是IP地址或域名,而端口号以“:”的形式跟随在地址或域名的后面
- TCP链接:conn , err := net.Dial("tcp" , "192.168.0.10:2100")
- UDP链接:conn , err := net.Dial("udp" , "192.168.0.12:975")
- ICMP链接(使用协议名称):conn , err := net.Dial("ip4:icmp" , "www.baidu.com")
- ICMP链接(使用协议编号):conn , err := net.Dial("ip4:1" , "10.0.0.3")
3 Dial( )函数支持如下几种网络协议:tcp,tcp4(仅限IPv4),tcp6(仅限IPv6),udp,udp4(仅限IPv4),udp6(仅限IPv6),ip,ip4(仅限IPv4),ip6(仅限IPv6)。
4 其实Dial( )函数是对DialTCP( ),DialUDP( ),DialIP( )和DialUnix( )的封装。这些函数原型如下
- func DialTCP(net string , laddr , raddr *TCPAddr) (c *TCPConn , err error)
- func DialUDP(net string , laddr , raddr *UDPAddr) (c *UDPConn , err error)
- func DialIP(netProto string , laddr , raddr *IPAddr) (*IPConn , error)
- func DialUnix(net string , laddr , raddr *UnixAddr) (c *UnixConn , err error)
5 例子
tcpAddr,err := net.ResolveTCPAddr("tcp4",service) // 解析地址和端口号
conn2,err := net.DialTCP("tcp",nil,tcpAddr) // 建立链接
6 net还包含了一系列的工具函数,如下
- 验证IP地址有效性: func net.ParseIP( )
- 创建子网掩码:func IPv4Mask(a , b , c , d byte) IPMask
- 获取默认子网掩码: func (ip IP) DefaultMask( ) IPMask
- 根据域名查找IP:
func ResolveIPAddr(net , addr string) (*IPAddr , error)
func LookupHost(name string) (cname string , addrs []string , err error)
7 Go语言提供了net/http包,涵盖了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)
8 要请求一个资源,只需要调用http.Get( ),如下
resp , err := http.Get("http://example.com/")
if err != nil {
// 处理异常
return
}
defer resp.Body.close( )
9 要以POST的方式发送数据,只需调用http.Post( )并依次传递三个参数即可(请求的目标URL,将要POST数据的资源类型MIMEType和以[]byte形式的数据比特流)
resp , err := http.Post("http://example.com/upload" , "image/jpeg" , &imageDataBuf)
10 HTTP中的Head请求方式表明只请求目标URL的头部信息,即HTTP Header而不返回HTTP Body。用http.Head( )方法即可,只需要传入目标URL即可
resp , err := http.Head("http://example.com/")
11 如果我们发起的HTTP请求需要更多的定制信息,如设定一些自定义的Http Header字段,如
- 设定自定义的“User-Agent”,而不是默认的“Go http package”
- 传递Cookie
此时可以使用net/http包的http.Client对象的Do( )方法来实现
req , err := http.NewRequest("GET" , "http://example.com" , nil)
req.Header.Add("User-Agent" , "Gobook Gustom User-Agent")
client := &http.Client{ // . . . }
resp , err := client.Do(req)
12 在Go语言标准库中,http.Client类型有3个公开数据成员:
Transport RoundTripper
CheckRedirect func(req *Request , via []*Request) error // 指定处理重定向的策略,如果HTTP请求响应为30x,HTTP Client会在遵循跳转规则前先调用这个CheckRedirect函数
Jar CookieJar
13 http.Transport类型,如下
type Transport struct {
Proxy func( *Request ) (*url.URL , error) // 指定代理方法
Dial func(net , addr string) (c net.Conn , err error) // 创建TCP连接
TLSClientConfig *tls.Config // SSL连接专用,指定tls.Client所用的TLS配置信息
DisableKeepAlives bool // 是否取消长连接,默认为false
DisableCompression bool // 是否取消压缩,默认是false
MaxIdleConnsPerHost int //指定与每个请求目标主机间的最大非活跃连接数量
}
14 http.Transport的其他公开成员函数
- func (t *Transport) CloseIdleConnections( ) 关闭所有非活跃的连接
- func (t *Transport) RegisterProtocol(scheme string , rt RoundTripper) 注册并启用一个新的传输协议,比如WebSocket的传输协议标准(ws) ,或FTP,File协议等
- func (t *Transport) RoundTrip(req *Request) (resp *Response , err error) 实现http.RoundTripper接口
15 处理HTTP请求:使用net/http包提供的http.ListenAndServe( )方法,可以在指定的地址进行监听,开启一个HTTP,如下 func ListenAndServe(addr string , handler Handler) error
该方法用于指定的TCP网络地址addr进行监听,然后调用服务端处理程序来处理传入的连接请求。该方法有两个参数,第一个是addr即监听地址;第二个是服务端处理程序,通常为空,表示服务端调用http.DefaultServeMux进行处理。具体代码如下
http.Handle("/foo" , fooHandler)
http.HandleFunc("/bar" , func( w http.ResponseWriter , r *http.Request) {
// toDo
})
log.Fatal( s.ListenAndServe(":8080" , nil) )
16 可以自定义http.Server,代码如下
s := &http.Server {
Addr : ":8080",
Handler: myHandler,
ReadTimeout: 10*time.Second,
WriteTimeout: 10*time.Second,
MaxHeaderBytes: 1 << 20,
}
17 处理HTTPS请求,net/http包还提供http.ListenAndServeTLS( ),用于处理HTTPS连接请求:
func ListenAndServeTLS(addr string , certFile string , keyFile string , handler Handler) error
ListenAndServeTLS( )和ListenAndServe( )的行为一致,区别在于只处理HTTPS请求。此外服务器上必须存在包含证书和与之匹配的私钥的相关文件,比如certFile对应SSL证书文件存放路径,keyFile对应证书私钥文件路径。
http.Handle("/foo" , fooHandler)
http.HandleFunc("/bar" , func( w http.ResponseWriter , r *http.Request) {
// toDo
})
log.Fatal( s.ListenAndServe(":8080" , "cert.pem" , "key.pem" , nil) )
18 在Go语言中,net/rpc包实现了RPC协议
19 使用json.Marshal( )函数可以对一组数据进行JSON格式的编码。函数原型如下
func Marshal( v interface{ } ) ([]byte , error)
20 使用json.Unmarshal( )函数将JSON格式的文本解码为Go里边预期的数据结构,函数原型如下
func Unmarshal( data []byte , v interface{ } ) error
补充:json.Unmarshal( )函数会根据一个约定的顺序查找目标结构中的字段,如一个JSON对象有个名为"Foo"的索引,会按如下顺序查找匹配
- 一个包含Foo标签,即 ` json: "Foo" ` 的字段,如 Name string ` json: "Foo" `;
- 一个名为Foo的字段
- 一个除了首字母其他字母不区分大小写名为Foo的字段
这些字段在类型声明中必须都是以大写字母开头,可被导出的字段。
type Book struct {
Name string `json:"title"`
}
func main(){
b := []byte(`{"Title":"Go语言编程"}`)
var gobook Book
err := json.Unmarshal(b,&gobook)
if err == nil {
fmt.Println(gobook.Name) // Go语言编程
}
}
21 Go语言允许使用map[string]interface{ }和[]interface{ }类型的值来分别存放未知结构的JSON对象或数组,代码如下
b := []byte(`{
"Title":"Go语言编程12",
"Authors":["Tom","Jim","Kily"],
"Publisher":"baidu.com",
"IsPublished": true,
"Price":9.99,
"Sales":1000}`)
var r interface{}
json.Unmarshal(b,&r)
fmt.Printf("r: %v\n",r)
gobook,_ := r.(map[string]interface{})
fmt.Printf("gobook: %v\n",gobook)
// 输出
r: map[Authors:[Tom Jim Kily] IsPublished:true Price:9.99 Publisher:baidu.com Sales:1000 Title:Go语言编程12]
gobook: map[Authors:[Tom Jim Kily] IsPublished:true Price:9.99 Publisher:baidu.com Sales:1000 Title:Go语言编程12]
22 例子
import (
"io"
"log"
"net/http"
)
func helloHandler(w http.ResponseWriter,r *http.Request){
io.WriteString(w,"Hello , world!")
}
func main(){
http.HandleFunc("/hello",helloHandler)
err := http.ListenAndServe(":8080",nil)
if err != nil {
log.Fatal("ListenAndServe: ",err.Error())
}
}
23 页面跳转
http.Redirect(w,r,"/view?id="+filename,http.StatusFound)
24 渲染页面,可以使用io.WriteString( )函数,也可以采用html/template包的ParseFiles( )函数
- 使用io.WriteString( )函数
func uploadHandler(w http.ResponseWriter,r *http.Request){
if r.Method == "GET" {
io.WriteString(w,"<html><form method=\"POST\" action=\"upload\" "+
" enctype=\"multipart/form-data\">"+
"Choose an image to upload: <input name=\"image\" type=\"file\" />"+
"<input type=\"submit\" value=\"Upload\" /></form></html>")
return
}
}
- 使用ParseFiles( )函数
t,err := template.ParseFiles("./src/upload.html")
if err != nil {
http.Error(w,err.Error(),http.StatusInternalServerError)
return
}
t.Execute(w,nil)
25 template.Must( )可以确保如果模板不能解析成功时一定会触发错误处理流程
t := template.Must(template.ParseFiles("./src/upload.html"))