|
@@ -3,6 +3,7 @@ package coap
|
|
import (
|
|
import (
|
|
"net"
|
|
"net"
|
|
"sparrow/pkg/server"
|
|
"sparrow/pkg/server"
|
|
|
|
+ "sync/atomic"
|
|
"time"
|
|
"time"
|
|
)
|
|
)
|
|
|
|
|
|
@@ -14,30 +15,23 @@ const (
|
|
ResponseRandomFactor = 1.5
|
|
ResponseRandomFactor = 1.5
|
|
// MaxRetransmit is the maximum number of times a message will
|
|
// MaxRetransmit is the maximum number of times a message will
|
|
// be retransmitted.
|
|
// be retransmitted.
|
|
- MaxRetransmit = 4
|
|
|
|
- maxPktlen = 1500
|
|
|
|
|
|
+ MaxRetransmit = 4
|
|
|
|
+ maxPktlen = 1500
|
|
|
|
+ maxWorkersCount = 10000
|
|
|
|
+ idleWorkerTimeout = 10 * time.Second
|
|
)
|
|
)
|
|
|
|
|
|
-type Handler interface {
|
|
|
|
- ServeCOAP(l *net.UDPConn, a *net.UDPAddr, m Message) Message
|
|
|
|
-}
|
|
|
|
type Manager struct {
|
|
type Manager struct {
|
|
- rh funcHandler
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-func NewManager() *Manager {
|
|
|
|
- return &Manager{}
|
|
|
|
|
|
+ queue chan *Request
|
|
|
|
+ Provider Provider
|
|
|
|
+ workersCount int32
|
|
}
|
|
}
|
|
|
|
|
|
-func (m *Manager) FuncHandler(f func(l *net.UDPConn, a *net.UDPAddr, m Message) Message) Handler {
|
|
|
|
- m.rh = f
|
|
|
|
- return funcHandler(f)
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-type funcHandler func(l *net.UDPConn, a *net.UDPAddr, m Message) Message
|
|
|
|
-
|
|
|
|
-func (f funcHandler) ServeCOAP(l *net.UDPConn, a *net.UDPAddr, m Message) Message {
|
|
|
|
- return f(l, a, m)
|
|
|
|
|
|
+func NewManager(p Provider) *Manager {
|
|
|
|
+ return &Manager{
|
|
|
|
+ Provider: p,
|
|
|
|
+ queue: make(chan *Request),
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
func (m *Manager) Handler(conn *net.UDPConn) {
|
|
func (m *Manager) Handler(conn *net.UDPConn) {
|
|
@@ -53,31 +47,78 @@ func (m *Manager) Handler(conn *net.UDPConn) {
|
|
}
|
|
}
|
|
tmp := make([]byte, nr)
|
|
tmp := make([]byte, nr)
|
|
copy(tmp, buf)
|
|
copy(tmp, buf)
|
|
- go m.handlerPacket(conn, tmp, addr)
|
|
|
|
|
|
+ msg, err := ParseMessage(tmp)
|
|
|
|
+ if err != nil {
|
|
|
|
+ server.Log.Error(err)
|
|
|
|
+ }
|
|
|
|
+ m.spawnWorker(&Request{
|
|
|
|
+ Msg: msg,
|
|
|
|
+ Addr: addr,
|
|
|
|
+ Conn: conn,
|
|
|
|
+ })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-func (m *Manager) handlerPacket(l *net.UDPConn, data []byte, a *net.UDPAddr) {
|
|
|
|
- msg, err := ParseMessage(data)
|
|
|
|
- if err != nil {
|
|
|
|
- server.Log.Error(err)
|
|
|
|
|
|
+func (m *Manager) worker(w *Request) {
|
|
|
|
+ m.serve(w)
|
|
|
|
+ for {
|
|
|
|
+ count := atomic.LoadInt32(&m.workersCount)
|
|
|
|
+ if count > maxWorkersCount {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if atomic.CompareAndSwapInt32(&m.workersCount, count, count+1) {
|
|
|
|
+ break
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- rv := m.rh.ServeCOAP(l, a, msg)
|
|
|
|
- if rv != nil {
|
|
|
|
- Transmit(l, a, msg)
|
|
|
|
|
|
+ defer atomic.AddInt32(&m.workersCount, -1)
|
|
|
|
+ inUse := false
|
|
|
|
+ timeout := time.NewTimer(idleWorkerTimeout)
|
|
|
|
+ defer timeout.Stop()
|
|
|
|
+ for m.workerChannelHandler(inUse, timeout) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-func Transmit(l *net.UDPConn, a *net.UDPAddr, m Message) error {
|
|
|
|
- d, err := m.Encode()
|
|
|
|
- if err != nil {
|
|
|
|
- return err
|
|
|
|
|
|
+func (m *Manager) workerChannelHandler(inUse bool, timeout *time.Timer) bool {
|
|
|
|
+ select {
|
|
|
|
+ case w, ok := <-m.queue:
|
|
|
|
+ if !ok {
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+ inUse = true
|
|
|
|
+ m.serve(w)
|
|
|
|
+ case <-timeout.C:
|
|
|
|
+ if !inUse {
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+ inUse = false
|
|
|
|
+ timeout.Reset(idleWorkerTimeout)
|
|
}
|
|
}
|
|
-
|
|
|
|
- if a == nil {
|
|
|
|
- _, err = l.Write(d)
|
|
|
|
- } else {
|
|
|
|
- _, err = l.WriteTo(d, a)
|
|
|
|
|
|
+ return true
|
|
|
|
+}
|
|
|
|
+func (m *Manager) serve(w *Request) {
|
|
|
|
+ msg := w.Msg
|
|
|
|
+ server.Log.Debugf("get packet:%#v", msg)
|
|
|
|
+ // check token
|
|
|
|
+ if msg.IsConfirmable() {
|
|
|
|
+ token := msg.GetToken()
|
|
|
|
+ if len(token) != 8 {
|
|
|
|
+ res := &BaseMessage{
|
|
|
|
+ Code: Unauthorized,
|
|
|
|
+ Type: ACK,
|
|
|
|
+ MessageID: msg.GetMessageID(),
|
|
|
|
+ Token: msg.GetToken(),
|
|
|
|
+ }
|
|
|
|
+ bytes, _ := res.Encode()
|
|
|
|
+ w.Conn.WriteTo(bytes, w.Addr)
|
|
|
|
+ server.Log.Debugf("token length error, size :%d", len(token))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+func (m *Manager) spawnWorker(req *Request) {
|
|
|
|
+ select {
|
|
|
|
+ case m.queue <- req:
|
|
|
|
+ default:
|
|
|
|
+ go m.serve(req)
|
|
}
|
|
}
|
|
- return err
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// Receive a message.
|
|
// Receive a message.
|