net包


• 官方文档

http://godoc.golangtc.com/pkg/net/

Package net provides a portable interface for network I/O, including TCP/IP, UDP, domain name resolution, and Unix domain sockets.

net包中提供了一系列可移植的网络I/O接口,其中包含了TCP/IPUDP域名解析Unix域套接字

RPC


• RPC定义

RPC,Remote Procedure Call Protocol,远程过程调用协议。RPC是一种通过网络从远程计算机程序上请求服务,但不需要了解底层网络技术的一种协议。RPC协议基于某些传输协议(如TCP和UDP协议等)而存在,为通信程序之间携带信息数据。

在传统计算机编程语言中,譬如C和C++,实现RPC是一件不容易的事情。为了实现RPC,首先得基于不同的操作系统提供的网络模型实现网络通信,然后需要自己封装协议来实现RPC,通常为了方便使用还结合使用Lua进行脚本调用。而golang语言原生支持RPC,极大地提高了开发效率。

• net/rpc包

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

官网介绍:rpc包提供了基于网络或其他I/O连接来访问某个对象的导出函数的方法。服务端需要注册提供RPC服务的对象,并以该对象类型的名称作为可见的服务名。对象注册完成之后,该对象的导出函数将可以被远程访问。务端可以注册多个不同类型的对象作为服务,但是需要注意的是,注册同一类型的多个对象将引发错误

▶ 导出函数需满足的条件

• 函数的类型需要导出。

• 函数需要导出。

• 函数必须拥有两个参数,参数必须是导出类型或内建类型。

• 函数的第二个参数必须是一个指针。

• 函数必须返回一个error类型的值。

满足上述条件的函数可以简单表示成:

• 类型T、T1和T2默认使用golang内置的encoding/gob包进行编码解码。

• 第一个参数argType表示由RPC客户端传入的参数。

• 第二个参数replyType表示要返回给RPC客户端的结果。

• 函数最后返回一个error类型的值。如果一个error值返回,replyType参数将不会发送给RPC客户端,而error值将会作为一个字符串发送给RPC客户端。

▶ RPC服务端

• RPC服务端可以通过调用ServeConn处理单个连接上的请求。

• 多数情况下,RPC服务端将创建一个TCP网络监听器并调用Accept,或创建一个HTTP监听器并调用HandleHTTPhttp.Serve

• 如果没有明确指定RPC传输过程中使用何种编码解码器,默认将使用标准库提供的encoding/gob包进行数据传输的编解码器。

▶ RPC客户端

• 将要使用RPC服务的客户端需要建立连接,然后在连接上调用NewClient函数。

• net/rpc包提供了便利的rpc.Dial()rpc.DialHTTP()方法来与指定的RPC服务端建立连接。

• net/rpc包允许客户端使用同步或异步的方式接收RPC服务端的处理结果:调用RPC客户端的Call()方法将进行同步处理,客户端程序顺序执行,只有接收完RPC服务端的处理结果之后才可继续执行后面的程序;调用RPC客户端的Go()方法时将进行异步处理,RPC客户端程序无需等待服务端的结果即可执行后面的程序,而当接收到RPC服务端的处理结果时,再对其进行相应的处理。

• 如果没有明确指定RPC传输过程中使用何种编码解码器,默认将使用标准库提供的encoding/gob包进行数据传输的编解码器。

HTTP RPC使用


• HTTP RPC服务端

▶ 目录结构

▶ 源码如下

package main

import (
"errors"
"log"
"net"
"net/http"
"net/rpc"
"time"
) 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 == {
return errors.New("divide by zero")
} quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
} func main() {
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP() l, e := net.Listen("tcp", ":1234")
defer l.Close()
if e != nil {
log.Fatal("listen error:", e)
return
} go http.Serve(l, nil)
log.Println("rpc server started!") for {
time.Sleep( * time.Second)
}
}

• HTTP RPC客户端

▶ 目录结构

▶ 源码如下

package main

import (
"log"
"net/rpc"
) type Args struct {
A, B int
} type Quotient struct {
Quo, Rem int
} func main() {
client, err := rpc.DialHTTP("tcp", "127.0.0.1"+":1234")
    defer client.Close()
if err != nil {
log.Fatal("dialing error:", err)
return
} args1 := &Args{, }
args2 := &Args{, }
args3 := &Args{, } reply1 :=
reply2 := Quotient{}
reply3 := Quotient{} err = client.Call("Arith.Multiply", args1, &reply1)
if err != nil {
log.Fatal("Arith error:", err)
        return
}
log.Println(reply1) // err = client.Call("Arith.Divide", args2, &reply2)
if err != nil {
log.Fatal("Arith error:", err)
        return
}
log.Println(reply2) // {3 1} err = client.Call("Arith.Divide", args3, &reply3)
if err != nil {
log.Fatal("Arith error:", err) // arith error:divide by zero
        return
}
log.Println(reply3)
}

TCP RPC使用


• TCP RPC服务端

▶ 目录结构

▶ 源码如下

package main

import (
"errors"
"log"
"net"
"net/rpc"
"time"
) 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 == {
return errors.New("divide by zero")
} quo.Quo = args.A / args.B
quo.Rem = args.A % args.B
return nil
} func main() {
arith := new(Arith)
server := rpc.NewServer()
server.Register(arith) l, e := net.Listen("tcp", ":1234")
defer l.Close() if e != nil {
log.Fatal("listen error:", e)
return
} go server.Accept(l)
log.Println("rpc server started!") for {
time.Sleep( * time.Second)
}
}

• TCP RPC客户端

▶ 目录结构

▶ 源码如下

package main

import (
"log"
"net/rpc"
) type Args struct {
A, B int
} type Quotient struct {
Quo, Rem int
} func main() {
client, err := rpc.Dial("tcp", "127.0.0.1:1234")
defer client.Close() if err != nil {
log.Fatal("dialing error:", err)
return
} args1 := &Args{, }
args2 := &Args{, }
args3 := &Args{, } reply1 :=
reply2 := Quotient{}
reply3 := Quotient{} // 同步方式RPC
err = client.Call("Arith.Multiply", args1, &reply1)
if err != nil {
log.Fatal("Arith error:", err)
return
}
log.Println(reply1) // 6 // 异步方式RPC
call2 := client.Go("Arith.Divide", args2, &reply2, nil)
if call2 != nil {
if replyCall, ok := <-call2.Done; ok {
if replyCall.Error != nil {
log.Fatal("Arith error:", replyCall.Error)
return
}
log.Println(reply2) // {3 1}
}
} // 异步方式RPC
call3 := client.Go("Arith.Divide", args3, &reply3, nil)
if call3 != nil {
if replyCall, ok := <-call3.Done; ok {
if replyCall.Error != nil {
log.Fatal("Arith error:", replyCall.Error) // Arith error:divide by zero
return
}
log.Println(reply3) // {3 1}
}
}
}

Protobuf RPC使用


• 环境准备

官网(https://github.com/golang/protobuf)介绍的安装步骤如下:

▶ 安装protobuf

下载地址:https://developers.google.com/protocol-buffers/

▪ windows下安装

下载地址:https://developers.google.com/protocol-buffers/docs/downloads

▪ 下载protoc-2.6.1-win32.zip并解压;

▪ 将protoc.exe路径加入系统路径;

▪ linux下安装

▶ 安装goprotobuf插件

Tips:
需要注意的是,使用go get命令之前需要安装git for windows,否则命令将不起作用。

▪ 在命令行下运行如下命令:

go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
go install github.com/golang/protobuf/proto
go install github.com/golang/protobuf/protoc-gen-go

▪ 工程bin目录下会生成protoc-gen-go.exe文件,将工程bin目录加入系统路径(windows下path路径),以便该工具文件使用。

• 编写proto文件

在src目录下新建pbprotocol目录,并在该目录下新建一个arith.proto的文本文件,编辑该文件内容如下:

package arith;

option cc_generic_services   = true;
option java_generic_services = true;
option py_generic_services = true; message ArithRequest {
optional int32 a = ;
optional int32 b = ;
} message ArithResponse {
optional int32 c = ;
} service ArithService {
rpc Multiply (ArithRequest) returns (ArithResponse);
rpc Divide (ArithRequest) returns (ArithResponse);
}

在命令行进入pbprotocol目录,运行下面命令,生成目标文件arith.pb.go。

protoc --go_out=. arith.proto

对应的目录结构:       
      

• Protobuf RPC服务端

▶ 目录结构

▶ 源码如下

package main

import (
"errors"
"log"
"net"
"net/http"
"net/rpc"
"pbprotocol"
"time" "github.com/golang/protobuf/proto"
) type Arith int func (t *Arith) Multiply(args *arith.ArithRequest, reply *arith.ArithResponse) error {
reply.C = proto.Int32(args.GetA() * args.GetB())
return nil
} func (t *Arith) Divide(args *arith.ArithRequest, reply *arith.ArithResponse) error {
if args.GetB() == {
return errors.New("divide by zero")
} reply.C = proto.Int32(args.GetA() / args.GetB())
return nil
} func main() {
arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP() l, e := net.Listen("tcp", ":1234")
defer l.Close() if e != nil {
log.Fatal("listen error:", e)
return
} go http.Serve(l, nil)
log.Println("rpc server started!") for {
time.Sleep( * time.Second)
}
}

• Protobuf RPC客户端

▶ 目录结构

▶ 源码如下

package main

import (
"log"
"net/rpc"
"pbprotocol" "github.com/golang/protobuf/proto"
) func main() {
client, err := rpc.DialHTTP("tcp", "127.0.0.1"+":1234")
defer client.Close() if err != nil {
log.Fatal("dialing error:", err)
return
} var args arith.ArithRequest
var reply arith.ArithResponse // Multiply
args.A = proto.Int32()
args.B = proto.Int32() err = client.Call("Arith.Multiply", &args, &reply)
if err != nil {
log.Fatal("Arith error:", err)
return
}
log.Println(reply.GetC()) // 2 // Divide
args.A = proto.Int32()
args.B = proto.Int32() err = client.Call("Arith.Divide", &args, &reply)
if err != nil {
log.Fatal("Arith error:", err)
return
}
log.Println(reply.GetC()) // 2 // Divide zero
args.A = proto.Int32()
args.B = proto.Int32() err = client.Call("Arith.Divide", &args, &reply)
if err != nil {
log.Fatal("Arith error:", err) // arith error:divide by zero
return
}
log.Println(reply.GetC())
}

[golang note] 网络编程 - RPC编程的更多相关文章

  1. 网络编程 -- RPC实现原理 -- 目录

    -- 啦啦啦 -- 网络编程 -- RPC实现原理 -- NIO单线程 网络编程 -- RPC实现原理 -- NIO多线程 -- 迭代版本V1 网络编程 -- RPC实现原理 -- NIO多线程 -- ...

  2. 网络编程 -- RPC实现原理 -- NIO单线程

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 Class : Service package lime.pri.limeNio.optimize.socket; import java.io.B ...

  3. 网络编程 -- RPC实现原理 -- NIO多线程 -- 迭代版本V1

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V1——设置标识变量selectionKey.attach(true);只处理一次(会一直循环遍历selectionKeys,占用CPU资源). ( ...

  4. 网络编程 -- RPC实现原理 -- NIO多线程 -- 迭代版本V2

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——增加WriteQueue队列,存放selectionKey.addWriteEventToQueue()添加selectionKey并唤醒阻 ...

  5. 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V1 -- 入门应用

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V1——Netty入门应用 Class : NIOServerBootStrap package lime.pri.limeNio.netty.ne ...

  6. 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V2 -- 对象传输

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——Netty -- 使用序列化和反序列化在网络上传输对象:需要实现 java.io.Serializable 接口 只能传输( ByteBuf ...

  7. 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V3 -- 编码解码

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——Netty -- pipeline.addLast(io.netty.handler.codec.MessageToMessageCodec ...

  8. 网络编程 -- RPC实现原理 -- Netty -- 迭代版本V4 -- 粘包拆包

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——Netty -- new LengthFieldPrepender(2) : 设置数据包 2 字节的特征码 new LengthFieldB ...

  9. 网络编程 -- RPC实现原理 -- RPC -- 迭代版本V1 -- 本地方法调用

    网络编程 -- RPC实现原理 -- 目录 啦啦啦 V1——RPC -- 本地方法调用:不通过网络 入门 1. RPCObjectProxy rpcObjectProxy = new RPCObjec ...

随机推荐

  1. 了解 Go 1.9 的类型别名

    http://colobu.com/2017/06/26/learn-go-type-aliases/

  2. excel中,一系列单元格中包含某一个字段的单元格数量?

    excel中,一系列单元格中包含某一个字段的单元格数量?这个怎么写公式?如:A列单元格A1-A7的内容分别为 A.AB.BC.AC.CD.AD.EA,怎么数这一列中几个单元格的内容包含A字母? 任意单 ...

  3. php学习十三:其他关键字

    在php中,其实不止在php中,在其他语言中我们也会常常接触到一些关键字,整理了一下php当中的一下关键字,可能有些不全,希望大家指出来,多多交流,一起进步. 1.final 特性:1.使用final ...

  4. 使用WdatePicker获取比当前时间大的写法

    <input name="today.date" class="Wdate" type="text" onClick="Wd ...

  5. Intellij debug模式使用

    1.这里以一个web工程为例,点击图中按钮开始运行web工程. 2.设置断点 3.使用postman发送http请求 4.请求发送之后会自动跳到断点处,并且在断点之前会有数据结果显示 5.按F8 在 ...

  6. 关于Android图片资源瘦身的奇思妙想

    版权声明:本文由况鹰原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/77 来源:腾云阁 https://www.qcloud ...

  7. vue报错一

    8080端口被占用 解决方案: 打开cmd输入:netstat -ano查看所有端口信息,如图,找到端口 8080,以及对应的 PID: 输入:tskill PID 即可杀死进程,ex:: tskil ...

  8. 图论之最短路径(3)队列优化的Bellman-Ford算法(SPFA算法)

    在Bellman-Ford算法中 我们可以看到大量的优化空间:如果一个点的最短路径已经确定了,那么它就不会再改变,因此不需要再处理.换句话说:我们每次只对最短路径改变了的顶点的所有出边进行操作 使用一 ...

  9. DNS隧道通信的检测

    DNS隧道通信的检测 DNS 隧道通信 DNS 隧道通信是C&C常用的通信方式,一般常用的编码方式Base64,Binary编码,NetBios编码等,Hex编码等.且请求的Type一般都是t ...

  10. 3.node的url属性

    node的url属性 1.parse: [Function: urlParse],2.format: [Function: urlFormat],3.resolve: [Function: urlRe ...