func (c *Client) Subscribe(topic string, qos byte, callback MessageHandler) Token { token := newToken(Message.Subscribe).(*SubscribeToken) if !c.IsConnected() { token.err = errors.New("Not Connected.") token.flowComplete() return token } sub := Message.NewControlPacket(Message.Subscribe).(*Message.SubscribePacket) if err := validateTopicAndQos(topic, qos); err != nil { token.err = err token.flowComplete() return token } sub.Topics = append(sub.Topics, topic) sub.Qoss = append(sub.Qoss, qos) if sub.Qos != 0 { sub.MessageID = c.getID(token) } if callback != nil { c.MessageRouter.addRoute(topic, callback) } c.WriteChan <- &PacketAndToken{p: sub, t: token} return token }
// to publish message , we need topic to publish to , //QoS for this message , Is this message to be Retained OR not and message body. func (c *Client) Publish(topic string, qos byte, retained bool, payload interface{}) Token { token := newToken(Message.Publish).(*PublishToken) if !c.IsConnected() { token.err = errors.New("Not Connected.") token.flowComplete() return token } pub := Message.NewControlPacket(Message.Publish).(*Message.PublishPacket) pub.Qos = qos pub.TopicName = topic pub.Retain = retained pub.Dup = false if pub.Qos != 0 { pub.MessageID = c.getID(token) } //check for the type of payload. switch payload.(type) { case string: pub.Payload = []byte(payload.(string)) case []byte: pub.Payload = payload.([]byte) default: token.err = errors.New("Unknown payload type") token.flowComplete() return token } c.WriteChan <- &PacketAndToken{p: pub, t: token} return token }
func keepalive(c *Client) { c.Workers.Add(1) // adding as worker. defer c.Workers.Done() // i am done client. you can do whatever you want. for { select { case <-c.stop: return default: last := uint(time.Since(c.HeartBeat.get()).Seconds()) if last > uint(c.KeepAlive.Seconds()) { token := newToken(Message.Pingreq) ping := Message.NewControlPacket(Message.Pingreq).(*Message.PingreqPacket) c.WriteChan <- &PacketAndToken{p: ping, t: token} token.Wait() } time.Sleep(1 * time.Second) } } }
func (c *Client) Disconnect() { token := newToken(Message.Disconnect).(*DisconnectToken) if !c.IsConnected() { log.Println("Already Disconnected.") } disPacket := Message.NewControlPacket(Message.Disconnect).(*Message.DisconnectPacket) c.WriteChan <- &PacketAndToken{p: disPacket, t: token} token.Wait() c.setConnected(false) close(c.stop) // this will signal all the go routines to finish what they are doing. c.disconnect() }
func (c *Client) UnSubscribe(topics ...string) Token { token := newToken(Message.Unsubscribe).(*UnsubscribeToken) if !c.IsConnected() { token.err = errors.New("Not Connected.") token.flowComplete() return token } unsub := Message.NewControlPacket(Message.Unsubscribe).(*Message.UnsubscribePacket) unsub.Topics = make([]string, len(topics)) if unsub.Qos != 0 { unsub.MessageID = c.getID(token) } copy(unsub.Topics, topics) c.WriteChan <- &PacketAndToken{p: unsub, t: token} for _, topic := range topics { c.MessageRouter.deleteRoute(topic) } return token }
func (c *Client) Connect() error { conn, err := net.DialTimeout(c.Broker.Type, string(c.Broker.Addr+":"+strconv.Itoa(c.Broker.Port)), c.Broker.TimeOut) if err != nil { log.Println("Error while TCP connection.") log.Println(err) return err } c.Con = conn //creating connect message : start. m := (Message.NewControlPacket(Message.Connect)).(*Message.ConnectPacket) m.ProtocolName = c.ProtocolName m.ProtocolVersion = byte(c.ProtocolVersion) m.ClientIdentifier = c.Id.(string) m.CleanSession = c.CleanSession m.WillFlag = c.WillFlag m.WillRetain = c.WillRetain if c.WillFlag { m.WillQos = c.WillQos m.WillTopic = c.WillTopic m.WillMessage = c.WillPayload } if c.UserName != "" { m.UsernameFlag = true m.Username = c.UserName //mustn't have password without user as well if c.Password != "" { m.PasswordFlag = true m.Password = []byte(c.Password) } } m.KeepaliveTimer = uint16(c.KeepAlive.Seconds()) // end. if err != m.Write(c.Con) { log.Println("Error while writing connect package.") log.Println(err) } //This is the response packet came from server. ca, err := Message.ReadPacket(c.Con) //read packet will validate packet and type. if err != nil { log.Println("Error in receiving.") log.Println(err.Error()) return err } if ca == nil { return errors.New("Received Nil Packet") } msg, ok := ca.(*Message.ConnackPacket) if !ok { return errors.New("Received wrong packet.") } if msg.ReturnCode != Message.Accepted { c.Con.Close() c.Con = nil return errors.New("Wrong Response code.") } log.Println("Connected...!") //connect is done. // INIT SETUP : START. c.WriteChan = make(chan *PacketAndToken, 100) c.ReadChan = make(chan *Message.ControlPacket, 100) c.InComingPubChan = make(chan *Message.PublishPacket, 100) c.stop = make(chan struct{}) c.setConnected(true) c.HeartBeat.update() // for keep alive. c.MessageRouter.matchAndDispatch(c.InComingPubChan, c.Order, c) //start contractors. go writeContractor(c) go readContractor(c) if c.KeepAlive > 0 { // if 0 then alive forever. else we need to do some work. go keepalive(c) } //INIT SETUP : DONE return nil }
func readContractor(c *Client) { c.Workers.Add(1) // tell client that i am working in your group. defer c.Workers.Done() // singnal client that i am done. var err error var msg Message.ControlPacket log.Println("Reader Ready..!") for { select { case <-c.stop: return default: if msg, err = Message.ReadPacket(c.Con); err != nil { log.Println("Error in read packet.") log.Fatalln(err.Error()) return } switch msg.(type) { case *Message.PingrespPacket: log.Println("Received PingResp") case *Message.SubackPacket: log.Println("Received SUBACKS") sa := msg.(*Message.SubackPacket) token := c.getToken(sa.MessageID).(*SubscribeToken) //these are the granted Qos From server. // for i, qos := range sa.GrantedQoss { // token.subResult[token.subs[i]] = qos // } token.flowComplete() // finish c.freeID(sa.MessageID) // free ID. case *Message.UnsubackPacket: ua := msg.(*Message.UnsubackPacket) token := c.getToken(ua.MessageID).(*UnsubscribeToken) token.flowComplete() c.freeID(ua.MessageID) case *Message.PublishPacket: // calls when server publish to this client. pp := msg.(*Message.PublishPacket) switch pp.Qos { case 2: c.InComingPubChan <- pp pr := Message.NewControlPacket(Message.Pubrec).(*Message.PubrecPacket) pr.MessageID = pp.MessageID c.WriteChan <- &PacketAndToken{p: pr, t: nil} case 1: c.InComingPubChan <- pp pa := Message.NewControlPacket(Message.Puback).(*Message.PubackPacket) pa.MessageID = pp.MessageID c.WriteChan <- &PacketAndToken{p: pa, t: nil} case 0: c.InComingPubChan <- pp } case *Message.PubackPacket: pa := msg.(*Message.PubackPacket) c.getToken(pa.MessageID).flowComplete() c.freeID(pa.MessageID) case *Message.PubrecPacket: prec := msg.(*Message.PubrecPacket) //log.Println("Received PUBREC") prel := Message.NewControlPacket(Message.Pubrel).(*Message.PubrelPacket) prel.MessageID = prec.MessageID select { case c.WriteChan <- &PacketAndToken{p: prel, t: nil}: case <-time.After(time.Second): } case *Message.PubrelPacket: pr := msg.(*Message.PubrelPacket) pc := Message.NewControlPacket(Message.Pubcomp).(*Message.PubcompPacket) pc.MessageID = pr.MessageID select { case c.WriteChan <- &PacketAndToken{p: pc, t: nil}: case <-time.After(time.Second): } case *Message.PubcompPacket: //log.Println("Received PUBCOMP") pc := msg.(*Message.PubcompPacket) c.getToken(pc.MessageID).flowComplete() c.freeID(pc.MessageID) } } } }