mosquitto0.15clientsub_client.c源码分析

  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 #include <errno.h>
 31 #include <stdio.h>
 32 #include <stdlib.h>
 33 #include <string.h>
 34 #ifndef WIN32
 35 #include <unistd.h>
 36 #else
 37 #include <process.h>
 38 #include <winsock2.h>
 39 #define snprintf sprintf_s
 40 #endif
 41 
 42 #include <mosquitto.h>
 43 
 44 static char **topics = NULL;
 45 static int topic_count = 0;
 46 static int topic_qos = 0;
 47 static char *username = NULL;
 48 static char *password = NULL;
 49 int verbose = 0;
 50 bool quiet = false;
 51 
 52 void my_message_callback(void *obj, const struct mosquitto_message *message)
 53 {
 54     //收到消息时,打印
 55     if(verbose){
 56         if(message->payloadlen){
 57             printf("%s %s\n", message->topic, message->payload);
 58         }else{
 59             printf("%s (null)\n", message->topic);
 60         }
 61         fflush(stdout);
 62     }else{
 63         if(message->payloadlen){
 64             printf("%s\n", message->payload);
 65             fflush(stdout);
 66         }
 67     }
 68 }
 69 
 70 void my_connect_callback(void *obj, int result)
 71 {
 72     //连接建立后订阅主题
 73     struct mosquitto *mosq = obj;
 74 
 75     int i;
 76     if(!result){
 77         for(i=0; i<topic_count; i++){
 78             mosquitto_subscribe(mosq, NULL, topics[i], topic_qos);
 79         }
 80     }else{
 81         switch(result){
 82             case 1:
 83                 if(!quiet) fprintf(stderr, "Connection Refused: unacceptable protocol version\n");
 84                 break;
 85             case 2:
 86                 if(!quiet) fprintf(stderr, "Connection Refused: identifier rejected\n");
 87                 break;
 88             case 3:
 89                 if(!quiet) fprintf(stderr, "Connection Refused: broker unavailable\n");
 90                 break;
 91             case 4:
 92                 if(!quiet) fprintf(stderr, "Connection Refused: bad user name or password\n");
 93                 break;
 94             case 5:
 95                 if(!quiet) fprintf(stderr, "Connection Refused: not authorised\n");
 96                 break;
 97             default:
 98                 if(!quiet) fprintf(stderr, "Connection Refused: unknown reason\n");
 99                 break;
100         }
101     }
102 }
103 
104 void my_subscribe_callback(void *obj, uint16_t mid, int qos_count, const uint8_t *granted_qos)
105 {
106     //打印订阅的相关信息
107     int i;
108 
109     if(!quiet) printf("Subscribed (mid: %d): %d", mid, granted_qos[0]);
110     for(i=1; i<qos_count; i++){
111         if(!quiet) printf(", %d", granted_qos[i]);
112     }
113     if(!quiet) printf("\n");
114 }
115 
116 void print_usage(void)
117 {
118     //打印mosquitto_sub帮助信息
119     printf("mosquitto_sub is a simple mqtt client that will subscribe to a single topic and print all messages it receives.\n\n");
120     printf("Usage: mosquitto_sub [-c] [-h host] [-k keepalive] [-p port] [-q qos] [-v] -t topic ...\n");
121     printf("                     [-i id] [-I id_prefix]\n");
122     printf("                     [-d] [--quiet]\n");
123     printf("                     [-u username [-P password]]\n");
124     printf("                     [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\n\n");
125     printf(" -c : disable 'clean session' (store subscription and pending messages when client disconnects).\n");
126     printf(" -d : enable debug messages.\n");
127     printf(" -h : mqtt host to connect to. Defaults to localhost.\n");
128     printf(" -i : id to use for this client. Defaults to mosquitto_sub_ appended with the process id.\n");
129     printf(" -I : define the client id as id_prefix appended with the process id. Useful for when the\n");
130     printf("      broker is using the clientid_prefixes option.\n");
131     printf(" -k : keep alive in seconds for this client. Defaults to 60.\n");
132     printf(" -p : network port to connect to. Defaults to 1883.\n");
133     printf(" -q : quality of service level to use for the subscription. Defaults to 0.\n");
134     printf(" -t : mqtt topic to subscribe to. May be repeated multiple times.\n");
135     printf(" -u : provide a username (requires MQTT 3.1 broker)\n");
136     printf(" -v : print published messages verbosely.\n");
137     printf(" -P : provide a password (requires MQTT 3.1 broker)\n");
138     printf(" --quiet : don't print error messages.\n");
139     printf(" --will-payload : payload for the client Will, which is sent by the broker in case of\n");
140     printf("                  unexpected disconnection. If not given and will-topic is set, a zero\n");
141     printf("                  length message will be sent.\n");
142     printf(" --will-qos : QoS level for the client Will.\n");
143     printf(" --will-retain : if given, make the client Will retained.\n");
144     printf(" --will-topic : the topic on which to publish the client Will.\n");
145     printf("\nSee http://mosquitto.org/ for more information.\n\n");
146 }
147 
148 int main(int argc, char *argv[])
149 {
150     char *id = NULL;
151     char *id_prefix = NULL;
152     int i;
153     char *host = "localhost";
154     int port = 1883;
155     int keepalive = 60;
156     bool clean_session = true;
157     bool debug = false;
158     struct mosquitto *mosq = NULL;
159     int rc;
160     char hostname[21];
161     char err[1024];
162     
163     uint8_t *will_payload = NULL;
164     long will_payloadlen = 0;
165     int will_qos = 0;
166     bool will_retain = false;
167     char *will_topic = NULL;
168 
169     for(i=1; i<argc; i++){
170         if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")){    //-p
171             if(i==argc-1){
172                 fprintf(stderr, "Error: -p argument given but no port specified.\n\n");
173                 print_usage();
174                 return 1;
175             }else{
176                 port = atoi(argv[i+1]);
177                 if(port<1 || port>65535){
178                     fprintf(stderr, "Error: Invalid port given: %d\n", port);
179                     print_usage();
180                     return 1;
181                 }
182             }
183             i++;
184         }else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "--disable-clean-session")){    //-c,在client断开连接之后,仍保持订阅
185             clean_session = false;
186         }else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")){    //-d
187             debug = true;
188         }else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--host")){        //-h
189             if(i==argc-1){
190                 fprintf(stderr, "Error: -h argument given but no host specified.\n\n");
191                 print_usage();
192                 return 1;
193             }else{
194                 host = argv[i+1];
195             }
196             i++;
197         }else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--id")){        //-i
198             if(id_prefix){
199                 fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
200                 print_usage();
201                 return 1;
202             }
203             if(i==argc-1){
204                 fprintf(stderr, "Error: -i argument given but no id specified.\n\n");
205                 print_usage();
206                 return 1;
207             }else{
208                 id = argv[i+1];
209             }
210             i++;
211         }else if(!strcmp(argv[i], "-I") || !strcmp(argv[i], "--id-prefix")){        //-I
212             if(id){
213                 fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
214                 print_usage();
215                 return 1;
216             }
217             if(i==argc-1){
218                 fprintf(stderr, "Error: -I argument given but no id prefix specified.\n\n");
219                 print_usage();
220                 return 1;
221             }else{
222                 id_prefix = argv[i+1];
223             }
224             i++;
225         }else if(!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keepalive")){    //-k,keepalive,定义客户端多久向server发送PING以保持连接
226             if(i==argc-1){
227                 fprintf(stderr, "Error: -k argument given but no keepalive specified.\n\n");
228                 print_usage();
229                 return 1;
230             }else{
231                 keepalive = atoi(argv[i+1]);
232                 if(keepalive>65535){
233                     fprintf(stderr, "Error: Invalid keepalive given: %d\n", keepalive);
234                     print_usage();
235                     return 1;
236                 }
237             }
238             i++;
239         }else if(!strcmp(argv[i], "-q") || !strcmp(argv[i], "--qos")){        //-q
240             if(i==argc-1){
241                 fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n");
242                 print_usage();
243                 return 1;
244             }else{
245                 topic_qos = atoi(argv[i+1]);
246                 if(topic_qos<0 || topic_qos>2){
247                     fprintf(stderr, "Error: Invalid QoS given: %d\n", topic_qos);
248                     print_usage();
249                     return 1;
250                 }
251             }
252             i++;
253         }else if(!strcmp(argv[i], "--quiet")){        //-quiet
254             quiet = true;
255         }else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--topic")){        //-t,可订阅多个主题
256             if(i==argc-1){
257                 fprintf(stderr, "Error: -t argument given but no topic specified.\n\n");
258                 print_usage();
259                 return 1;
260             }else{
261                 topic_count++;
262                 topics = realloc(topics, topic_count*sizeof(char *));
263                 topics[topic_count-1] = argv[i+1];
264             }
265             i++;
266         }else if(!strcmp(argv[i], "-u") || !strcmp(argv[i], "--username")){        //-u
267             if(i==argc-1){
268                 fprintf(stderr, "Error: -u argument given but no username specified.\n\n");
269                 print_usage();
270                 return 1;
271             }else{
272                 username = argv[i+1];
273             }
274             i++;
275         }else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")){        //-v,详细打印消息,格式:topic payload
276             verbose = 1;
277         }else if(!strcmp(argv[i], "-P") || !strcmp(argv[i], "--pw")){        //-P
278             if(i==argc-1){
279                 fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
280                 print_usage();
281                 return 1;
282             }else{
283                 password = argv[i+1];
284             }
285             i++;
286         }else if(!strcmp(argv[i], "--will-payload")){        //--will-payload
287             if(i==argc-1){
288                 fprintf(stderr, "Error: --will-payload argument given but no will payload specified.\n\n");
289                 print_usage();
290                 return 1;
291             }else{
292                 will_payload = (uint8_t *)argv[i+1];
293                 will_payloadlen = strlen((char *)will_payload);
294             }
295             i++;
296         }else if(!strcmp(argv[i], "--will-qos")){   //--will-qos
297             if(i==argc-1){
298                 fprintf(stderr, "Error: --will-qos argument given but no will QoS specified.\n\n");
299                 print_usage();
300                 return 1;
301             }else{
302                 will_qos = atoi(argv[i+1]);
303                 if(will_qos < 0 || will_qos > 2){
304                     fprintf(stderr, "Error: Invalid will QoS %d.\n\n", will_qos);
305                     return 1;
306                 }
307             }
308             i++;
309         }else if(!strcmp(argv[i], "--will-retain")){    //--will-retain
310             will_retain = true;
311         }else if(!strcmp(argv[i], "--will-topic")){        //--will-topic
312             if(i==argc-1){
313                 fprintf(stderr, "Error: --will-topic argument given but no will topic specified.\n\n");
314                 print_usage();
315                 return 1;
316             }else{
317                 will_topic = argv[i+1];
318             }
319             i++;
320         }else{
321             fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]);
322             print_usage();
323             return 1;
324         }
325     }
326     if(clean_session == false && (id_prefix || !id)){        //设置了-c,就必须使用-i,不能用-I
327         if(!quiet) fprintf(stderr, "Error: You must provide a client id if you are using the -c option.\n");
328         return 1;
329     }
330     if(id_prefix){
331         id = malloc(strlen(id_prefix)+10);
332         if(!id){
333             if(!quiet) fprintf(stderr, "Error: Out of memory.\n");
334             return 1;
335         }
336         snprintf(id, strlen(id_prefix)+10, "%s%d", id_prefix, getpid());
337     }else if(!id){
338         id = malloc(30);
339         if(!id){
340             if(!quiet) fprintf(stderr, "Error: Out of memory.\n");
341             return 1;
342         }
343         memset(hostname, 0, 21);
344         gethostname(hostname, 20);
345         snprintf(id, 23, "mosq_sub_%d_%s", getpid(), hostname);
346     }
347 
348     if(topic_count == 0){
349         fprintf(stderr, "Error: You must specify a topic to subscribe to.\n");
350         print_usage();
351         return 1;
352     }
353     if(will_payload && !will_topic){
354         fprintf(stderr, "Error: Will payload given, but no will topic given.\n");
355         print_usage();
356         return 1;
357     }
358     if(will_retain && !will_topic){
359         fprintf(stderr, "Error: Will retain given, but no will topic given.\n");
360         print_usage();
361         return 1;
362     }
363     if(password && !username){
364         if(!quiet) fprintf(stderr, "Warning: Not using password since username not set.\n");
365     }
366     mosquitto_lib_init();        //任何mosquitto functions之前都必须调用的函数,初始化操作
367     mosq = mosquitto_new(id, NULL);        //新建一个 mosquitto client实例
368     if(!mosq){
369         if(!quiet) fprintf(stderr, "Error: Out of memory.\n");
370         return 1;
371     }
372     if(debug){
373         mosquitto_log_init(mosq, MOSQ_LOG_DEBUG | MOSQ_LOG_ERR | MOSQ_LOG_WARNING
374                 | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO, MOSQ_LOG_STDERR);
375     }
376     if(will_topic && mosquitto_will_set(mosq, true, will_topic, will_payloadlen, will_payload, will_qos, will_retain)){
377         if(!quiet) fprintf(stderr, "Error: Problem setting will.\n");
378         return 1;
379     }
380     if(username && mosquitto_username_pw_set(mosq, username, password)){
381         if(!quiet) fprintf(stderr, "Error: Problem setting username and password.\n");
382         return 1;
383     }
384     mosquitto_connect_callback_set(mosq, my_connect_callback);        //设置client与broker建立连接后所调用的函数
385     mosquitto_message_callback_set(mosq, my_message_callback);        //设置client从broker收到消息后所调用的函数
386     if(debug){
387         mosquitto_subscribe_callback_set(mosq, my_subscribe_callback);    //设置当broker相应一个订阅请求时所调用的函数
388     }
389 
390     rc = mosquitto_connect(mosq, host, port, keepalive, clean_session);        //建立连接
391     if(rc){
392         if(!quiet){
393             if(rc == MOSQ_ERR_ERRNO){
394 #ifndef WIN32
395                 strerror_r(errno, err, 1024);
396 #else
397                 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL);
398 #endif
399                 fprintf(stderr, "Error: %s\n", err);
400             }else{
401                 fprintf(stderr, "Unable to connect (%d).\n", rc);
402             }
403         }
404         return rc;
405     }
406 
407     do{
408         rc = mosquitto_loop(mosq, -1);    //主网络循环监控client,以保持client和broker之间的通信正常
409     }while(rc == MOSQ_ERR_SUCCESS);
410 
411     mosquitto_destroy(mosq);    //释放mosquitto实例的内存空间
412     mosquitto_lib_cleanup();    //释放library所使用的资源
413 
414     return rc;
415 }
原文地址:https://www.cnblogs.com/aaronwxb/p/2614739.html