forked from xiegeo/modbusone
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tcp_server.go
126 lines (113 loc) · 3.04 KB
/
tcp_server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package modbusone
import (
"fmt"
"io"
"net"
)
const (
TCPHeaderLength = 6
MBAPHeaderLength = TCPHeaderLength + 1
)
// TCPServer implements Server/Slave side logic for Modbus over TCP to
// be used by a ProtocolHandler.
type TCPServer struct {
listener net.Listener
}
// NewTCPServer runs TCP server.
func NewTCPServer(listener net.Listener) *TCPServer {
s := TCPServer{
listener: listener,
}
return &s
}
func readTCP(r io.Reader, bs []byte) (n int, err error) {
n, err = io.ReadFull(r, bs[:TCPHeaderLength])
if err != nil {
return n, err
}
if bs[2] != 0 || bs[3] != 0 {
return n, fmt.Errorf("MBAP protocol of %X %X is unknown", bs[2], bs[3])
}
l := int(bs[4])*256 + int(bs[5])
if l <= 2 {
return n, fmt.Errorf("MBAP data length of %v is too short, bs:%x", l, bs[:n])
}
if len(bs) < l+TCPHeaderLength {
return n, fmt.Errorf("MBAP data length of %v is too long", l)
}
n, err = io.ReadFull(r, bs[TCPHeaderLength:l+TCPHeaderLength])
return n + TCPHeaderLength, err
}
// writeTCP writes a PDU packet on TCP reusing the headers and buffer space in bs.
func writeTCP(w io.Writer, bs []byte, pdu PDU) (int, error) {
l := len(pdu) + 1 // PDU + byte of slaveID
bs[4] = byte(l / 256)
bs[5] = byte(l)
copy(bs[MBAPHeaderLength:], pdu)
return w.Write(bs[:len(pdu)+MBAPHeaderLength])
}
// Serve runs the server and only returns after a connection or data error occurred.
// The underling connection is always closed before this function returns.
func (s *TCPServer) Serve(handler ProtocolHandler) error {
defer s.Close()
wec := func(conn net.Conn, bs []byte, req PDU, err error) {
writeTCP(conn, bs, ExceptionReplyPacket(req, ToExceptionCode(err)))
}
for {
conn, err := s.listener.Accept()
if err != nil {
return err
}
go func(conn net.Conn) {
defer conn.Close()
var rb []byte
if OverSizeSupport {
rb = make([]byte, MBAPHeaderLength+OverSizeMaxRTU+TCPHeaderLength)
} else {
rb = make([]byte, MBAPHeaderLength+MaxPDUSize)
}
for {
n, err := readTCP(conn, rb)
if err != nil {
debugf("readTCP %v\n", err)
return
}
p := PDU(rb[MBAPHeaderLength:n])
err = p.ValidateRequest()
if err != nil {
debugf("ValidateRequest %v\n", err)
return
}
fc := p.GetFunctionCode()
if fc.IsReadToServer() {
data, err := handler.OnRead(p)
if err != nil {
debugf("TCPServer handler.OnOutput error:%v\n", err)
wec(conn, rb, p, err)
continue
}
writeTCP(conn, rb, p.MakeReadReply(data))
} else if fc.IsWriteToServer() {
data, err := p.GetRequestValues()
if err != nil {
debugf("p:%v\n", p)
debugf("TCPServer p.GetRequestValues error:%v\n", err)
wec(conn, rb, p, err)
continue
}
err = handler.OnWrite(p, data)
if err != nil {
debugf("TCPServer handler.OnInput error:%v\n", err)
wec(conn, rb, p, err)
continue
}
writeTCP(conn, rb, p.MakeWriteReply())
}
}
}(conn)
}
}
// Close closes the server and closes the listener.
func (s *TCPServer) Close() error {
return s.listener.Close()
}