func (s *Socket) pingTimeoutHandler() {
	defer func() {
		s.pingTimeout.Stop()
	}()
	select {
	case <-s.pingTimeout.C:
		s.bs.Close()
	case <-s.isClosedChan:
	}
}
Close the socket during a ping response timeout.
package glue
import (
	"encoding/json"
	"errors"
	"fmt"
	"runtime/debug"
	"sync"
	"time"
	"github.com/Sirupsen/logrus"
	"github.com/blang/semver"
	"github.com/desertbit/glue/backend"
	"github.com/desertbit/glue/log"
	"github.com/desertbit/glue/utils"
)
const (
	Version = "1.8.0"
)
const (
	socketIDLength = 20
	pingPeriod = 30 * time.Second
	pingResponseTimeout = 7 * time.Second
	mainChannelName = "m"
	cmdLen               = 2
	cmdInit              = "in"
	cmdPing              = "pi"
	cmdPong              = "po"
	cmdClose             = "cl"
	cmdInvalid           = "iv"
	cmdDontAutoReconnect = "dr"
	cmdChannelData       = "cd"
)
var (
	ErrSocketClosed = errors.New("the socket connection is closed")
	ErrReadTimeout  = errors.New("the read timeout was reached")
)
var (
	serverVersion semver.Version
)
type ClosedChan <-chan struct{}
type OnCloseFunc func()
type OnReadFunc func(data string)
type initData struct {
	SocketID string `json:"socketID"`
}
type clientInitData struct {
	Version string `json:"version"`
}
type Socket struct {
	Value interface{}
	server *Server
	bs     backend.BackendSocket
	id            string  
	isInitialized bool
	channels    *channels
	mainChannel *Channel
	writeChan    chan string
	readChan     chan string
	isClosedChan chan struct{}
	onCloseFunc OnCloseFunc
	pingTimer         *time.Timer
	pingTimeout       *time.Timer
	sendPingMutex     sync.Mutex
	pingRequestActive bool
}
func newSocket(server *Server, bs backend.BackendSocket) *Socket {
	s := &Socket{
		server: server,
		bs:     bs,
		id:       utils.RandomString(socketIDLength),
		channels: newChannels(),
		writeChan:    bs.WriteChan(),
		readChan:     bs.ReadChan(),
		isClosedChan: make(chan struct{}),
		pingTimer:   time.NewTimer(pingPeriod),
		pingTimeout: time.NewTimer(pingResponseTimeout),
	}
	s.mainChannel = s.Channel(mainChannelName)
	bs.OnClose(s.onClose)
	s.pingTimeout.Stop()
	func() {
		s.server.socketsMutex.Lock()
		defer s.server.socketsMutex.Unlock()
		for {
			if _, ok := s.server.sockets[s.id]; !ok {
				break
			}
			s.id = utils.RandomString(socketIDLength)
		}
		s.server.sockets[s.id] = s
	}()
	go s.pingTimeoutHandler()
	go s.readLoop()
	go s.pingLoop()
	return s
}
func (s *Socket) ID() string {
	return s.id
}
func (s *Socket) IsInitialized() bool {
	return s.isInitialized
}
func (s *Socket) RemoteAddr() string {
	return s.bs.RemoteAddr()
}
func (s *Socket) UserAgent() string {
	return s.bs.UserAgent()
}
func (s *Socket) Close() {
	s.bs.Close()
}
func (s *Socket) IsClosed() bool {
	return s.bs.IsClosed()
}
func (s *Socket) OnClose(f OnCloseFunc) {
	s.onCloseFunc = f
}
func (s *Socket) ClosedChan() ClosedChan {
	return s.isClosedChan
}
func (s *Socket) Write(data string) {
	s.mainChannel.Write(data)
}
func (s *Socket) Read(timeout ...time.Duration) (string, error) {
	return s.mainChannel.Read(timeout...)
}
func (s *Socket) OnRead(f OnReadFunc) {
	s.mainChannel.OnRead(f)
}
func (s *Socket) DiscardRead() {
	s.mainChannel.DiscardRead()
}
func (s *Socket) write(rawData string) {
	select {
	case <-s.isClosedChan:
		return
	case s.writeChan <- rawData:
	default:
		s.sendPing()
		s.writeChan <- rawData
	}
}
func (s *Socket) onClose() {
	func() {
		s.server.socketsMutex.Lock()
		defer s.server.socketsMutex.Unlock()
		delete(s.server.sockets, s.id)
	}()
	close(s.isClosedChan)
	for i := 0; i < len(s.writeChan); i++ {
		select {
		case <-s.writeChan:
		default:
			break
		}
	}
	if s.onCloseFunc != nil {
		func() {
			defer func() {
				if e := recover(); e != nil {
					log.L.Errorf("glue: panic while calling onClose function: %v\n%s", e, debug.Stack())
				}
			}()
			s.onCloseFunc()
		}()
	}
}
func (s *Socket) resetPingTimeout() {
	s.sendPingMutex.Lock()
	defer s.sendPingMutex.Unlock()
	s.pingTimeout.Stop()
	s.pingRequestActive = false
	s.pingTimer.Reset(pingPeriod)
}
func (s *Socket) sendPing() {
	s.sendPingMutex.Lock()
	if s.pingRequestActive {
		s.sendPingMutex.Unlock()
		return
	}
	s.pingRequestActive = true
	s.sendPingMutex.Unlock()
	s.pingTimeout.Reset(pingResponseTimeout)
	s.writeChan <- cmdPing
}