Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
neo
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Stefane Fermigier
neo
Commits
c2a1b63a
Commit
c2a1b63a
authored
Jun 09, 2017
by
Kirill Smelkov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
X naming: Packet = raw data; Message = meaningful object
Message can be delivered encoded in a packet.
parent
b13e8150
Changes
11
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
552 additions
and
555 deletions
+552
-555
go/neo/cluster_test.go
go/neo/cluster_test.go
+6
-0
go/neo/connection.go
go/neo/connection.go
+8
-8
go/neo/connection_test.go
go/neo/connection_test.go
+59
-70
go/neo/pkt.go
go/neo/pkt.go
+3
-1
go/neo/proto-marshal.go
go/neo/proto-marshal.go
+367
-367
go/neo/proto.go
go/neo/proto.go
+15
-15
go/neo/proto_test.go
go/neo/proto_test.go
+27
-27
go/neo/protogen.go
go/neo/protogen.go
+36
-36
go/neo/server/master.go
go/neo/server/master.go
+5
-5
go/neo/server/storage.go
go/neo/server/storage.go
+2
-2
go/neo/x_connection.go
go/neo/x_connection.go
+24
-24
No files found.
go/neo/cluster_test.go
View file @
c2a1b63a
...
...
@@ -53,6 +53,9 @@ func xfs1stor(net Network, path string) (*server.Storage, *fs1.FileStorage) {
// M drives cluster with 1 S through recovery -> verification -> service -> shutdown
func
TestMasterStorage
(
t
*
testing
.
T
)
{
// XXX temp disabled
return
net
:=
NetPipe
(
""
)
// test network FIXME New registers to global table
M
:=
server
.
NewMaster
(
"abc1"
)
S
,
_
:=
xfs1stor
(
net
,
"../zodb/storage/fs1/testdata/1.fs"
)
// XXX +readonly
...
...
@@ -70,6 +73,9 @@ func TestMasterStorage(t *testing.T) {
// basic interaction between Client -- Storage
func
TestClientStorage
(
t
*
testing
.
T
)
{
// XXX temp disabled
return
Cnl
,
Snl
:=
NodeLinkPipe
()
wg
:=
WorkGroup
()
...
...
go/neo/connection.go
View file @
c2a1b63a
...
...
@@ -297,7 +297,7 @@ func (nl *NodeLink) Accept() (c *Conn, err error) {
}
}
// errRecvShutdown returns appropriate error when c.down is found ready in
Recv
// errRecvShutdown returns appropriate error when c.down is found ready in
recvPkt
func
(
c
*
Conn
)
errRecvShutdown
()
error
{
switch
{
case
atomic
.
LoadUint32
(
&
c
.
closed
)
!=
0
:
...
...
@@ -323,8 +323,8 @@ func (c *Conn) errRecvShutdown() error {
}
}
//
Recv receives
packet from connection
func
(
c
*
Conn
)
Recv
()
(
*
PktBuf
,
error
)
{
//
recvPkt receives raw
packet from connection
func
(
c
*
Conn
)
recvPkt
()
(
*
PktBuf
,
error
)
{
select
{
case
<-
c
.
down
:
return
nil
,
c
.
err
(
"recv"
,
c
.
errRecvShutdown
())
...
...
@@ -441,13 +441,13 @@ func (c *Conn) errSendShutdown() error {
}
}
//
Send sends
packet via connection
func
(
c
*
Conn
)
Send
(
pkt
*
PktBuf
)
error
{
err
:=
c
.
send
(
pkt
)
//
sendPkt sends raw
packet via connection
func
(
c
*
Conn
)
sendPkt
(
pkt
*
PktBuf
)
error
{
err
:=
c
.
send
Pkt2
(
pkt
)
return
c
.
err
(
"send"
,
err
)
}
func
(
c
*
Conn
)
send
(
pkt
*
PktBuf
)
error
{
func
(
c
*
Conn
)
send
Pkt2
(
pkt
*
PktBuf
)
error
{
// set pkt connId associated with this connection
pkt
.
Header
()
.
ConnId
=
hton32
(
c
.
connId
)
var
err
error
...
...
@@ -541,7 +541,7 @@ func (nl *NodeLink) recvPkt() (*PktBuf, error) {
// first read to read pkt header and hopefully up to page of data in 1 syscall
pkt
:=
&
PktBuf
{
make
([]
byte
,
4096
)}
// TODO reenable, but NOTE next packet can be also prefetched here -> use buffering ?
//n, err := io.ReadAtLeast(nl.peerLink, p
kt
.Data, PktHeadLen)
//n, err := io.ReadAtLeast(nl.peerLink, p
tb
.Data, PktHeadLen)
n
,
err
:=
io
.
ReadFull
(
nl
.
peerLink
,
pkt
.
Data
[
:
PktHeadLen
])
if
err
!=
nil
{
return
nil
,
err
...
...
go/neo/connection_test.go
View file @
c2a1b63a
...
...
@@ -72,24 +72,13 @@ func xaccept(nl *NodeLink) *Conn {
return
c
}
func
xsend
(
c
*
Conn
,
pkt
*
PktBuf
)
{
err
:=
c
.
Send
(
pkt
)
func
xsend
Pkt
(
c
interface
{
sendPkt
(
*
PktBuf
)
error
}
,
pkt
*
PktBuf
)
{
err
:=
c
.
sendPkt
(
pkt
)
exc
.
Raiseif
(
err
)
}
func
xrecv
(
c
*
Conn
)
*
PktBuf
{
pkt
,
err
:=
c
.
Recv
()
exc
.
Raiseif
(
err
)
return
pkt
}
func
xsendPkt
(
nl
*
NodeLink
,
pkt
*
PktBuf
)
{
err
:=
nl
.
sendPkt
(
pkt
)
exc
.
Raiseif
(
err
)
}
func
xrecvPkt
(
nl
*
NodeLink
)
*
PktBuf
{
pkt
,
err
:=
nl
.
recvPkt
()
func
xrecvPkt
(
c
interface
{
recvPkt
()
(
*
PktBuf
,
error
)
})
*
PktBuf
{
pkt
,
err
:=
c
.
recvPkt
()
exc
.
Raiseif
(
err
)
return
pkt
}
...
...
@@ -134,7 +123,7 @@ func _mkpkt(connid uint32, msgcode uint16, payload []byte) *PktBuf {
}
func
mkpkt
(
msgcode
uint16
,
payload
[]
byte
)
*
PktBuf
{
// in Conn exchange connid is automatically set by Conn.
Send
// in Conn exchange connid is automatically set by Conn.
sendPkt
return
_mkpkt
(
0
,
msgcode
,
payload
)
}
...
...
@@ -300,7 +289,7 @@ func TestNodeLink(t *testing.T) {
// Test connections on top of nodelink
// Close vs
Recv
// Close vs
recvPkt
nl1
,
nl2
=
_nodeLinkPipe
(
0
,
linkNoRecvSend
)
c
=
xnewconn
(
nl1
)
wg
=
WorkGroup
()
...
...
@@ -308,15 +297,15 @@ func TestNodeLink(t *testing.T) {
tdelay
()
xclose
(
c
)
})
pkt
,
err
=
c
.
Recv
()
pkt
,
err
=
c
.
recvPkt
()
if
!
(
pkt
==
nil
&&
xconnError
(
err
)
==
ErrClosedConn
)
{
t
.
Fatalf
(
"Conn.
Recv
() after close: pkt = %v err = %v"
,
pkt
,
err
)
t
.
Fatalf
(
"Conn.
recvPkt
() after close: pkt = %v err = %v"
,
pkt
,
err
)
}
xwait
(
wg
)
xclose
(
nl1
)
xclose
(
nl2
)
// Close vs
Send
// Close vs
sendPkt
nl1
,
nl2
=
_nodeLinkPipe
(
0
,
linkNoRecvSend
)
c
=
xnewconn
(
nl1
)
wg
=
WorkGroup
()
...
...
@@ -325,27 +314,27 @@ func TestNodeLink(t *testing.T) {
xclose
(
c
)
})
pkt
=
&
PktBuf
{[]
byte
(
"data"
)}
err
=
c
.
Send
(
pkt
)
err
=
c
.
sendPkt
(
pkt
)
if
xconnError
(
err
)
!=
ErrClosedConn
{
t
.
Fatalf
(
"Conn.
Send
() after close: err = %v"
,
err
)
t
.
Fatalf
(
"Conn.
sendPkt
() after close: err = %v"
,
err
)
}
xwait
(
wg
)
// NodeLink.Close vs Conn.
Send/Recv
// NodeLink.Close vs Conn.
sendPkt/recvPkt
c11
:=
xnewconn
(
nl1
)
c12
:=
xnewconn
(
nl1
)
wg
=
WorkGroup
()
wg
.
Gox
(
func
()
{
pkt
,
err
:=
c11
.
Recv
()
pkt
,
err
:=
c11
.
recvPkt
()
if
!
(
pkt
==
nil
&&
xconnError
(
err
)
==
ErrLinkClosed
)
{
exc
.
Raisef
(
"Conn.
Recv
() after NodeLink close: pkt = %v err = %v"
,
pkt
,
err
)
exc
.
Raisef
(
"Conn.
recvPkt
() after NodeLink close: pkt = %v err = %v"
,
pkt
,
err
)
}
})
wg
.
Gox
(
func
()
{
pkt
:=
&
PktBuf
{[]
byte
(
"data"
)}
err
:=
c12
.
Send
(
pkt
)
err
:=
c12
.
sendPkt
(
pkt
)
if
xconnError
(
err
)
!=
ErrLinkClosed
{
exc
.
Raisef
(
"Conn.
Send
() after NodeLink close: err = %v"
,
err
)
exc
.
Raisef
(
"Conn.
sendPkt
() after NodeLink close: err = %v"
,
err
)
}
})
tdelay
()
...
...
@@ -355,7 +344,7 @@ func TestNodeLink(t *testing.T) {
xclose
(
c12
)
xclose
(
nl2
)
// NodeLink.Close vs Conn.
Send/Recv
and Accept on another side
// NodeLink.Close vs Conn.
sendPkt/recvPkt
and Accept on another side
nl1
,
nl2
=
_nodeLinkPipe
(
linkNoRecvSend
,
0
)
c21
:=
xnewconn
(
nl2
)
c22
:=
xnewconn
(
nl2
)
...
...
@@ -363,22 +352,22 @@ func TestNodeLink(t *testing.T) {
wg
=
WorkGroup
()
var
errRecv
error
wg
.
Gox
(
func
()
{
pkt
,
err
:=
c21
.
Recv
()
pkt
,
err
:=
c21
.
recvPkt
()
want1
:=
io
.
EOF
// if recvPkt wakes up due to peer close
want2
:=
io
.
ErrClosedPipe
// if recvPkt wakes up due to sendPkt wakes up first and closes nl1
cerr
:=
xconnError
(
err
)
if
!
(
pkt
==
nil
&&
(
cerr
==
want1
||
cerr
==
want2
))
{
exc
.
Raisef
(
"Conn.
Recv
after peer NodeLink shutdown: pkt = %v err = %v"
,
pkt
,
err
)
exc
.
Raisef
(
"Conn.
recvPkt
after peer NodeLink shutdown: pkt = %v err = %v"
,
pkt
,
err
)
}
errRecv
=
cerr
})
wg
.
Gox
(
func
()
{
pkt
:=
&
PktBuf
{[]
byte
(
"data"
)}
err
:=
c22
.
Send
(
pkt
)
err
:=
c22
.
sendPkt
(
pkt
)
want
:=
io
.
ErrClosedPipe
// always this in both due to peer close or recvPkt waking up and closing nl2
if
xconnError
(
err
)
!=
want
{
exc
.
Raisef
(
"Conn.
Send
after peer NodeLink shutdown: %v"
,
err
)
exc
.
Raisef
(
"Conn.
sendPkt
after peer NodeLink shutdown: %v"
,
err
)
}
})
...
...
@@ -406,46 +395,46 @@ func TestNodeLink(t *testing.T) {
t
.
Fatalf
(
"Accept after NodeLink shutdown: conn = %v err = %v"
,
c
,
err
)
}
//
Recv/Send
on another Conn
pkt
,
err
=
c23
.
Recv
()
//
recvPkt/sendPkt
on another Conn
pkt
,
err
=
c23
.
recvPkt
()
if
!
(
pkt
==
nil
&&
xconnError
(
err
)
==
errRecv
)
{
t
.
Fatalf
(
"Conn.
Recv
2 after peer NodeLink shutdown: pkt = %v err = %v"
,
pkt
,
err
)
t
.
Fatalf
(
"Conn.
recvPkt
2 after peer NodeLink shutdown: pkt = %v err = %v"
,
pkt
,
err
)
}
err
=
c23
.
Send
(
&
PktBuf
{[]
byte
(
"data"
)})
err
=
c23
.
sendPkt
(
&
PktBuf
{[]
byte
(
"data"
)})
if
xconnError
(
err
)
!=
ErrLinkDown
{
t
.
Fatalf
(
"Conn.
Send
2 after peer NodeLink shutdown: %v"
,
err
)
t
.
Fatalf
(
"Conn.
sendPkt
2 after peer NodeLink shutdown: %v"
,
err
)
}
//
Recv/Send
error on second call
pkt
,
err
=
c21
.
Recv
()
//
recvPkt/sendPkt
error on second call
pkt
,
err
=
c21
.
recvPkt
()
if
!
(
pkt
==
nil
&&
xconnError
(
err
)
==
ErrLinkDown
)
{
t
.
Fatalf
(
"Conn.
Recv
after NodeLink shutdown: pkt = %v err = %v"
,
pkt
,
err
)
t
.
Fatalf
(
"Conn.
recvPkt
after NodeLink shutdown: pkt = %v err = %v"
,
pkt
,
err
)
}
err
=
c22
.
Send
(
&
PktBuf
{[]
byte
(
"data"
)})
err
=
c22
.
sendPkt
(
&
PktBuf
{[]
byte
(
"data"
)})
if
xconnError
(
err
)
!=
ErrLinkDown
{
t
.
Fatalf
(
"Conn.
Send
after NodeLink shutdown: %v"
,
err
)
t
.
Fatalf
(
"Conn.
sendPkt
after NodeLink shutdown: %v"
,
err
)
}
xclose
(
c23
)
//
Recv/Send
on closed Conn but not closed NodeLink
pkt
,
err
=
c23
.
Recv
()
//
recvPkt/sendPkt
on closed Conn but not closed NodeLink
pkt
,
err
=
c23
.
recvPkt
()
if
!
(
pkt
==
nil
&&
xconnError
(
err
)
==
ErrClosedConn
)
{
t
.
Fatalf
(
"Conn.
Recv
after close but only stopped NodeLink: pkt = %v err = %v"
,
pkt
,
err
)
t
.
Fatalf
(
"Conn.
recvPkt
after close but only stopped NodeLink: pkt = %v err = %v"
,
pkt
,
err
)
}
err
=
c23
.
Send
(
&
PktBuf
{[]
byte
(
"data"
)})
err
=
c23
.
sendPkt
(
&
PktBuf
{[]
byte
(
"data"
)})
if
xconnError
(
err
)
!=
ErrClosedConn
{
t
.
Fatalf
(
"Conn.
Send
after close but only stopped NodeLink: %v"
,
err
)
t
.
Fatalf
(
"Conn.
sendPkt
after close but only stopped NodeLink: %v"
,
err
)
}
xclose
(
nl2
)
//
Recv/Send
NewConn/Accept error after NodeLink close
pkt
,
err
=
c21
.
Recv
()
//
recvPkt/sendPkt
NewConn/Accept error after NodeLink close
pkt
,
err
=
c21
.
recvPkt
()
if
!
(
pkt
==
nil
&&
xconnError
(
err
)
==
ErrLinkClosed
)
{
t
.
Fatalf
(
"Conn.
Recv
after NodeLink shutdown: pkt = %v err = %v"
,
pkt
,
err
)
t
.
Fatalf
(
"Conn.
recvPkt
after NodeLink shutdown: pkt = %v err = %v"
,
pkt
,
err
)
}
err
=
c22
.
Send
(
&
PktBuf
{[]
byte
(
"data"
)})
err
=
c22
.
sendPkt
(
&
PktBuf
{[]
byte
(
"data"
)})
if
xconnError
(
err
)
!=
ErrLinkClosed
{
t
.
Fatalf
(
"Conn.
Send
after NodeLink shutdown: %v"
,
err
)
t
.
Fatalf
(
"Conn.
sendPkt
after NodeLink shutdown: %v"
,
err
)
}
c
,
err
=
nl2
.
NewConn
()
...
...
@@ -460,14 +449,14 @@ func TestNodeLink(t *testing.T) {
xclose
(
c21
)
xclose
(
c22
)
//
Recv/Send
error after Close & NodeLink shutdown
pkt
,
err
=
c21
.
Recv
()
//
recvPkt/sendPkt
error after Close & NodeLink shutdown
pkt
,
err
=
c21
.
recvPkt
()
if
!
(
pkt
==
nil
&&
xconnError
(
err
)
==
ErrClosedConn
)
{
t
.
Fatalf
(
"Conn.
Recv
after close and NodeLink close: pkt = %v err = %v"
,
pkt
,
err
)
t
.
Fatalf
(
"Conn.
recvPkt
after close and NodeLink close: pkt = %v err = %v"
,
pkt
,
err
)
}
err
=
c22
.
Send
(
&
PktBuf
{[]
byte
(
"data"
)})
err
=
c22
.
sendPkt
(
&
PktBuf
{[]
byte
(
"data"
)})
if
xconnError
(
err
)
!=
ErrClosedConn
{
t
.
Fatalf
(
"Conn.
Send
after close and NodeLink close: %v"
,
err
)
t
.
Fatalf
(
"Conn.
sendPkt
after close and NodeLink close: %v"
,
err
)
}
...
...
@@ -477,25 +466,25 @@ func TestNodeLink(t *testing.T) {
wg
.
Gox
(
func
()
{
c
:=
xaccept
(
nl2
)
pkt
:=
xrecv
(
c
)
pkt
:=
xrecv
Pkt
(
c
)
xverifyPkt
(
pkt
,
c
.
connId
,
33
,
[]
byte
(
"ping"
))
// change pkt a bit and send it back
xsend
(
c
,
mkpkt
(
34
,
[]
byte
(
"pong"
)))
xsend
Pkt
(
c
,
mkpkt
(
34
,
[]
byte
(
"pong"
)))
// one more time
pkt
=
xrecv
(
c
)
pkt
=
xrecv
Pkt
(
c
)
xverifyPkt
(
pkt
,
c
.
connId
,
35
,
[]
byte
(
"ping2"
))
xsend
(
c
,
mkpkt
(
36
,
[]
byte
(
"pong2"
)))
xsend
Pkt
(
c
,
mkpkt
(
36
,
[]
byte
(
"pong2"
)))
xclose
(
c
)
})
c
=
xnewconn
(
nl1
)
xsend
(
c
,
mkpkt
(
33
,
[]
byte
(
"ping"
)))
pkt
=
xrecv
(
c
)
xsend
Pkt
(
c
,
mkpkt
(
33
,
[]
byte
(
"ping"
)))
pkt
=
xrecv
Pkt
(
c
)
xverifyPkt
(
pkt
,
c
.
connId
,
34
,
[]
byte
(
"pong"
))
xsend
(
c
,
mkpkt
(
35
,
[]
byte
(
"ping2"
)))
pkt
=
xrecv
(
c
)
xsend
Pkt
(
c
,
mkpkt
(
35
,
[]
byte
(
"ping2"
)))
pkt
=
xrecv
Pkt
(
c
)
xverifyPkt
(
pkt
,
c
.
connId
,
36
,
[]
byte
(
"pong2"
))
xwait
(
wg
)
...
...
@@ -520,13 +509,13 @@ func TestNodeLink(t *testing.T) {
c
:=
xaccept
(
nl2
)
wg
.
Gox
(
func
()
{
pkt
:=
xrecv
(
c
)
pkt
:=
xrecv
Pkt
(
c
)
n
:=
ntoh16
(
pkt
.
Header
()
.
MsgCode
)
x
:=
replyOrder
[
n
]
// wait before it is our turn & echo pkt back
<-
x
.
start
xsend
(
c
,
pkt
)
xsend
Pkt
(
c
,
pkt
)
xclose
(
c
)
...
...
@@ -540,12 +529,12 @@ func TestNodeLink(t *testing.T) {
c1
:=
xnewconn
(
nl1
)
c2
:=
xnewconn
(
nl1
)
xsend
(
c1
,
mkpkt
(
1
,
[]
byte
(
""
)))
xsend
(
c2
,
mkpkt
(
2
,
[]
byte
(
""
)))
xsend
Pkt
(
c1
,
mkpkt
(
1
,
[]
byte
(
""
)))
xsend
Pkt
(
c2
,
mkpkt
(
2
,
[]
byte
(
""
)))
// replies must be coming in reverse order
xechoWait
:=
func
(
c
*
Conn
,
msgCode
uint16
)
{
pkt
:=
xrecv
(
c
)
pkt
:=
xrecv
Pkt
(
c
)
xverifyPkt
(
pkt
,
c
.
connId
,
msgCode
,
[]
byte
(
""
))
}
xechoWait
(
c2
,
2
)
...
...
go/neo/pkt.go
View file @
c2a1b63a
...
...
@@ -26,6 +26,8 @@ import (
// TODO organize rx buffers management (freelist etc)
// PktBuf is a buffer with full raw packet (header + data)
//
// variables of type PktBuf are usually named "pkb" (packet buffer), similar to "skb" in Linux
type
PktBuf
struct
{
Data
[]
byte
// whole packet data including all headers XXX -> Buf ?
}
...
...
@@ -61,7 +63,7 @@ func (pkt *PktBuf) String() string {
s
:=
fmt
.
Sprintf
(
".%d"
,
ntoh32
(
h
.
ConnId
))
msgCode
:=
ntoh16
(
h
.
MsgCode
)
msgType
:=
pkt
TypeRegistry
[
msgCode
]
msgType
:=
msg
TypeRegistry
[
msgCode
]
if
msgType
==
nil
{
s
+=
fmt
.
Sprintf
(
" ? (%d)"
,
msgCode
)
}
else
{
...
...
go/neo/proto-marshal.go
View file @
c2a1b63a
This diff is collapsed.
Click to expand it.
go/neo/proto.go
View file @
c2a1b63a
...
...
@@ -110,7 +110,7 @@ const (
// node finishes to replicate it. It means a partition is moved from 1 node
// to another.
FEEDING
//short: F
// Not really a state: only used in network
packet
s to tell storages to drop
// Not really a state: only used in network
message
s to tell storages to drop
// partitions.
DISCARDED
//short: D
// A check revealed that data differs from other replicas. Cell is neither
...
...
@@ -136,24 +136,24 @@ type NodeUUID int32
// TODO NodeType -> base NodeUUID
// ErrDecodeOverflow is the error returned by NEO
Pkt
Decode when decoding hit buffer overflow
// ErrDecodeOverflow is the error returned by NEO
Msg
Decode when decoding hit buffer overflow
var
ErrDecodeOverflow
=
errors
.
New
(
"decode: bufer overflow"
)
//
Pkt is the interface implemented by NEO packet
s to marshal/unmarshal them into/from wire format
type
Pkt
interface
{
// NEO
PktMsgCode returns message code needed to be used for particular packet
type
//
Msg is the interface implemented by NEO message
s to marshal/unmarshal them into/from wire format
type
Msg
interface
{
// NEO
MsgCode returns message code needed to be used for particular message
type
// on the wire
NEO
Pkt
MsgCode
()
uint16
NEOMsgCode
()
uint16
// NEO
PktEncodedLen returns how much space is needed to encode current state
NEO
Pkt
EncodedLen
()
int
// NEO
MsgEncodedLen returns how much space is needed to encode current message payload
NEO
Msg
EncodedLen
()
int
// NEO
PktEncode encodes current
state into buf.
// len(buf) must be >= NEO
Pkt
EncodedLen()
NEO
Pkt
Encode
(
buf
[]
byte
)
// NEO
MsgEncode encodes current message
state into buf.
// len(buf) must be >= NEO
Msg
EncodedLen()
NEO
Msg
Encode
(
buf
[]
byte
)
// NEO
PktDecode decodes data into current packet stat
e.
NEO
Pkt
Decode
(
data
[]
byte
)
(
nread
int
,
err
error
)
// NEO
MsgDecode decodes data into message in-plac
e.
NEO
Msg
Decode
(
data
[]
byte
)
(
nread
int
,
err
error
)
}
...
...
@@ -163,7 +163,7 @@ type Address struct {
}
// NOTE if Host == "" -> Port not added to wire (see py.PAddress):
// func (a *Address) NEO
Pkt
Encode(b []byte) int {
// func (a *Address) NEO
Msg
Encode(b []byte) int {
// n := string_NEOEncode(a.Host, b[0:])
// if a.Host != "" {
// BigEndian.PutUint16(b[n:], a.Port)
...
...
@@ -263,7 +263,7 @@ type Ping struct {
type
CloseClient
struct
{
}
// Request a node identification. This must be the first
packet
for any
// Request a node identification. This must be the first
message
for any
// connection. Any -> Any.
type
RequestIdentification
struct
{
NodeType
NodeType
// XXX name
...
...
go/neo/proto_test.go
View file @
c2a1b63a
...
...
@@ -68,10 +68,10 @@ func TestPktHeader(t *testing.T) {
}
}
// test marshalling for one
packet
type
func
test
PktMarshal
(
t
*
testing
.
T
,
pkt
Pkt
,
encoded
string
)
{
typ
:=
reflect
.
TypeOf
(
pkt
)
.
Elem
()
// type of *pkt
pkt2
:=
reflect
.
New
(
typ
)
.
Interface
()
.
(
Pkt
)
// test marshalling for one
message
type
func
test
MsgMarshal
(
t
*
testing
.
T
,
msg
Msg
,
encoded
string
)
{
typ
:=
reflect
.
TypeOf
(
msg
)
.
Elem
()
// type of *msg
msg2
:=
reflect
.
New
(
typ
)
.
Interface
()
.
(
Msg
)
defer
func
()
{
if
e
:=
recover
();
e
!=
nil
{
t
.
Errorf
(
"%v: panic ↓↓↓:"
,
typ
)
...
...
@@ -79,10 +79,10 @@ func testPktMarshal(t *testing.T, pkt Pkt, encoded string) {
}
}()
//
pkt
.encode() == expected
msgCode
:=
pkt
.
NEOPkt
MsgCode
()
n
:=
pkt
.
NEOPkt
EncodedLen
()
msgType
:=
pkt
TypeRegistry
[
msgCode
]
//
msg
.encode() == expected
msgCode
:=
msg
.
NEO
MsgCode
()
n
:=
msg
.
NEOMsg
EncodedLen
()
msgType
:=
msg
TypeRegistry
[
msgCode
]
if
msgType
!=
typ
{
t
.
Errorf
(
"%v: msgCode = %v which corresponds to %v"
,
typ
,
msgCode
,
msgType
)
}
...
...
@@ -91,7 +91,7 @@ func testPktMarshal(t *testing.T, pkt Pkt, encoded string) {
}
buf
:=
make
([]
byte
,
n
)
pkt
.
NEOPkt
Encode
(
buf
)
msg
.
NEOMsg
Encode
(
buf
)
if
string
(
buf
)
!=
encoded
{
t
.
Errorf
(
"%v: encode result unexpected:"
,
typ
)
t
.
Errorf
(
"
\t
have: %s"
,
hexpkg
.
EncodeToString
(
buf
))
...
...
@@ -121,13 +121,13 @@ func testPktMarshal(t *testing.T, pkt Pkt, encoded string) {
}
}()
pkt
.
NEOPkt
Encode
(
buf
[
:
l
])
msg
.
NEOMsg
Encode
(
buf
[
:
l
])
}()
}
//
pkt
.decode() == expected
//
msg
.decode() == expected
data
:=
[]
byte
(
encoded
+
"noise"
)
n
,
err
:=
pkt2
.
NEOPkt
Decode
(
data
)
n
,
err
:=
msg2
.
NEOMsg
Decode
(
data
)
if
err
!=
nil
{
t
.
Errorf
(
"%v: decode error %v"
,
typ
,
err
)
}
...
...
@@ -135,13 +135,13 @@ func testPktMarshal(t *testing.T, pkt Pkt, encoded string) {
t
.
Errorf
(
"%v: nread = %v ; want %v"
,
typ
,
n
,
len
(
encoded
))
}
if
!
reflect
.
DeepEqual
(
pkt2
,
pkt
)
{
t
.
Errorf
(
"%v: decode result unexpected: %v ; want %v"
,
typ
,
pkt2
,
pkt
)
if
!
reflect
.
DeepEqual
(
msg2
,
msg
)
{
t
.
Errorf
(
"%v: decode result unexpected: %v ; want %v"
,
typ
,
msg2
,
msg
)
}
// decode must detect buffer overflow
for
l
:=
len
(
encoded
)
-
1
;
l
>=
0
;
l
--
{
n
,
err
=
pkt2
.
NEOPkt
Decode
(
data
[
:
l
])
n
,
err
=
msg2
.
NEOMsg
Decode
(
data
[
:
l
])
if
!
(
n
==
0
&&
err
==
ErrDecodeOverflow
)
{
t
.
Errorf
(
"%v: decode overflow not detected on [:%v]"
,
typ
,
l
)
}
...
...
@@ -149,10 +149,10 @@ func testPktMarshal(t *testing.T, pkt Pkt, encoded string) {
}
}
// test encoding/decoding of
packet
s
func
Test
Pkt
Marshal
(
t
*
testing
.
T
)
{
// test encoding/decoding of
message
s
func
Test
Msg
Marshal
(
t
*
testing
.
T
)
{
var
testv
=
[]
struct
{
pkt
Pkt
msg
Msg
encoded
string
// []byte
}
{
// empty
...
...
@@ -259,25 +259,25 @@ func TestPktMarshal(t *testing.T) {
}
for
_
,
tt
:=
range
testv
{
test
PktMarshal
(
t
,
tt
.
pkt
,
tt
.
encoded
)
test
MsgMarshal
(
t
,
tt
.
msg
,
tt
.
encoded
)
}
}
// For all
packet types: same as testPkt
Marshal but zero-values only
// For all
message types: same as testMsg
Marshal but zero-values only
// this way we additionally lightly check encode / decode overflow behaviour for all types.
func
Test
Pkt
MarshalAllOverflowLightly
(
t
*
testing
.
T
)
{
for
_
,
typ
:=
range
pkt
TypeRegistry
{
func
Test
Msg
MarshalAllOverflowLightly
(
t
*
testing
.
T
)
{
for
_
,
typ
:=
range
msg
TypeRegistry
{
// zero-value for a type
pkt
:=
reflect
.
New
(
typ
)
.
Interface
()
.
(
Pkt
)
l
:=
pkt
.
NEOPkt
EncodedLen
()
msg
:=
reflect
.
New
(
typ
)
.
Interface
()
.
(
Msg
)
l
:=
msg
.
NEOMsg
EncodedLen
()
zerol
:=
make
([]
byte
,
l
)
// decoding will turn nil slice & map into empty allocated ones.
// we need it so that reflect.DeepEqual works for
pkt
encode/decode comparison
n
,
err
:=
pkt
.
NEOPkt
Decode
(
zerol
)
// we need it so that reflect.DeepEqual works for
msg
encode/decode comparison
n
,
err
:=
msg
.
NEOMsg
Decode
(
zerol
)
if
!
(
n
==
l
&&
err
==
nil
)
{
t
.
Errorf
(
"%v: zero-decode unexpected: %v, %v ; want %v, nil"
,
typ
,
n
,
err
,
l
)
}
test
PktMarshal
(
t
,
pkt
,
string
(
zerol
))
test
MsgMarshal
(
t
,
msg
,
string
(
zerol
))
}
}
go/neo/protogen.go
View file @
c2a1b63a
...
...
@@ -20,15 +20,15 @@
/*
NEO. Protocol module. Code generator
This program generates marshalling code for
packet
types defined in proto.go .
For every type 4 methods are generated in accordance with neo.
Pkt
interface:
This program generates marshalling code for
message
types defined in proto.go .
For every type 4 methods are generated in accordance with neo.
Msg
interface:
NEO
Pkt
MsgCode() uint16
NEO
Pkt
EncodedLen() int
NEO
Pkt
Encode(buf []byte)
NEO
Pkt
Decode(data []byte) (nread int, err error)
NEOMsgCode() uint16
NEO
Msg
EncodedLen() int
NEO
Msg
Encode(buf []byte)
NEO
Msg
Decode(data []byte) (nread int, err error)
List of
packet
types is obtained via searching through proto.go AST - looking
List of
message
types is obtained via searching through proto.go AST - looking
for appropriate struct declarations there.
Code generation for a type is organized via recursively walking through type's
...
...
@@ -169,11 +169,11 @@ import (
"../zodb"
)`
)
pktTypeRegistry
:=
map
[
int
]
string
{}
// pkt
Code -> typename
msgTypeRegistry
:=
map
[
int
]
string
{}
// msg
Code -> typename
// go over
packet
types declaration and generate marshal code for them
buf
.
emit
(
"//
packet
s marshalling
\n
"
)
pkt
Code
:=
0
// go over
message
types declaration and generate marshal code for them
buf
.
emit
(
"//
message
s marshalling
\n
"
)
msg
Code
:=
0
for
_
,
decl
:=
range
f
.
Decls
{
// we look for types (which can be only under GenDecl)
gendecl
,
ok
:=
decl
.
(
*
ast
.
GenDecl
)
...
...
@@ -195,35 +195,35 @@ import (
continue
case
*
ast
.
StructType
:
fmt
.
Fprintf
(
&
buf
,
"// %d. %s
\n\n
"
,
pkt
Code
,
typename
)
fmt
.
Fprintf
(
&
buf
,
"// %d. %s
\n\n
"
,
msg
Code
,
typename
)
buf
.
emit
(
"func (_ *%s) NEO
Pkt
MsgCode() uint16 {"
,
typename
)
buf
.
emit
(
"return %d"
,
pkt
Code
)
buf
.
emit
(
"func (_ *%s) NEOMsgCode() uint16 {"
,
typename
)
buf
.
emit
(
"return %d"
,
msg
Code
)
buf
.
emit
(
"}
\n
"
)
buf
.
WriteString
(
generateCodecCode
(
typespec
,
&
sizer
{}))
buf
.
WriteString
(
generateCodecCode
(
typespec
,
&
encoder
{}))
buf
.
WriteString
(
generateCodecCode
(
typespec
,
&
decoder
{}))
pktTypeRegistry
[
pkt
Code
]
=
typename
pkt
Code
++
msgTypeRegistry
[
msg
Code
]
=
typename
msg
Code
++
}
}
}
// now generate
packet
types registry
buf
.
emit
(
"
\n
// registry of
packet
types"
)
buf
.
emit
(
"var
pktTypeRegistry = map[uint16]reflect.Type {"
)
// XXX key -> Pkt
Code ?
// now generate
message
types registry
buf
.
emit
(
"
\n
// registry of
message
types"
)
buf
.
emit
(
"var
msgTypeRegistry = map[uint16]reflect.Type {"
)
// XXX key -> Msg
Code ?
// ordered by
pkt
Code
pkt
CodeV
:=
[]
int
{}
for
pktCode
:=
range
pkt
TypeRegistry
{
pktCodeV
=
append
(
pktCodeV
,
pkt
Code
)
// ordered by
msg
Code
msg
CodeV
:=
[]
int
{}
for
msgCode
:=
range
msg
TypeRegistry
{
msgCodeV
=
append
(
msgCodeV
,
msg
Code
)
}
sort
.
Ints
(
pkt
CodeV
)
sort
.
Ints
(
msg
CodeV
)
for
_
,
pktCode
:=
range
pkt
CodeV
{
buf
.
emit
(
"%v: reflect.TypeOf(%v{}),"
,
pktCode
,
pktTypeRegistry
[
pkt
Code
])
for
_
,
msgCode
:=
range
msg
CodeV
{
buf
.
emit
(
"%v: reflect.TypeOf(%v{}),"
,
msgCode
,
msgTypeRegistry
[
msg
Code
])
}
buf
.
emit
(
"}"
)
...
...
@@ -456,21 +456,21 @@ func (o *OverflowCheck) AddExpr(format string, a ...interface{}) {
}
// sizer generates code to compute encoded size of a
packet
// sizer generates code to compute encoded size of a
message
//
// when type is recursively walked, for every case symbolic size is added appropriately.
// in case when it was needed to generate loops, runtime accumulator variable is additionally used.
// result is: symbolic size + (optionally) runtime accumulator.
type
sizer
struct
{
commonCodeGen
size
SymSize
// currently accumulated
packet
size
size
SymSize
// currently accumulated size
}
// encoder generates code to encode a
packet
// encoder generates code to encode a
message
//
// when type is recursively walked, for every case code to update `data[n:]` is generated.
// no overflow checks are generated as by neo.
Pkt
interface provided data
// buffer should have at least payloadLen length returned by NEO
Pkt
EncodedInfo()
// no overflow checks are generated as by neo.
Msg
interface provided data
// buffer should have at least payloadLen length returned by NEO
Msg
EncodedInfo()
// (the size computed by sizer).
//
// the code emitted looks like:
...
...
@@ -479,14 +479,14 @@ type sizer struct {
// encode<typ2>(data[n2:], path2)
// ...
//
// TODO encode have to care in NEO
Pkt
Encode to emit preambule such that bound
// TODO encode have to care in NEO
Msg
Encode to emit preambule such that bound
// checking is performed only once (currenty compiler emits many of them)
type
encoder
struct
{
commonCodeGen
n
int
// current write position in data
}
// decoder generates code to decode a
packet
// decoder generates code to decode a
message
//
// when type is recursively walked, for every case code to decode next item from
// `data[n:]` is generated.
...
...
@@ -527,7 +527,7 @@ var _ CodeGenerator = (*decoder)(nil)
func
(
s
*
sizer
)
generatedCode
()
string
{
code
:=
Buffer
{}
// prologue
code
.
emit
(
"func (%s *%s) NEO
Pkt
EncodedLen() int {"
,
s
.
recvName
,
s
.
typeName
)
code
.
emit
(
"func (%s *%s) NEO
Msg
EncodedLen() int {"
,
s
.
recvName
,
s
.
typeName
)
if
s
.
varUsed
[
"size"
]
{
code
.
emit
(
"var %s int"
,
s
.
var_
(
"size"
))
}
...
...
@@ -548,7 +548,7 @@ func (s *sizer) generatedCode() string {
func
(
e
*
encoder
)
generatedCode
()
string
{
code
:=
Buffer
{}
// prologue
code
.
emit
(
"func (%s *%s) NEO
Pkt
Encode(data []byte) {"
,
e
.
recvName
,
e
.
typeName
)
code
.
emit
(
"func (%s *%s) NEO
Msg
Encode(data []byte) {"
,
e
.
recvName
,
e
.
typeName
)
code
.
Write
(
e
.
buf
.
Bytes
())
...
...
@@ -655,7 +655,7 @@ func (d *decoder) generatedCode() string {
code
:=
Buffer
{}
// prologue
code
.
emit
(
"func (%s *%s) NEO
Pkt
Decode(data []byte) (int, error) {"
,
d
.
recvName
,
d
.
typeName
)
code
.
emit
(
"func (%s *%s) NEO
Msg
Decode(data []byte) (int, error) {"
,
d
.
recvName
,
d
.
typeName
)
if
d
.
varUsed
[
"nread"
]
{
code
.
emit
(
"var %v uint32"
,
d
.
var_
(
"nread"
))
}
...
...
go/neo/server/master.go
View file @
c2a1b63a
...
...
@@ -64,7 +64,7 @@ type Master struct {
type
nodeCome
struct
{
link
*
neo
.
NodeLink
idReq
neo
.
RequestIdentification
// we received this identification request
idResp
chan
neo
.
Pkt
// what we reply (AcceptIdentification | Error)
idResp
chan
neo
.
Msg
// what we reply (AcceptIdentification | Error)
}
// node disconnects
...
...
@@ -701,7 +701,7 @@ func (m *Master) ServeLink(ctx context.Context, link *neo.NodeLink) {
}
// convey identification request to master
idRespCh
:=
make
(
chan
neo
.
Pkt
)
idRespCh
:=
make
(
chan
neo
.
Msg
)
m
.
nodeCome
<-
nodeCome
{
link
,
idReq
,
idRespCh
}
idResp
:=
<-
idRespCh
...
...
@@ -757,7 +757,7 @@ func (m *Master) ServeLink(ctx context.Context, link *neo.NodeLink) {
m
.
stateMu
.
Unlock
()
go
func
()
{
var
pkt
neo
.
Pkt
var
msg
neo
.
Msg
for
{
select
{
...
...
@@ -767,7 +767,7 @@ func (m *Master) ServeLink(ctx context.Context, link *neo.NodeLink) {
return
case
nodeUpdateV
:=
<-
nodeCh
:
pkt
=
&
neo
.
NotifyNodeInformation
{
msg
=
&
neo
.
NotifyNodeInformation
{
IdTimestamp
:
math
.
NaN
(),
// XXX
NodeList
:
nodeUpdateV
,
}
...
...
@@ -776,7 +776,7 @@ func (m *Master) ServeLink(ctx context.Context, link *neo.NodeLink) {
// changed = true
}
err
=
neo
.
EncodeAndSend
(
connNotify
,
pkt
)
err
=
neo
.
EncodeAndSend
(
connNotify
,
msg
)
if
err
!=
nil
{
// XXX err
}
...
...
go/neo/server/storage.go
View file @
c2a1b63a
...
...
@@ -277,7 +277,7 @@ func (stor *Storage) ServeClient(ctx context.Context, conn *neo.Conn) {
xid
.
TidBefore
=
true
}
var
reply
neo
.
Pkt
var
reply
neo
.
Msg
data
,
tid
,
err
:=
stor
.
zstor
.
Load
(
xid
)
if
err
!=
nil
{
// TODO translate err to NEO protocol error codes
...
...
@@ -299,7 +299,7 @@ func (stor *Storage) ServeClient(ctx context.Context, conn *neo.Conn) {
neo
.
EncodeAndSend
(
conn
,
reply
)
// XXX err
case
*
neo
.
LastTransaction
:
var
reply
neo
.
Pkt
var
reply
neo
.
Msg
lastTid
,
err
:=
stor
.
zstor
.
LastTid
()
if
err
!=
nil
{
...
...
go/neo/x_connection.go
View file @
c2a1b63a
...
...
@@ -10,9 +10,9 @@ import (
"lab.nexedi.com/kirr/go123/xerr"
)
// Recv
AndDecode receives packet from conn and decodes
it
func
RecvAndDecode
(
conn
*
Conn
)
(
Pkt
,
error
)
{
pkt
,
err
:=
conn
.
Recv
()
// Recv
receives packet and decodes message from
it
func
RecvAndDecode
(
conn
*
Conn
)
(
Msg
,
error
)
{
pkt
,
err
:=
conn
.
recvPkt
()
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -20,7 +20,7 @@ func RecvAndDecode(conn *Conn) (Pkt, error) {
// decode packet
pkth
:=
pkt
.
Header
()
msgCode
:=
ntoh16
(
pkth
.
MsgCode
)
msgType
:=
pkt
TypeRegistry
[
msgCode
]
msgType
:=
msg
TypeRegistry
[
msgCode
]
if
msgType
==
nil
{
err
=
fmt
.
Errorf
(
"invalid msgCode (%d)"
,
msgCode
)
// XXX -> ProtoError ?
...
...
@@ -28,47 +28,47 @@ func RecvAndDecode(conn *Conn) (Pkt, error) {
}
// TODO use free-list for decoded packets + when possible decode in-place
pktObj
:=
reflect
.
New
(
msgType
)
.
Interface
()
.
(
Pkt
)
_
,
err
=
pktObj
.
NEOPkt
Decode
(
pkt
.
Payload
())
msg
:=
reflect
.
New
(
msgType
)
.
Interface
()
.
(
Msg
)
_
,
err
=
msg
.
NEOMsg
Decode
(
pkt
.
Payload
())
if
err
!=
nil
{
// XXX -> ProtoError ?
return
nil
,
&
ConnError
{
Conn
:
conn
,
Op
:
"decode"
,
Err
:
err
}
}
return
pktObj
,
nil
return
msg
,
nil
}
// EncodeAndSend encodes
pkt and sends it to conn
func
EncodeAndSend
(
conn
*
Conn
,
pkt
Pkt
)
error
{
l
:=
pkt
.
NEOPkt
EncodedLen
()
// EncodeAndSend encodes
message into packet and sends it
func
EncodeAndSend
(
conn
*
Conn
,
msg
Msg
)
error
{
l
:=
msg
.
NEOMsg
EncodedLen
()
buf
:=
PktBuf
{
make
([]
byte
,
PktHeadLen
+
l
)}
// XXX -> freelist
h
:=
buf
.
Header
()
// h.ConnId will be set by conn.Send
h
.
MsgCode
=
hton16
(
pkt
.
NEOPkt
MsgCode
())
h
.
MsgCode
=
hton16
(
msg
.
NEO
MsgCode
())
h
.
MsgLen
=
hton32
(
uint32
(
l
))
// XXX casting: think again
pkt
.
NEOPkt
Encode
(
buf
.
Payload
())
msg
.
NEOMsg
Encode
(
buf
.
Payload
())
return
conn
.
Send
(
&
buf
)
// XXX why pointer?
return
conn
.
sendPkt
(
&
buf
)
// XXX why pointer?
}
// Ask does simple request/response protocol exchange
// It expects the answer to be exactly of resp type and errors otherwise
func
Ask
(
conn
*
Conn
,
req
Pkt
,
resp
Pkt
)
error
{
func
Ask
(
conn
*
Conn
,
req
Msg
,
resp
Msg
)
error
{
err
:=
EncodeAndSend
(
conn
,
req
)
if
err
!=
nil
{
return
err
}
err
=
Expect
(
conn
,
resp
)
err
=
Expect
(
conn
,
resp
)
// XXX +Error
return
err
}
// ProtoError is returned when there wa
a
a protocol error, like receiving
// ProtoError is returned when there wa
s
a protocol error, like receiving
// unexpected packet or packet with wrong header
//
XXX -> ConnError{Op: "decode"} ?
//
FIXME -> ConnError{Op: "decode"}
type
ProtoError
struct
{
Conn
*
Conn
Err
error
...
...
@@ -80,8 +80,8 @@ func (e *ProtoError) Error() string {
// Expect receives 1 packet and expects it to be exactly of msg type
// XXX naming (-> Recv1 ?)
func
Expect
(
conn
*
Conn
,
msg
Pkt
)
(
err
error
)
{
pkt
,
err
:=
conn
.
Recv
()
func
Expect
(
conn
*
Conn
,
msg
Msg
)
(
err
error
)
{
pkt
,
err
:=
conn
.
recvPkt
()
if
err
!=
nil
{
return
err
}
...
...
@@ -92,11 +92,11 @@ func Expect(conn *Conn, msg Pkt) (err error) {
pkth
:=
pkt
.
Header
()
msgCode
:=
ntoh16
(
pkth
.
MsgCode
)
if
msgCode
!=
msg
.
NEO
Pkt
MsgCode
()
{
if
msgCode
!=
msg
.
NEOMsgCode
()
{
// unexpected Error response
if
msgCode
==
(
&
Error
{})
.
NEO
Pkt
MsgCode
()
{
if
msgCode
==
(
&
Error
{})
.
NEOMsgCode
()
{
errResp
:=
Error
{}
_
,
err
=
errResp
.
NEO
Pkt
Decode
(
pkt
.
Payload
())
_
,
err
=
errResp
.
NEO
Msg
Decode
(
pkt
.
Payload
())
if
err
!=
nil
{
return
&
ProtoError
{
conn
,
err
}
}
...
...
@@ -108,7 +108,7 @@ func Expect(conn *Conn, msg Pkt) (err error) {
return
ErrDecode
(
&
errResp
)
// XXX err ctx vs ^^^ errcontextf ?
}
msgType
:=
pkt
TypeRegistry
[
msgCode
]
msgType
:=
msg
TypeRegistry
[
msgCode
]
if
msgType
==
nil
{
return
&
ProtoError
{
conn
,
fmt
.
Errorf
(
"invalid msgCode (%d)"
,
msgCode
)}
}
...
...
@@ -116,7 +116,7 @@ func Expect(conn *Conn, msg Pkt) (err error) {
return
&
ProtoError
{
conn
,
fmt
.
Errorf
(
"unexpected packet: %v"
,
msgType
)}
}
_
,
err
=
msg
.
NEO
Pkt
Decode
(
pkt
.
Payload
())
_
,
err
=
msg
.
NEO
Msg
Decode
(
pkt
.
Payload
())
if
err
!=
nil
{
return
&
ProtoError
{
conn
,
err
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment