1 /*
  2 Copyright (c) 2009-2012 Roger Light <roger@atchoo.org>
  3 All rights reserved.
  4 
  5 Redistribution and use in source and binary forms, with or without
  6 modification, are permitted provided that the following conditions are met:
  7 
  8 1. Redistributions of source code must retain the above copyright notice,
  9    this list of conditions and the following disclaimer.
 10 2. Redistributions in binary form must reproduce the above copyright
 11    notice, this list of conditions and the following disclaimer in the
 12    documentation and/or other materials provided with the distribution.
 13 3. Neither the name of mosquitto nor the names of its
 14    contributors may be used to endorse or promote products derived from
 15    this software without specific prior written permission.
 16 
 17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 27 POSSIBILITY OF SUCH DAMAGE.
 28 */
 29 
 30 
 31 #include <errno.h>
 32 #include <fcntl.h>
 33 #include <stdio.h>
 34 #include <stdlib.h>
 35 #include <string.h>
 36 #ifndef WIN32
 37 #include <unistd.h>
 38 #else
 39 #include <process.h>
 40 #include <winsock2.h>
 41 #define snprintf sprintf_s
 42 #endif
 43 
 44 #include <mosquitto.h>
 45 
 46 #define MSGMODE_NONE 0
 47 #define MSGMODE_CMD 1
 48 #define MSGMODE_STDIN_LINE 2
 49 #define MSGMODE_STDIN_FILE 3
 50 #define MSGMODE_FILE 4
 51 #define MSGMODE_NULL 5
 52 
 53 #define STATUS_CONNECTING 0
 54 #define STATUS_CONNACK_RECVD 1
 55 
 56 static char *topic = NULL;
 57 static char *message = NULL;
 58 static long msglen = 0;
 59 static int qos = 0;
 60 static int retain = 0;
 61 static int mode = MSGMODE_NONE;        //消息类型,默认是MSGMODE_NONE
 62 static int status = STATUS_CONNECTING;
 63 static uint16_t mid_sent = 0;
 64 static bool connected = true;
 65 static char *username = NULL;
 66 static char *password = NULL;
 67 static bool disconnect_sent = false;
 68 static bool quiet = false;
 69 
 70 void my_connect_callback(void *obj, int result)    //obj:<mosquitto_new>中提供的用户数据;result:0-成功,1-不可接受的协议版本,2-标示符拒绝,3-broker不可达。。。
 71 {
 72     //mode是MSGMODE_STDIN_FILE和MSGMODE_NULL时发布消息
 73     struct mosquitto *mosq = obj;
 74     int rc = MOSQ_ERR_SUCCESS;
 75 
 76     if(!result){
 77         switch(mode){
 78             case MSGMODE_CMD:    //-m
 79             case MSGMODE_FILE:    //-f
 80             case MSGMODE_STDIN_FILE:    //-s
 81                 rc = mosquitto_publish(mosq, &mid_sent, topic, msglen, (uint8_t *)message, qos, retain);
 82                 break;
 83             case MSGMODE_NULL:    //-n
 84                 rc = mosquitto_publish(mosq, &mid_sent, topic, 0, NULL, qos, retain);
 85                 break;
 86             case MSGMODE_STDIN_LINE:    //-l
 87                 status = STATUS_CONNACK_RECVD;
 88                 break;
 89         }
 90         if(rc){
 91             if(!quiet){
 92                 switch(rc){
 93                     case MOSQ_ERR_INVAL:
 94                         fprintf(stderr, "Error: Invalid input. Does your topic contain '+' or '#'?\n");
 95                         break;
 96                     case MOSQ_ERR_NOMEM:
 97                         fprintf(stderr, "Error: Out of memory when trying to publish message.\n");
 98                         break;
 99                     case MOSQ_ERR_NO_CONN:
100                         fprintf(stderr, "Error: Client not connected when trying to publish.\n");
101                         break;
102                     case MOSQ_ERR_PROTOCOL:
103                         fprintf(stderr, "Error: Protocol error when communicating with broker.\n");
104                         break;
105                     case MOSQ_ERR_PAYLOAD_SIZE:
106                         fprintf(stderr, "Error: Message payload is too large.\n");
107                         break;
108                 }
109             }
110             mosquitto_disconnect(mosq);
111         }
112     }else{
113         switch(result){
114             case 1:
115                 if(!quiet) fprintf(stderr, "Connection Refused: unacceptable protocol version\n");
116                 break;
117             case 2:
118                 if(!quiet) fprintf(stderr, "Connection Refused: identifier rejected\n");
119                 break;
120             case 3:
121                 if(!quiet) fprintf(stderr, "Connection Refused: broker unavailable\n");
122                 break;
123             case 4:
124                 if(!quiet) fprintf(stderr, "Connection Refused: bad user name or password\n");
125                 break;
126             case 5:
127                 if(!quiet) fprintf(stderr, "Connection Refused: not authorised\n");
128                 break;
129             default:
130                 if(!quiet) fprintf(stderr, "Connection Refused: unknown reason\n");
131                 break;
132         }
133     }
134 }
135 
136 void my_disconnect_callback(void *obj)    
137 {
138     //连接状态conneted设为false
139     connected = false;
140 }
141 
142 void my_publish_callback(void *obj, uint16_t mid)        
143 {
144     //mode不是MSGMODE_STDIN_LINE(-l,从标准输入读取消息)且disconnect_sent是false(未断开连接)时,与broker断开连接
145     struct mosquitto *mosq = obj;
146 
147     if(mode != MSGMODE_STDIN_LINE && disconnect_sent == false){
148         mosquitto_disconnect(mosq);
149         disconnect_sent = true;
150     }
151 }
152 
153 int load_stdin(void)
154 {
155     //-s,从标准输入按文件读取,复制到message,直到遇到文件终止符EOF(ctrl+D)
156     long pos = 0, rlen;
157     char buf[1024];
158 
159     mode = MSGMODE_STDIN_FILE;
160 
161     while(!feof(stdin)){        
162         rlen = fread(buf, 1, 1024, stdin);
163         message = realloc(message, pos+rlen);
164         if(!message){
165             if(!quiet) fprintf(stderr, "Error: Out of memory.\n");
166             return 1;
167         }
168         memcpy(&(message[pos]), buf, rlen);
169         pos += rlen;
170     }
171     msglen = pos;
172 
173     if(!msglen){
174         if(!quiet) fprintf(stderr, "Error: Zero length input.\n");
175         return 1;
176     }
177 
178     return 0;
179 }
180 
181 int load_file(const char *filename)
182 {
183     long pos, rlen;
184     FILE *fptr = NULL;
185 
186     fptr = fopen(filename, "rb"); //只读打开一个二进制文件
187     if(!fptr){
188         if(!quiet) fprintf(stderr, "Error: Unable to open file \"%s\".\n", filename);
189         return 1;
190     }
191     mode = MSGMODE_FILE;
192     fseek(fptr, 0, SEEK_END);
193     msglen = ftell(fptr);    //获取文件当前读写位置偏移字节数,即文本长度
194     if(msglen > 268435455){    //不超过255MB
195         fclose(fptr);
196         if(!quiet) fprintf(stderr, "Error: File \"%s\" is too large (>268,435,455 bytes).\n", filename);
197         return 1;
198     }
199     if(msglen == 0){
200         fclose(fptr);
201         if(!quiet) fprintf(stderr, "Error: File \"%s\" is empty.\n", filename);
202         return 1;
203     }
204     fseek(fptr, 0, SEEK_SET);
205     message = malloc(msglen);
206     if(!message){
207         fclose(fptr);
208         if(!quiet) fprintf(stderr, "Error: Out of memory.\n");
209         return 1;
210     }
211     pos = 0;
212     //读取文件内容至message中
213     while(pos < msglen){
214         rlen = fread(&(message[pos]), sizeof(char), msglen-pos, fptr);
215         pos += rlen;
216     }
217     fclose(fptr);
218     return 0;
219 }
220 
221 void print_usage(void)
222 {
223     printf("mosquitto_pub is a simple mqtt client that will publish a message on a single topic and exit.\n\n");
224     printf("Usage: mosquitto_pub [-h host] [-p port] [-q qos] [-r] {-f file | -l | -n | -m message} -t topic\n");
225     printf("                     [-i id] [-I id_prefix]\n");
226     printf("                     [-d] [--quiet]\n");
227     printf("                     [-u username [-P password]]\n");
228     printf("                     [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\n\n");
229     printf(" -d : enable debug messages.\n");
230     printf(" -f : send the contents of a file as the message.\n");
231     printf(" -h : mqtt host to connect to. Defaults to localhost.\n");
232     printf(" -i : id to use for this client. Defaults to mosquitto_pub_ appended with the process id.\n");
233     printf(" -I : define the client id as id_prefix appended with the process id. Useful for when the\n");
234     printf("      broker is using the clientid_prefixes option.\n");
235     printf(" -l : read messages from stdin, sending a separate message for each line.\n");
236     printf(" -m : message payload to send.\n");
237     printf(" -n : send a null (zero length) message.\n");
238     printf(" -p : network port to connect to. Defaults to 1883.\n");
239     printf(" -q : quality of service level to use for all messages. Defaults to 0.\n");
240     printf(" -r : message should be retained.\n");
241     printf(" -s : read message from stdin, sending the entire input as a message.\n");
242     printf(" -t : mqtt topic to publish to.\n");
243     printf(" -u : provide a username (requires MQTT 3.1 broker)\n");
244     printf(" -P : provide a password (requires MQTT 3.1 broker)\n");
245     printf(" --quiet : don't print error messages.\n");
246     printf(" --will-payload : payload for the client Will, which is sent by the broker in case of\n");
247     printf("                  unexpected disconnection. If not given and will-topic is set, a zero\n");
248     printf("                  length message will be sent.\n");
249     printf(" --will-qos : QoS level for the client Will.\n");
250     printf(" --will-retain : if given, make the client Will retained.\n");
251     printf(" --will-topic : the topic on which to publish the client Will.\n");
252     printf("\nSee http://mosquitto.org/ for more information.\n\n");
253 }
254 
255 int main(int argc, char *argv[])
256 {
257     char *id = NULL;    //client ID
258     char *id_prefix = NULL;    //client ID 前缀
259     int i;
260     char *host = "localhost";    //server IP,默认是localhost
261     int port = 1883;    //server PORT,默认是1883
262     int keepalive = 60;        //
263     int opt;
264     char buf[1024];
265     bool debug = false;        //是否打印debug消息
266     struct mosquitto *mosq = NULL;
267     int rc;
268     int rc2;
269     char hostname[21];
270     char err[1024];
271 
272     uint8_t *will_payload = NULL;
273     long will_payloadlen = 0;
274     int will_qos = 0;
275     bool will_retain = false;
276     char *will_topic = NULL;
277 
278     //获取命令参数
279     for(i=1; i<argc; i++){
280         if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")){    //端口号
281             if(i==argc-1){
282                 fprintf(stderr, "Error: -p argument given but no port specified.\n\n");
283                 print_usage();
284                 return 1;
285             }else{
286                 port = atoi(argv[i+1]);
287                 if(port<1 || port>65535){
288                     fprintf(stderr, "Error: Invalid port given: %d\n", port);
289                     print_usage();
290                     return 1;
291                 }
292             }
293             i++;
294         }else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")){    //debug选项
295             debug = true;
296         }else if(!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")){        //-f,读取文件
297             if(mode != MSGMODE_NONE){
298                 fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
299                 print_usage();
300                 return 1;
301             }else if(i==argc-1){
302                 fprintf(stderr, "Error: -f argument given but no file specified.\n\n");
303                 print_usage();
304                 return 1;
305             }else{
306                 if(load_file(argv[i+1])) return 1;
307             }
308             i++;
309         }else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--host")){        //-h,server IP
310             if(i==argc-1){
311                 fprintf(stderr, "Error: -h argument given but no host specified.\n\n");
312                 print_usage();
313                 return 1;
314             }else{
315                 host = argv[i+1];
316             }
317             i++;
318         }else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--id")){        //-i, client id
319             if(id_prefix){
320                 fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
321                 print_usage();
322                 return 1;
323             }
324             if(i==argc-1){
325                 fprintf(stderr, "Error: -i argument given but no id specified.\n\n");
326                 print_usage();
327                 return 1;
328             }else{
329                 id = argv[i+1];
330             }
331             i++;
332         }else if(!strcmp(argv[i], "-I") || !strcmp(argv[i], "--id-prefix")){     //-I,client id前缀
333             if(id){
334                 fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
335                 print_usage();
336                 return 1;
337             }
338             if(i==argc-1){
339                 fprintf(stderr, "Error: -I argument given but no id prefix specified.\n\n");
340                 print_usage();
341                 return 1;
342             }else{
343                 id_prefix = argv[i+1];
344             }
345             i++;
346         }else if(!strcmp(argv[i], "-l") || !strcmp(argv[i], "--stdin-line")){        //-l,从标准输入按行读取发送消息,一行发送一条消息
347             if(mode != MSGMODE_NONE){
348                 fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
349                 print_usage();
350                 return 1;
351             }else{
352                 mode = MSGMODE_STDIN_LINE;
353 #ifndef WIN32
354                 opt = fcntl(fileno(stdin), F_GETFL, 0); //获取文件的flags
355                 if(opt == -1 || fcntl(fileno(stdin), F_SETFL, opt | O_NONBLOCK) == -1){ //设置文件flags非阻塞O_NONBLOCK
356                     fprintf(stderr, "Error: Unable to set stdin to non-blocking.\n");
357                     return 1;
358                 }
359 #endif
360             }
361         }else if(!strcmp(argv[i], "-m") || !strcmp(argv[i], "--message")){    //-m,从cmd发送消息,消息内容跟在-m之后
362             if(mode != MSGMODE_NONE){
363                 fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
364                 print_usage();
365                 return 1;
366             }else if(i==argc-1){
367                 fprintf(stderr, "Error: -m argument given but no message specified.\n\n");
368                 print_usage();
369                 return 1;
370             }else{
371                 message = argv[i+1];
372                 msglen = strlen(message);
373                 mode = MSGMODE_CMD;
374             }
375             i++;
376         }else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--null-message")){        //-n,发送空消息
377             if(mode != MSGMODE_NONE){
378                 fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
379                 print_usage();
380                 return 1;
381             }else{
382                 mode = MSGMODE_NULL;
383             }
384         }else if(!strcmp(argv[i], "-q") || !strcmp(argv[i], "--qos")){        //-q,服务质量,0,1,或2
385             if(i==argc-1){
386                 fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n");
387                 print_usage();
388                 return 1;
389             }else{
390                 qos = atoi(argv[i+1]);
391                 if(qos<0 || qos>2){
392                     fprintf(stderr, "Error: Invalid QoS given: %d\n", qos);
393                     print_usage();
394                     return 1;
395                 }
396             }
397             i++;
398         }else if(!strcmp(argv[i], "--quiet")){    //-quiet,什么都不打印出来
399             quiet = true;
400         }else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--retain")){    //-r,retained消息会在服务器上保留,但只保留带-r标志的最后一条消息
401             retain = 1;
402         }else if(!strcmp(argv[i], "-s") || !strcmp(argv[i], "--stdin-file")){        //-s,从标准输入按文件读取,到EOF时把所有输入内容用一条消息发送
403             if(mode != MSGMODE_NONE){
404                 fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
405                 print_usage();
406                 return 1;
407             }else{
408                 if(load_stdin()) return 1;
409             }
410         }else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--topic")){    //-t,消息主题,只能发布一个主题
411             if(i==argc-1){    
412                 fprintf(stderr, "Error: -t argument given but no topic specified.\n\n");
413                 print_usage();
414                 return 1;
415             }else{
416                 topic = argv[i+1];
417             }
418             i++;
419         }else if(!strcmp(argv[i], "-u") || !strcmp(argv[i], "--username")){        //-u,username
420             if(i==argc-1){
421                 fprintf(stderr, "Error: -u argument given but no username specified.\n\n");
422                 print_usage();
423                 return 1;
424             }else{
425                 username = argv[i+1];
426             }
427             i++;
428         }else if(!strcmp(argv[i], "-P") || !strcmp(argv[i], "--pw")){    //-P,password
429             if(i==argc-1){
430                 fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
431                 print_usage();
432                 return 1;
433             }else{
434                 password = argv[i+1];
435             }
436             i++;
437         }else if(!strcmp(argv[i], "--will-payload")){
438             if(i==argc-1){
439                 fprintf(stderr, "Error: --will-payload argument given but no will payload specified.\n\n");
440                 print_usage();
441                 return 1;
442             }else{
443                 will_payload = (uint8_t *)argv[i+1];
444                 will_payloadlen = strlen((char *)will_payload);
445             }
446             i++;
447         }else if(!strcmp(argv[i], "--will-qos")){
448             if(i==argc-1){
449                 fprintf(stderr, "Error: --will-qos argument given but no will QoS specified.\n\n");
450                 print_usage();
451                 return 1;
452             }else{
453                 will_qos = atoi(argv[i+1]);
454                 if(will_qos < 0 || will_qos > 2){
455                     fprintf(stderr, "Error: Invalid will QoS %d.\n\n", will_qos);
456                     return 1;
457                 }
458             }
459             i++;
460         }else if(!strcmp(argv[i], "--will-retain")){
461             will_retain = true;
462         }else if(!strcmp(argv[i], "--will-topic")){
463             if(i==argc-1){
464                 fprintf(stderr, "Error: --will-topic argument given but no will topic specified.\n\n");
465                 print_usage();
466                 return 1;
467             }else{
468                 will_topic = argv[i+1];
469             }
470             i++;
471         }else{
472             fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]);
473             print_usage();
474             return 1;
475         }
476     }
477     if(id_prefix){    //有设定client ID前缀
478         id = malloc(strlen(id_prefix)+10);
479         if(!id){
480             if(!quiet) fprintf(stderr, "Error: Out of memory.\n");
481             return 1;
482         }
483         snprintf(id, strlen(id_prefix)+10, "%s%d", id_prefix, getpid());
484     }else if(!id){        //没有前缀,也没有设定client ID
485         id = malloc(30);
486         if(!id){
487             if(!quiet) fprintf(stderr, "Error: Out of memory.\n");
488             return 1;
489         }
490         memset(hostname, 0, 21);
491         gethostname(hostname, 20);    //获得主机名
492         snprintf(id, 23, "mosq_pub_%d_%s", getpid(), hostname);
493     }
494 
495     if(!topic || mode == MSGMODE_NONE){
496         fprintf(stderr, "Error: Both topic and message must be supplied.\n");
497         print_usage();
498         return 1;
499     }
500 
501     if(will_payload && !will_topic){
502         fprintf(stderr, "Error: Will payload given, but no will topic given.\n");
503         print_usage();
504         return 1;
505     }
506     if(will_retain && !will_topic){
507         fprintf(stderr, "Error: Will retain given, but no will topic given.\n");
508         print_usage();
509         return 1;
510     }
511     if(password && !username){
512         if(!quiet) fprintf(stderr, "Warning: Not using password since username not set.\n");
513     }
514     mosquitto_lib_init();    //任何mosquitto functions之前都必须调用的函数,初始化操作
515     mosq = mosquitto_new(id, NULL);    //新建一个 mosquitto client实例
516     if(!mosq){        //未建成功client实例
517         if(!quiet) fprintf(stderr, "Error: Out of memory.\n");
518         return 1;
519     }
520     if(debug){        //需要记录debug信息,初始化
521         mosquitto_log_init(mosq, MOSQ_LOG_DEBUG | MOSQ_LOG_ERR | MOSQ_LOG_WARNING
522                 | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO, MOSQ_LOG_STDERR);
523     }
524     if(will_topic && mosquitto_will_set(mosq, true, will_topic, will_payloadlen, will_payload, will_qos, will_retain)){        //will信息配置,在connect之前调用
525         if(!quiet) fprintf(stderr, "Error: Problem setting will.\n");
526         return 1;
527     }
528     if(username && mosquitto_username_pw_set(mosq, username, password)){        //设置用户名,密码
529         if(!quiet) fprintf(stderr, "Error: Problem setting username and password.\n");
530         return 1;
531     }
532 
533     mosquitto_connect_callback_set(mosq, my_connect_callback);    //设置当broker给一个连接回复CONNACK时所调用的函数void callback(void *obj, int rc)
534     mosquitto_disconnect_callback_set(mosq, my_disconnect_callback);    //设置当broker收到DISCONNECT命令且与client断开后调用的函数
535     mosquitto_publish_callback_set(mosq, my_publish_callback);        //设置当一条被<mosquitto_publish>初始化的消息发送给broker后调用的函数
536 
537     rc = mosquitto_connect(mosq, host, port, keepalive, true);    //连接到一个MQTT broker
538     if(rc){
539         //连接不成功
540         if(!quiet){
541             if(rc == MOSQ_ERR_ERRNO){
542 #ifndef WIN32
543                 strerror_r(errno, err, 1024);
544 #else
545                 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL);
546 #endif
547                 fprintf(stderr, "Error: %s\n", err);
548             }else{
549                 fprintf(stderr, "Unable to connect (%d).\n", rc);
550             }
551         }
552         return rc;
553     }
554 
555     do{
556         if(mode == MSGMODE_STDIN_LINE && status == STATUS_CONNACK_RECVD){
557             if(fgets(buf, 1024, stdin)){
558                 buf[strlen(buf)-1] = '\0';
559                 rc2 = mosquitto_publish(mosq, &mid_sent, topic, strlen(buf), (uint8_t *)buf, qos, retain);
560                 if(rc2){
561                     if(!quiet) fprintf(stderr, "Error: Publish returned %d, disconnecting.\n", rc2);
562                     mosquitto_disconnect(mosq);
563                 }
564             }else if(feof(stdin) && disconnect_sent == false){
565                 mosquitto_disconnect(mosq);
566                 disconnect_sent = true;
567             }
568         }
569         rc = mosquitto_loop(mosq, -1);
570     }while(rc == MOSQ_ERR_SUCCESS && connected);
571 
572     if(message && mode == MSGMODE_FILE){
573         free(message);
574     }
575     mosquitto_destroy(mosq);        //释放mosquitto实例的内存空间
576     mosquitto_lib_cleanup();        //释放library所使用的资源
577     return rc;
578 }
posted on 2012-07-30 10:05  aaronwxb  阅读(3339)  评论(0编辑  收藏  举报