├── MQTTClient.js └── README /MQTTClient.js: -------------------------------------------------------------------------------- 1 | /** 2 | *This is a simple MQTT cient on node.js 3 | *Author: Fan Yilun @CEIT @14 FEB 2011 4 | */ 5 | var sys = require('sys'); 6 | var net = require('net'); 7 | var EventEmitter = require('events').EventEmitter; 8 | 9 | var MQTTCONNECT = 0x10; 10 | var MQTTPUBLISH = 0x30; 11 | var MQTTSUBSCRIBE = 0x80; //8<<4; 12 | 13 | var KEEPALIVE = 15000; 14 | 15 | //var client = Client(1883, '127.0.0.1', 'mirror'); 16 | 17 | function MQTTClient(port, host, clientID) { 18 | //EventEmitter.call(this); 19 | 20 | this.connected = false; 21 | this.sessionSend = false; 22 | this.sessionOpened = false; 23 | this.id = clientID; 24 | 25 | //this.conn = net.createConnection(port || 1883, host || '127.0.0.1'); 26 | this.conn = net.createConnection(port, host); 27 | this.conn.setEncoding('utf8'); 28 | 29 | var self = this; 30 | 31 | //Set timer 32 | self.timeout = setTimeout(function() { 33 | self.timeUp(); 34 | }, 25000); 35 | 36 | self.conn.addListener('data', function (data) { 37 | if(!self.sessionOpened){ 38 | //sys.puts("len:"+data.length+' @3:'+data.charCodeAt(3)+'\n'); 39 | if(data.length==4 && data.charCodeAt(3)==0){ 40 | self.sessionOpened = true; 41 | sys.puts("Session opend\n"); 42 | self.emit("sessionOpened"); 43 | 44 | //reset timer 45 | clearTimeout(self.timeout); 46 | self.timeout = setTimeout(function() { 47 | self.timeUp(); 48 | }, 3000); 49 | }else{ 50 | clearTimeout(self.timeout); 51 | self.emit("openSessionFailed"); 52 | self.conn.end(); 53 | //this.conn.destroy(); 54 | return; 55 | } 56 | } else { 57 | //sys.puts('len:' + data.length+' Data received:'+data+'\n'); 58 | if(data.length > 2){ 59 | var buf = new Buffer(data); 60 | self.onData(buf); 61 | } 62 | } 63 | }); 64 | 65 | self.conn.addListener('connect', function () { 66 | //sys.puts('connected\n'); 67 | self.connected = true; 68 | //Once connected, send open stream to broker 69 | self.openSession(self.id); 70 | }); 71 | 72 | self.conn.addListener('end', function() { 73 | self.connected = false; 74 | self.sessionSend = false; 75 | self.sessionOpened = false; 76 | sys.puts('Connection closed by broker'); 77 | }); 78 | } 79 | 80 | sys.inherits(MQTTClient, EventEmitter); 81 | exports.MQTTClient = MQTTClient; 82 | 83 | MQTTClient.prototype.timeUp = function(){ 84 | if(this.connected && this.sessionOpened){ 85 | //sys.puts('25s keep alive'); 86 | this.live(); 87 | } else if (!this.connected ){ 88 | sys.puts('MQTT connect to server time out'); 89 | this.emit("connectTimeOut"); 90 | } else { 91 | sys.puts('Unknow state'); 92 | } 93 | }; 94 | 95 | MQTTClient.prototype.openSession = function (id) { 96 | 97 | var i = 0; 98 | var buffer = new Buffer(16+id.length); 99 | 100 | buffer[i++] = MQTTCONNECT; 101 | buffer[i++] = 14+id.length; 102 | buffer[i++] = 0x00; 103 | buffer[i++] = 0x06; 104 | buffer[i++] = 0x4d; 105 | buffer[i++] = 0x51; 106 | buffer[i++] = 0x49; 107 | buffer[i++] = 0x73; 108 | buffer[i++] = 0x64; 109 | buffer[i++] = 0x70; 110 | buffer[i++] = 0x03; 111 | 112 | buffer[i++] = 0x02; 113 | 114 | //Keep alive for 30s 115 | buffer[i++] = 0x00; 116 | buffer[i++] = KEEPALIVE/500; //Keepalive for 30s 117 | 118 | buffer[i++] = 0x00; 119 | buffer[i++] = id.length; 120 | 121 | for (var n = 0; n < id.length; n++) { //Insert client id 122 | buffer[i++] = id.charCodeAt(n); //Convert string to utf8 123 | } 124 | 125 | //sys.puts(buffer.toString('utf8',0, 16)+' '+buffer.length); 126 | this.conn.write(buffer, encoding="ascii"); 127 | 128 | this.sessionSend = true; 129 | sys.puts('Connected as :'+id+'\n'); 130 | 131 | //publish('node', 'here is nodejs'); 132 | //this.subscribe('mirror'); 133 | }; 134 | 135 | 136 | /*subscribes to topics */ 137 | MQTTClient.prototype.subscribe = function (sub_topic) { 138 | if(this.connected){ 139 | var i = 0; 140 | var buffer = new Buffer(7+sub_topic.length);; 141 | 142 | //fixed header 143 | buffer[i++] = MQTTSUBSCRIBE; 144 | buffer[i++] = 5 + sub_topic.length; 145 | 146 | //varibale header 147 | buffer[i++] = 0x00; 148 | buffer[i++] = 0x0a; //message id 149 | 150 | //payload 151 | buffer[i++] = 0x00; 152 | buffer[i++] = sub_topic.length; 153 | 154 | for (var n = 0; n < sub_topic.length; n++) { 155 | buffer[i++] = sub_topic.charCodeAt(n); 156 | } 157 | buffer[i++] = 0x00; 158 | 159 | //sys.puts(7+sub_topic.length); 160 | sys.puts('Subcribe to:'+sub_topic); 161 | //sys.puts("Subscribe send len:"+buffer.length+'\n'); 162 | 163 | this.conn.write(buffer, encoding="ascii"); 164 | 165 | //reset timer 166 | var cc = this; 167 | clearTimeout(this.timeout); 168 | this.timeout = setTimeout(function() { 169 | cc.timeUp(); 170 | }, 25000); 171 | } 172 | }; 173 | 174 | /*publishes to topics*/ 175 | MQTTClient.prototype.publish = function (pub_topic, payload) { 176 | 177 | if(this.connected){ 178 | var i = 0, n = 0; 179 | var var_header = new Buffer(2+pub_topic.length); 180 | 181 | //Variable header 182 | //Assume payload length no longer than 128 183 | var_header[i++] = 0; 184 | var_header[i++] = pub_topic.length; 185 | for (n = 0; n < pub_topic.length; n++) { 186 | var_header[i++] = pub_topic.charCodeAt(n); 187 | } 188 | //QoS 1&2 189 | //var_header[i++] = 0; 190 | //var_header[i++] = 0x03; 191 | 192 | i = 0; 193 | var buffer = new Buffer(2+var_header.length+payload.length); 194 | 195 | //Fix header 196 | buffer[i++] = MQTTPUBLISH; 197 | buffer[i++] = payload.length + var_header.length; 198 | for (n = 0; n < var_header.length; n++) { 199 | buffer[i++] = var_header[n]; 200 | } 201 | for (n = 0; n < payload.length; n++) { //Insert payloads 202 | buffer[i++] = payload.charCodeAt(n); 203 | } 204 | 205 | sys.puts("||Publish|| "+pub_topic+' : '+payload); 206 | 207 | this.conn.write(buffer, encoding="ascii"); 208 | 209 | //reset timer 210 | var cc = this; 211 | clearTimeout(this.timeout); 212 | this.timeout = setTimeout(function() { 213 | cc.timeUp(); 214 | }, 25000); 215 | } 216 | }; 217 | 218 | MQTTClient.prototype.onData = function(data){ 219 | var type = data[0]>>4; 220 | //sys.puts('\ntype:'+type); 221 | //sys.puts('1:'+data[1]); 222 | //sys.puts('2:'+data[2]); 223 | //sys.puts('3:'+data[3]); 224 | if (type == 3) { // PUBLISH 225 | var tl = data[3]+data[2]; //<<4 226 | var topic = new Buffer(tl); 227 | for(var i = 0; i < tl; i++){ 228 | topic[i] = data[i+4]; 229 | } 230 | if(tl+4 <= data.length){ 231 | var payload = data.slice(tl+4, data.length); 232 | sys.puts("Receive on Topic:"+topic); 233 | sys.puts("Payload:"+payload+'\n'); 234 | this.emit("mqttData", topic, payload); 235 | } 236 | } else if (type == 12) { // PINGREG -- Ask for alive 237 | //Send [208, 0] to server 238 | sys.puts('Send 208 0'); 239 | var packet208 = new Buffer(2); 240 | packet208[0] = 0xd0; 241 | packet208[1] = 0x00; 242 | 243 | this.conn.write(packet208, encoding="utf8"); 244 | 245 | //reset timer 246 | var cc = this; 247 | clearTimeout(this.timeout); 248 | this.timeout = setTimeout(function() { 249 | cc.timeUp(); 250 | }, 25000); 251 | } 252 | } 253 | 254 | MQTTClient.prototype.live = function () { 255 | //Send [192, 0] to server 256 | var packet192 = new Buffer(2); 257 | packet192[0] = 0xc0; 258 | packet192[1] = 0x00; 259 | this.conn.write(packet192, encoding="utf8"); 260 | 261 | //reset timer 262 | var cc = this; 263 | clearTimeout(this.timeout); 264 | this.timeout = setTimeout(function() { 265 | cc.timeUp(); 266 | //self.publish('node', 'hello wtf'); 267 | }, 25000); //send keepavie every 25s 268 | }; 269 | 270 | MQTTClient.prototype.disconnect = function () { 271 | //Send [224,0] to server 272 | var packet224 = new Buffer(2); 273 | packet224[0] = 0xe0; 274 | packet224[1] = 0x00; 275 | this.conn.write(packet224, encoding="utf8"); 276 | }; -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is a simple MQTT client on node.js 2 | 3 | The lib is still under developing. 4 | 5 | Author: FAN Yilun @ CEIT 6 | 7 | 8 | API 9 | 10 | Event: ‘sessionOpened’ 11 | Fired when a session with broker is opened sucesffully. 12 | 13 | Event: ‘mqttData’ 14 | Fired when data is available for client, topic and payload have been extracted from data. 15 | client.addListener(‘mqttData’, function(topic, payload){ 16 | 17 | }); 18 | 19 | Event: ‘openSessionFailed’ 20 | Fired when a session can not be established. 21 | 22 | Event: ‘connectTimeOut’ 23 | Fired when cant establish a connection with server. 24 | 25 | MQTTClient(port, host, clientID); 26 | Construct a instance of mqtt client. 27 | @port: port number, like 1883. 28 | @host: server ip address. 29 | @clientID: client name for server. 30 | 31 | subscribe(sub_topic); 32 | Subscribe to a topic. 33 | @sub_topic: topic to be subscribed. 34 | 35 | publish(pub_topic, msg); 36 | Publish messages to a topic. 37 | @pub_topic: publish topics. 38 | @msg: payload data, can be anything, string, bytes. 39 | 40 | disconnect(); 41 | Disconnect with server. 42 | --------------------------------------------------------------------------------