github上的golang双向rpc,基于原生“net/rpc”库实现,可以注册回调。仅支持一个server和一个client交互。

地址:https://github.com/rocket049/rpc2d

公共包:

package rpc2d

import (
"bufio"
"bytes"
"encoding/binary"
"io"
"log"
"net"
"net/rpc"
"sync"
) //wrap message( []byte ): "T uint8 + length uint16 + bytes [length]byte". T = S/C/E
const (
S = byte('S') //Flag : Server Message
C = byte('C') //Flag : Client Message
E = byte('E') //Flag : Error
) //Pool: bytes.Buffer, use : bufPool.Get().(*bytes.Buffer)
var bufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
} func newBuffer() *bytes.Buffer {
return bufPool.Get().(*bytes.Buffer)
} //ProviderType This is NOT must fit below struct. But this struct can help CALLBACK. See test server.go/client.go
type ProviderType struct {
Client *rpc.Client
Data interface{}
} //RpcNode double direction RPC
type RpcNode struct {
Server *rpc.Server
Client *rpc.Client
connC1, connC2 net.Conn
connS1, connS2 net.Conn
remote net.Conn
} //NewRpcNode create new Rpc.Node ,init rpc.Server with service provider
func NewRpcNode(provider interface{}) *RpcNode {
res := new(RpcNode)
res.Server = rpc.NewServer()
res.Server.Register(provider)
return res
} //WrapSend wrap and split message, send to remote
func (self *RpcNode) wrapSend(t byte, msg []byte, conn io.Writer) (nbytes int, e error) {
len1 := len(msg)
n := len1 / 65535
m := uint16(len1 % 65535)
//log.Printf("length:%d split:%d last:%d\n", len1, n, m)
var h1 = [3]byte{t, 0xff, 0xff}
//h1[0] = t
//binary.BigEndian.PutUint16(h1[1:2], 65535)
bufconn := bufio.NewWriter(conn)
b := newBuffer()
for i := 0; i < n; i++ {
//send
p := msg[i*65535 : i*65535+65535]
b.Reset()
b.Write(h1[:])
b.Write(p)
_, e := bufconn.Write(b.Bytes())
if e != nil {
return 0, e
}
}
if m > 0 {
//send
binary.BigEndian.PutUint16(h1[1:3], m)
p := msg[n*65535 : n*65535+int(m)]
b.Reset()
b.Write(h1[:])
b.Write(p)
_, e := bufconn.Write(b.Bytes())
if e != nil {
return 0, e
}
//log.Printf("length:%d split:%d last:%d\nfrom %c:%v\n", len1, n, m, t, b.Bytes())
}
bufPool.Put(b)
err := bufconn.Flush()
if err != nil {
log.Printf("WrapSend:%v\n", err)
return 0, err
} else {
return len1, nil
}
} //wrapRecv receive message from remote. Next: route to server or client
func (self *RpcNode) wrapRecv(conn io.Reader) (msg []byte, t byte) {
//bufconn := bufio.NewReader(conn)
var h1 [3]byte
n, _ := io.ReadFull(conn, h1[:])
if n != 3 {
return nil, E
}
length := binary.BigEndian.Uint16(h1[1:])
buf1 := make([]byte, int(length))
n, _ = io.ReadFull(conn, buf1)
if n == int(length) {
return buf1, h1[0]
} else {
return nil, E
}
} //proxyLoop proxy between remote and local server/client,redirect/wrapsend messages
func (self *RpcNode) proxyLoop(conn net.Conn) {
self.connS1, self.connS2 = net.Pipe()
self.connC1, self.connC2 = net.Pipe()
//self.Server = rpc.NewServer()
go func() {
self.Server.ServeConn(self.connS1)
//log.Println("end serve")
}()
self.Client = rpc.NewClient(self.connC1)
self.remote = conn
//loop next
go self.localToRemote(self.connC2, C)
go self.localToRemote(self.connS2, S)
go self.remoteToLocal() //block
} func (self *RpcNode) remoteToLocal() {
var bufremote = bufio.NewReader(self.remote) for {
msg, t := self.wrapRecv(bufremote)
switch t {
case S:
self.connC2.Write(msg)
//log.Printf("to C:%v\n", msg)
case C:
self.connS2.Write(msg)
//log.Printf("to S:%v\n", msg)
case E:
break
}
}
self.remote.Close()
log.Println("remote disconnect")
} func (self *RpcNode) localToRemote(from io.ReadCloser, t byte) {
var buf = make([]byte, 512)
for {
n, err := from.Read(buf)
if n > 0 {
_, err := self.wrapSend(t, buf[:n], self.remote)
if err != nil {
log.Printf("WrapSend:%v\n", err)
break
}
} else {
log.Printf("local Read:%v\n", err)
break
}
}
from.Close()
log.Printf("local disconnect: %c\n", t)
} //Dial connect to remote, and link local server/client,use after NewRpcNode
func (self *RpcNode) Dial(addr string) error {
conn, err := net.Dial("tcp", addr)
if err != nil {
return err
}
self.proxyLoop(conn)
return nil
} //Close close
func (self *RpcNode) Close() {
self.Client.Close()
self.connC2.Close()
self.connC1.Close()
self.connS2.Close()
self.connS1.Close()
} //Accept accept remote connection,and link local server/client
func Accept(l net.Listener, provider interface{}) (*RpcNode, error) {
conn, err := l.Accept()
if err != nil {
return nil, err
}
node1 := NewRpcNode(provider)
node1.proxyLoop(conn)
return node1, nil
}

  

server端代码:

package main

import (
"fmt"
"log"
"net"
"time" rpc2d "../rpc2d"
) type ServerStu rpc2d.ProviderType var p = new(ServerStu)
var count = 0 func (self *ServerStu) FuncName(arg *string, reply *int) error {
time.Sleep(2 * time.Second)
var ret int
p.Client.Call("ClentStu."+*arg, "this is callback from ServerStu.FuncName.", &ret)
return nil
} func (self *ServerStu) Show(arg string, reply *int) error {
fmt.Printf(" ServerStuRecv: %s count: %d\n", arg, count)
*reply = count
count++
go Sleepfun()
return nil
} func Sleepfun() {
time.Sleep(2 * time.Second)
var ret int
p.Client.Call("ClentStu.Show", "this is callback from ServerStu.Show.", &ret)
} func main() {
listener, err := net.Listen("tcp", "127.0.0.1:5678")
if err != nil {
log.Fatal("Listen:", err)
}
defer listener.Close() node1, err := rpc2d.Accept(listener, p)
if err != nil {
log.Fatal("Accept:", err)
}
defer node1.Close()
p.Client = node1.Client
var s string
var ret int
fmt.Println("------------")
for i := 0; i < 10; i++ {
fmt.Scanln(&s)
node1.Client.Call("ClentStu.Show", s, &ret)
log.Printf("Return: %d\n", ret)
}
}

  

  

客户端代码:

package main

import (
"fmt"
"log" rpc2d "../rpc2d"
) type ClentStu int var p = new(ClentStu) var count = 10 func (self *ClentStu) Show(arg string, reply *int) error {
fmt.Printf("ClentStu Recv: %s\n", arg)
*reply = count
count++
return nil
} func main() { node1 := rpc2d.NewRpcNode(p)
err := node1.Dial("127.0.0.1:5678")
if err != nil {
log.Fatal("Dial:", err)
}
//p.Client = node1.Client
defer node1.Close()
var s string
var ret int
fmt.Println("------------")
for i := 0; i < 10; i++ {
fmt.Scanln(&s)
node1.Client.Call("ServerStu.Show", s, &ret)
node1.Client.Call("ServerStu.FuncName", "Show", &ret)
log.Printf("Return: %d\n", ret)
}
}

  

github上的golang双向rpc,基于原生“net/rpc”库实现,可以注册回调的更多相关文章

  1. 使用git克隆github上的项目失败,报错error: RPC failed; curl 56 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 10054

    错误描述 今天在github上使用 git clone 某个项目代码的时, git clone https://github.com/XXXX/xxx-blog.git 下载速度很慢,然后下载一段时间 ...

  2. Android 配置从GitHub上下载下来的不太规则的源代码库,并保证程序正常运行

    用过github的朋友一定会发现,我们在github上下载下来的源代码(例子和库),放到eclipse中并不是总能正常运行的,它有可能会出现这样或者那样的错误,例如:找不到jar包,配置文件错误,R文 ...

  3. github上用golang写的项目

    1.moby/moby docker的新马甲 2.kubernetes/kubernetes 分布式容器管理 3.grafana/grafana 一个可视化面板,有漂亮的仪表盘,多种数据来源,适合做系 ...

  4. [resource]Github上维护的一个机器学习相关的框架,库和工具列表

    https://github.com/josephmisiti/awesome-machine-learning  A curated list of awesome Machine Learning ...

  5. 深度学习动手入门:GitHub上四个超棒的TensorFlow开源项目

    作者简介:akshay pai,数据科学工程师,热爱研究机器学习问题.Source Dexter网站创办人. TensorFlow是Google的开源深度学习库,你可以使用这个框架以及Python编程 ...

  6. 【重要】使用Git命令行上传到GitHub上

    [本人GitHub账号:] 用户名:chenhongshuang 密码:shuangshuang6300 邮箱:2452420371@qq.com 进入GitHub账号后 1·新建项目文件名称例dem ...

  7. C# 基于Directshow.Net lib库 USB摄像头使用DirectShow.NET获取摄像头视频流

    https://blog.csdn.net/u010118312/article/details/91766787 https://download.csdn.net/download/u010118 ...

  8. Golang优秀开源项目汇总, 10大流行Go语言开源项目, golang 开源项目全集(golang/go/wiki/Projects), GitHub上优秀的Go开源项目

    Golang优秀开源项目汇总(持续更新...)我把这个汇总放在github上了, 后面更新也会在github上更新. https://github.com/hackstoic/golang-open- ...

  9. GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。

    1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便 ...

随机推荐

  1. Win10安装msi程序报错2503和2502错误解决方案

    刚升级了系统到win10,重新搭建开发环境,在安装scala的时候一直报2503.2502错误,如图 试了好几种办法都不好使,现在罗列依次用到的三种方法: 一.命令提示符(管理员)启动 "w ...

  2. Hyperledger Fabric 1.0 学习搭建 (四)--- 创建Fabric多节点集群

    4.1.配置说明 首先可以根据官方Fabric自带的e2e_cli列子中的集群方案来生成我们自己的集群,与案例不同的是我们需要把容器都分配到不同的服务器上,彼此之间通过网络来进行通信,网络构建完成后则 ...

  3. 如何退出virtualbox scale mode

    进入scale mode之后,可能会退不出来 HOST Key + C. 默认是 右Ctrl + C

  4. July 08th 2017 Week 27th Saturday

    You are never wrong to do the right thing. 坚持做对的事情,永远都不会错. I think the translation may be not precis ...

  5. PhoneGap 的存储 API_Web Sql

    一.介绍 此 API 基于 W3C WEB SQL Database Specification 和 W3C Web Storage API Specification. 有些 设备已经提供了对该规范 ...

  6. django 静态文件

    django 1.8版本以上 django 静态文件配置. 小作之前, 一直觉得django的静态文件配置非常的麻烦. 1. 要设置url(r'^static/(?P<path>.*)&a ...

  7. gpu 显卡 本质

    根据一切皆文件的理念,显卡.gpu.显示器是特殊的单向的只写文件.

  8. mac 系统安装selenium注意事项

    mac最新系统:OS X EI Captian python: 本机自带的python2.7. (本来想升级3.5,觉得太复杂,放弃了) pip: https://pypi.python.org/py ...

  9. 给已安装的NGINX添加新的模块

    给已安装的NGINX添加新的模块 2018-11-16 14:02:45   Visit  0 使用 nginx -V 查看当前nginx的信息,包括版本号和configure编译配置信息 版本号 : ...

  10. ASP.NET教程

    1.ASP.NET是一个使用Html Css JavaScript和服务器脚本创建网页和网站的开发框架 2.ASP.NET支持三种开发模式:WebPages(Web页面).MVC(Model View ...