ping.go 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. /*
  2. * Copyright (c) 2013 IBM Corp.
  3. *
  4. * All rights reserved. This program and the accompanying materials
  5. * are made available under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution, and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Seth Hoenig
  11. * Allan Stockdill-Mander
  12. * Mike Robertson
  13. */
  14. package mqtt
  15. import (
  16. "errors"
  17. "io"
  18. "sync/atomic"
  19. "time"
  20. "github.com/eclipse/paho.mqtt.golang/packets"
  21. )
  22. // keepalive - Send ping when connection unused for set period
  23. // connection passed in to avoid race condition on shutdown
  24. func keepalive(c *client, conn io.Writer) {
  25. defer c.workers.Done()
  26. DEBUG.Println(PNG, "keepalive starting")
  27. var checkInterval int64
  28. var pingSent time.Time
  29. if c.options.KeepAlive > 10 {
  30. checkInterval = 5
  31. } else {
  32. checkInterval = c.options.KeepAlive / 2
  33. }
  34. intervalTicker := time.NewTicker(time.Duration(checkInterval * int64(time.Second)))
  35. defer intervalTicker.Stop()
  36. for {
  37. select {
  38. case <-c.stop:
  39. DEBUG.Println(PNG, "keepalive stopped")
  40. return
  41. case <-intervalTicker.C:
  42. lastSent := c.lastSent.Load().(time.Time)
  43. lastReceived := c.lastReceived.Load().(time.Time)
  44. DEBUG.Println(PNG, "ping check", time.Since(lastSent).Seconds())
  45. if time.Since(lastSent) >= time.Duration(c.options.KeepAlive*int64(time.Second)) || time.Since(lastReceived) >= time.Duration(c.options.KeepAlive*int64(time.Second)) {
  46. if atomic.LoadInt32(&c.pingOutstanding) == 0 {
  47. DEBUG.Println(PNG, "keepalive sending ping")
  48. ping := packets.NewControlPacket(packets.Pingreq).(*packets.PingreqPacket)
  49. // We don't want to wait behind large messages being sent, the Write call
  50. // will block until it it able to send the packet.
  51. atomic.StoreInt32(&c.pingOutstanding, 1)
  52. if err := ping.Write(conn); err != nil {
  53. ERROR.Println(PNG, err)
  54. }
  55. c.lastSent.Store(time.Now())
  56. pingSent = time.Now()
  57. }
  58. }
  59. if atomic.LoadInt32(&c.pingOutstanding) > 0 && time.Since(pingSent) >= c.options.PingTimeout {
  60. CRITICAL.Println(PNG, "pingresp not received, disconnecting")
  61. c.internalConnLost(errors.New("pingresp not received, disconnecting")) // no harm in calling this if the connection is already down (or shutdown is in progress)
  62. return
  63. }
  64. }
  65. }
  66. }