当前位置:   article > 正文

移植MQTT-C库(附源码)

mqtt-c

Software (mqtt.org)mqtt客户端的c库里面有一个叫MQTT-C的库,就2个实现文件,算比较简单的了,实现了基本的mqtt客户端功能,移植一下试试。

我的移植代码放在我的资源里面:https://download.csdn.net/download/oushaojun2/87281533?spm=1001.2014.3001.5501

进到这个项目的github仓库地址:https://github.com/LiamBindle/MQTT-C

src里面有两个文件mqtt.c和mqtt_pal.c,第一个是mqtt的实现,第二个是移植需要的文件。移植文件里面主要包括了常见平台的socket接收和发送函数的封装,假如移植到自己的平台可能需要修改这个文件里面的代码,目前的移植是想要在visual studio里面移植,里面已经有了移植接口了。

移植到visual studio里面的步骤如下:

1 将MQTT-C的代码增加到visual studio的一个空白工程里面。需要的文件如下,记得删掉创建工程是自带的文件和修改文件包含路径:

2 修改mqtt_pal.h,128行增加一行:#pragma comment(lib,"ws2_32.lib"),为了在win32平台下链接到ws2_32.lib库,否则编译

3 修改posix_sockets.h内容,虽然这个头文件是按照socket标准接口来调用的,但是win32的socket接口跟linux的接口有些不一样,例如close在win32里面是没有的,gai_strerror在win32里面没效果,win32需要调用WSAStartup函数。修改如下:

  1. #if !defined(__POSIX_SOCKET_TEMPLATE_H__)
  2. #define __POSIX_SOCKET_TEMPLATE_H__
  3. #include <stdio.h>
  4. #include <sys/types.h>
  5. #if !defined(WIN32)
  6. #include <sys/socket.h>
  7. #include <netdb.h>
  8. #else
  9. #include <ws2tcpip.h>
  10. #define close closesocket
  11. #endif
  12. #if defined(__VMS)
  13. #include <ioctl.h>
  14. #endif
  15. #include <fcntl.h>
  16. /*
  17. A template for opening a non-blocking POSIX socket.
  18. */
  19. int open_nb_socket(const char* addr, const char* port);
  20. int open_nb_socket(const char* addr, const char* port) {
  21. struct addrinfo hints = {0};
  22. hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */
  23. hints.ai_socktype = SOCK_STREAM; /* Must be TCP */
  24. int sockfd = -1;
  25. int rv;
  26. struct addrinfo *p, *servinfo;
  27. #if defined(WIN32)
  28. {
  29. WSADATA wsaData;
  30. int iResult;
  31. iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
  32. if (iResult != NO_ERROR) {
  33. printf("WSAStartup failed: %d\n", iResult);
  34. return 1;
  35. }
  36. }
  37. #endif
  38. /* get address information */
  39. rv = getaddrinfo(addr, port, &hints, &servinfo);
  40. if(rv != 0) {
  41. #if defined(__UNIX__)
  42. fprintf(stderr, "Failed to open socket (getaddrinfo): %s\n", gai_strerror(rv));
  43. #else
  44. fprintf(stderr, "Failed to open socket (getaddrinfo): %s\n", gai_strerrorA(rv));
  45. #endif
  46. return -1;
  47. }
  48. /* open the first possible socket */
  49. for(p = servinfo; p != NULL; p = p->ai_next) {
  50. sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
  51. if (sockfd == -1) continue;
  52. /* connect to server */
  53. rv = connect(sockfd, p->ai_addr, p->ai_addrlen);
  54. if(rv == -1) {
  55. close(sockfd);
  56. sockfd = -1;
  57. continue;
  58. }
  59. break;
  60. }
  61. /* free servinfo */
  62. freeaddrinfo(servinfo);
  63. /* make non-blocking */
  64. #if !defined(WIN32)
  65. if (sockfd != -1) fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
  66. #else
  67. if (sockfd != INVALID_SOCKET) {
  68. int iMode = 1;
  69. ioctlsocket(sockfd, FIONBIO, &iMode);
  70. }
  71. #endif
  72. #if defined(__VMS)
  73. /*
  74. OpenVMS only partially implements fcntl. It works on file descriptors
  75. but silently fails on socket descriptors. So we need to fall back on
  76. to the older ioctl system to set non-blocking IO
  77. */
  78. int on = 1;
  79. if (sockfd != -1) ioctl(sockfd, FIONBIO, &on);
  80. #endif
  81. /* return the new socket fd */
  82. return sockfd;
  83. }
  84. #endif

 4 修改simple_publisher.c文件,这个文件的接口都是posix接口,在win32环境下有些要修改,例如建立线程函数等。修改如下:

  1. /**
  2. * @file
  3. * A simple program to that publishes the current time whenever ENTER is pressed.
  4. */
  5. #if defined(__unix__)
  6. #include <unistd.h>
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <pthread.h>
  10. #else
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #pragma warning(disable : 4996)
  14. #endif
  15. #include <mqtt.h>
  16. #include "posix_sockets.h"
  17. /**
  18. * @brief The function that would be called whenever a PUBLISH is received.
  19. *
  20. * @note This function is not used in this example.
  21. */
  22. void publish_callback(void** unused, struct mqtt_response_publish *published);
  23. /**
  24. * @brief The client's refresher. This function triggers back-end routines to
  25. * handle ingress/egress traffic to the broker.
  26. *
  27. * @note All this function needs to do is call \ref __mqtt_recv and
  28. * \ref __mqtt_send every so often. I've picked 100 ms meaning that
  29. * client ingress/egress traffic will be handled every 100 ms.
  30. */
  31. #if defined(__UNIX__)
  32. void* client_refresher(void* client);
  33. #else
  34. DWORD WINAPI client_refresher(LPVOID client);
  35. #endif
  36. /**
  37. * @brief Safelty closes the \p sockfd and cancels the \p client_daemon before \c exit.
  38. */
  39. #if defined(__unix__)
  40. void exit_example(int status, int sockfd, pthread_t *client_daemon);
  41. #else
  42. void exit_example(int status, int sockfd, HANDLE client_daemon);
  43. #endif
  44. /**
  45. * A simple program to that publishes the current time whenever ENTER is pressed.
  46. */
  47. int main(int argc, const char *argv[])
  48. {
  49. const char* addr;
  50. const char* port;
  51. const char* topic;
  52. /* get address (argv[1] if present) */
  53. if (argc > 1) {
  54. addr = argv[1];
  55. } else {
  56. addr = "test.mosquitto.org";
  57. }
  58. /* get port number (argv[2] if present) */
  59. if (argc > 2) {
  60. port = argv[2];
  61. } else {
  62. port = "1883";
  63. }
  64. /* get the topic name to publish */
  65. if (argc > 3) {
  66. topic = argv[3];
  67. } else {
  68. topic = "datetime";
  69. }
  70. /* open the non-blocking TCP socket (connecting to the broker) */
  71. int sockfd = open_nb_socket(addr, port);
  72. if (sockfd == -1) {
  73. perror("Failed to open socket: ");
  74. exit_example(EXIT_FAILURE, sockfd, NULL);
  75. }
  76. /* setup a client */
  77. struct mqtt_client client;
  78. uint8_t sendbuf[2048]; /* sendbuf should be large enough to hold multiple whole mqtt messages */
  79. uint8_t recvbuf[1024]; /* recvbuf should be large enough any whole mqtt message expected to be received */
  80. mqtt_init(&client, sockfd, sendbuf, sizeof(sendbuf), recvbuf, sizeof(recvbuf), publish_callback);
  81. /* Create an anonymous session */
  82. const char* client_id = NULL;
  83. /* Ensure we have a clean session */
  84. uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION;
  85. /* Send connection request to the broker. */
  86. mqtt_connect(&client, client_id, NULL, NULL, 0, NULL, NULL, connect_flags, 400);
  87. /* check that we don't have any errors */
  88. if (client.error != MQTT_OK) {
  89. fprintf(stderr, "error: %s\n", mqtt_error_str(client.error));
  90. exit_example(EXIT_FAILURE, sockfd, NULL);
  91. }
  92. /* start a thread to refresh the client (handle egress and ingree client traffic) */
  93. #if defined(__UNIX__)
  94. pthread_t client_daemon;
  95. if(pthread_create(&client_daemon, NULL, client_refresher, &client)) {
  96. fprintf(stderr, "Failed to start client daemon.\n");
  97. exit_example(EXIT_FAILURE, sockfd, NULL);
  98. }
  99. #else
  100. HANDLE client_daemon;
  101. DWORD dwThreadIdArray;
  102. client_daemon = CreateThread(
  103. NULL, // default security attributes
  104. 0, // use default stack size
  105. client_refresher, // thread function name
  106. &client, // argument to thread function
  107. 0, // use default creation flags
  108. &dwThreadIdArray); // returns the thread identifier
  109. #endif
  110. /* start publishing the time */
  111. printf("%s is ready to begin publishing the time.\n", argv[0]);
  112. printf("Press ENTER to publish the current time.\n");
  113. printf("Press CTRL-D (or any other key) to exit.\n\n");
  114. while(fgetc(stdin) == '\n') {
  115. /* get the current time */
  116. time_t timer;
  117. time(&timer);
  118. struct tm* tm_info = localtime(&timer);
  119. char timebuf[26];
  120. strftime(timebuf, 26, "%Y-%m-%d %H:%M:%S", tm_info);
  121. /* print a message */
  122. char application_message[256];
  123. snprintf(application_message, sizeof(application_message), "The time is %s", timebuf);
  124. printf("%s published : \"%s\"", argv[0], application_message);
  125. /* publish the time */
  126. mqtt_publish(&client, topic, application_message, strlen(application_message) + 1, MQTT_PUBLISH_QOS_0);
  127. /* check for errors */
  128. if (client.error != MQTT_OK) {
  129. fprintf(stderr, "error: %s\n", mqtt_error_str(client.error));
  130. exit_example(EXIT_FAILURE, sockfd, &client_daemon);
  131. }
  132. }
  133. /* disconnect */
  134. printf("\n%s disconnecting from %s\n", argv[0], addr);
  135. #if defined(__UNIX__)
  136. sleep(1);
  137. #else
  138. Sleep(1000);
  139. #endif
  140. /* exit */
  141. exit_example(EXIT_SUCCESS, sockfd, &client_daemon);
  142. }
  143. #if defined(__UNIX__)
  144. void exit_example(int status, int sockfd, pthread_t *client_daemon)
  145. {
  146. if (sockfd != -1) close(sockfd);
  147. if (client_daemon != NULL) pthread_cancel(*client_daemon);
  148. exit(status);
  149. }
  150. #else
  151. void exit_example(int status, int sockfd, HANDLE client_daemon)
  152. {
  153. if (sockfd != -1) close(sockfd);
  154. if (client_daemon != NULL) CloseHandle(client_daemon);
  155. exit(status);
  156. }
  157. #endif
  158. void publish_callback(void** unused, struct mqtt_response_publish *published)
  159. {
  160. /* not used in this example */
  161. }
  162. #if defined(__UNIX__)
  163. void* client_refresher(void* client)
  164. #else
  165. DWORD WINAPI client_refresher(LPVOID client)
  166. #endif
  167. {
  168. while(1)
  169. {
  170. mqtt_sync((struct mqtt_client*) client);
  171. #if defined(__UNIX__)
  172. usleep(100000U);
  173. #else
  174. Sleep(100);
  175. #endif
  176. }
  177. return NULL;
  178. }

点击visual studio编译后运行,这个程序会去连接test.mosquitto.org的1883接口,当用户在命令行点击换行后将发布消息到datetime主题,消息内容为当前时间。

在另外一个mqtt客户端订阅这个主题后会收到发布到datetime主题的消息:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/176336
推荐阅读
相关标签
  

闽ICP备14008679号