网络编程基本介绍
Golang的主要目标之一就是面向大规模后端服务程序,网络通信整块是服务端 程序必不可少也是至关重要的一部分
-
网络编程有两种:
- TCP socket编程,是网络编程的主流。之所以叫TCP socket编程,是因为底层是基于TCP/IP协议的。比如:QQ聊天
- b/s结构的HTTP编程,我们使用浏览器去访问服务器时,使用的就是http协议,而http底层依旧是用tcp socket实现的。比如:京东商城
-
端口介绍
- 一个IP地址的端口可以有65535(256 * 256,0 - 65535)
- 0号是保留端口
- 1 - 1024 是固定端口:又称有名端口,即被某些程序固定使用,一般程序员不使用
- 1025 - 65535 是动态端口:程序员可以使用
- 注意事项
- 在计算机要尽可能的少开端口
- 一个端口只能被一个程序监听
- 如果使用 netstat -an 可以常看本机有哪些端口在监听
- 如果使用 netstat -anb 来查看监听端口的pid,在结合任务管理器关闭不安全的端口
-
案例
client.go
import (
"bufio"
"fmt"
"net"
"os"
)
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
fmt.Println("connect server fail")
fmt.Println(err)
return
}
defer conn.Close()
fmt.Printf("connect success %v
", conn)
reader := bufio.NewReader(os.Stdin)
for {
fmt.Println("input data: ")
line, err := reader.ReadString('
')
if err != nil {
fmt.Println("read data fail: ", err)
continue
}
if line == "-1" {
fmt.Println("exit client...")
break
}
n, err := conn.Write([]byte(line))
if err != nil {
fmt.Println("send data fail: ", err)
continue
}
fmt.Printf("send %d byte data
", n)
}
}
server.go
import (
"fmt"
"net"
)
func handleConnection(conn net.Conn) {
defer conn.Close() // 函数退出的时候关闭连接
ip := conn.RemoteAddr().String()
for {
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
fmt.Printf("clien %v logout
", ip)
break
}
fmt.Printf("%v say: %v", ip, string(buf[:n]))
}
}
func main() {
fmt.Println("start server...")
ln, err := net.Listen("tcp", "127.0.0.1:8080") // IPV4的写法, IPV6的写法:0.0.0.0:8888
if err != nil {
fmt.Printf("server start error: %v", err)
return
}
defer ln.Close() // 延时关闭
for {
conn, err := ln.Accept() // 接收一个客户端请求
if err != nil {
fmt.Println("connect fail ", err)
continue
}
fmt.Printf("accept client ip = %v
", conn.RemoteAddr().String()) // client info
go handleConnection(conn)
}
}
Dial函数:
在网络network上连接地址address,并返回一个Conn接口。可用的网络类型有:
"tcp"、"tcp4"、"tcp6"、"udp"、"udp4"、"udp6"、"ip"、"ip4"、"ip6"、"unix"、"unixgram"、"unixpacket"对TCP和UDP网络,地址格式是host:port或[host]:port,参见函数JoinHostPort和SplitHostPort。
HTTP 编程
HTTP( HyperText Transfer Protocol,超文本传输协议)是互联网上应用最为广泛的一种网络
协议,定义了客户端和服务端之间请求与响应的传输标准。
Go语言标准库内建提供了net/http包,涵盖了HTTP客户端和服务端的具体实现。使用
net/http包,我们可以很方便地编写HTTP客户端或服务端的程序。
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 服务端
- 处理HTTP请求
使用 net/http 包提供的 http.ListenAndServe() 方法,可以在指定的地址进行监听,开启一个HTTP,服务端该方法的原型如下:
func ListenAndServe(addr string, handler Handler) error
该方法有两个参数:第一个参数 addr 即监听地址;第二个参数表示服务端处理程序,通常为空
- 处理HTTPS请求
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证书。
RPC 编程
RPC( Remote Procedure Call,远程过程调用)是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络细节的应用程序通信协议。 RPC协议构建于TCP或UDP,或者是 HTTP之上,允许开发者直接调用另一台计算机上的程序,而开发者无需额外地为这个调用过程编写网络通信相关代码,使得开发包括网络分布式程序在内的应用程序更加容易。
RPC 采用客户端—服务器( Client/Server)的工作模式。请求程序就是一个客户端( Client),而服务提供程序就是一个服务器( Server)。当执行一个远程过程调用时,客户端程序首先发送一个带有参数的调用信息到服务端,然后等待服务端响应。在服务端,服务进程保持睡眠状态直到客户端的调用信息到达为止。当一个调用信息到达时,服务端获得进程参数,计算出结果,并向客户端发送应答信息,然后等待下一个调用。最后,客户端接收来自服务端的应答信息,获得进程结果,然后调用执行并继续进行
Go语言中的RPC支持与处理
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 类型的值
Go 的 net/rpc 包提供了便利的rpc.Dial()
和 rpc.DialHTTP()
方法来与指定的 RPC 服务端建立连接。
调用 RPC 客户端的 Call() 方法则进行同步处理,这时候客户端程序按顺序执行,只有接收完 RPC 服务端的处理结果之后才可以继续执行后面的程序。当调用 RPC 客户端的 Go() 方法时,则可以进行异步处理, RPC 客户端程序无需等待服务端的结果即可执行后面的程序,而当接收到 RPC 服务端的处理结果时,再对其进行相应的处理。
例子
// rpc_server.go
package service
import (
"errors"
)
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("divide by zero")
}
quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
}
// server.go
package main
import (
"log"
"net"
"net/http"
"net/rpc"
"rpcdemo/server/service"
)
func main() {
arith := new(service.Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", "127.0.0.1:8080")
if e != nil {
log.Fatal("listen error:", e)
}
go http.Serve(l, nil)
for {
}
}
// client.go
package main
import (
"fmt"
"log"
"net/rpc"
"rpcdemo/server/service"
)
func main() {
serverAddress := "127.0.0.1"
client, err := rpc.DialHTTP("tcp", serverAddress+":8080")
if err != nil {
log.Fatal("dialing:", err)
}
args := &service.Args{7, 8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply)
}
Gob简介
Gob 是 Go 的一个序列化数据结构的编码解码工具,在 Go 标准库中内置encoding/gob包以供使用。一个数据结构使用 Gob 进行序列化之后,能够用于网络传输。与 JSON 或 XML 这种基于文本描述的数据交换语言不同, Gob 是二进制编码的数据流,并且 Gob 流是可以自解释的,它在保证高效率的同时,也具备完整的表达能力。
作为针对 Go 的数据结构进行编码和解码的专用序列化方法,这意味着 Gob 无法跨语言使用。在 Go 的net/rpc包中,传输数据所需要用到的编码解码器,默认就是 Gob。
JSON 处理
- json.Marshal()函数:
func Marshal(v interface{}) ([]byte, error)
- json.Unmarshal()函数:
func Unmarshal(data []byte, v interface{}) error