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. ORACLE_TO_CHAR Function

    TECHONTHENNTE  WEBSITE: https://www.techonthenet.com/oracle/functions/to_char.php Oracle / PLSQL: TO ...

  2. July 09th 2017 Week 28th Sunday

    He that boasts of his own knowledge proclaims ignorance. 夸耀知识实乃无知. Honestly speaking, I don't agree ...

  3. July 01st 2017 Week 26th Saturday

    Kind hearts are more than coronets. 善良的心灵胜于显贵的地位. Some people say that this is a dog-eat-dog world, ...

  4. 在windows平台上构建自己的PHP(php5.3+)

    这是一篇翻译的文章,原文参见:https://wiki.php.net/internals/windows/stepbystepbuild 顺便提一句,wiki.php.net有很多精彩的内容,想深入 ...

  5. 应用服务&领域服务

    应用服务&领域服务 DDD理论学习系列——案例及目录 1. 引言 单从字面理解,不管是领域服务还是应用服务,都是服务.而什么是服务?从SOA到微服务,它们所描述的服务都是一个宽泛的概念,我们可 ...

  6. (转)从信息隐藏的一个需求看C++接口与实现的分离

    原文地址https://blog.csdn.net/tonywearme/article/details/6926649 让我们从stackoverflow上一个同学的问题来开始.问题的原型是这样的( ...

  7. 通俗易懂的来讲讲DOM——科普性质的DOM入门教程

    DOM这个东西很重要,不过初学的时候很容易蒙,什么Document.Element.Node用官方语言来解释根本就不是人话,只能在实践中硬着头皮一点一点尝试.今天要推荐的是一篇关于DOM的博客.说是教 ...

  8. springmvc(5)拦截器

    1.什么是拦截器 是指通过统一的拦截从浏览器发送到服务器的请求来完成相应服务增强 2.拦截器的基本原理 可以通过配置过滤器解决乱码问题 和过滤器非常相似 3.搭建工程 注意jar包 此时的工程是完成后 ...

  9. 禁用U盘的两种方法

    方法一:注册表 计算机\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet003\Services\USBSTOR 更改值为4即可,恢复时同理 ,重启即可 方法二:组策略

  10. python 闭包@装饰器

    1.装饰器 装饰器(Decorator)相对简单,咱们先介绍它:“装饰器的功能是将被装饰的函数当作参数传递给与装饰器对应的函数(名称相同的函数),并返回包装后的被装饰的函数”,听起来有点绕,没关系,直 ...