platformNetwork.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #define rlogLevel (rlogLvlDebug) // 日志打印等级
  2. #include "platformNetwork.h"
  3. #include "RyanMqttLog.h"
  4. /**
  5. * @brief 初始化网络接口层
  6. *
  7. * @param userData
  8. * @param platformNetwork
  9. * @return RyanMqttError_e
  10. */
  11. RyanMqttError_e platformNetworkInit(void *userData, platformNetwork_t *platformNetwork)
  12. {
  13. platformNetwork->socket = -1;
  14. return RyanMqttSuccessError;
  15. }
  16. /**
  17. * @brief 销毁网络接口层
  18. *
  19. * @param userData
  20. * @param platformNetwork
  21. * @return RyanMqttError_e
  22. */
  23. RyanMqttError_e platformNetworkDestroy(void *userData, platformNetwork_t *platformNetwork)
  24. {
  25. platformNetwork->socket = -1;
  26. return RyanMqttSuccessError;
  27. }
  28. /**
  29. * @brief Establishes a TCP connection to an MQTT server at the specified host and port.
  30. *
  31. * If the host is an IP address, connects directly without DNS resolution. If the host is a domain name, attempts DNS resolution using a thread-safe method, falling back to a non-thread-safe method if necessary. Allocates and frees temporary memory for DNS resolution as needed. On failure, cleans up resources and returns an appropriate error code.
  32. *
  33. * @param host The server hostname or IP address to connect to.
  34. * @param port The server port number.
  35. * @return RyanMqttError_e Returns RyanMqttSuccessError on success, or an error code on failure.
  36. */
  37. RyanMqttError_e platformNetworkConnect(void *userData, platformNetwork_t *platformNetwork, const char *host, uint16_t port)
  38. {
  39. RyanMqttError_e result = RyanMqttSuccessError;
  40. char *buf = NULL;
  41. struct sockaddr_in server_addr = {
  42. .sin_family = AF_INET,
  43. .sin_port = htons(port), // 指定端口号
  44. };
  45. // 传递的是ip地址,不用进行dns解析,某些情况下调用dns解析反而会错误
  46. if (INADDR_NONE != inet_addr(host))
  47. {
  48. rlog_d("host: %s, 不用dns解析", host);
  49. server_addr.sin_addr.s_addr = inet_addr(host);
  50. }
  51. // 解析域名信息
  52. else
  53. {
  54. rlog_d("host: %s, 需要dns解析", host);
  55. int h_errnop;
  56. struct hostent *phost;
  57. struct hostent hostinfo = {0};
  58. buf = (char *)platformMemoryMalloc(384);
  59. if (NULL == buf)
  60. {
  61. result = RyanMqttNoRescourceError;
  62. goto __exit;
  63. }
  64. if (0 != gethostbyname_r(host, &hostinfo, buf, sizeof(buf), &phost, &h_errnop))
  65. {
  66. rlog_w("平台可能不支持 gethostbyname_r 函数, 再次尝试使用 gethostbyname 获取域名信息");
  67. // 非线程安全版本,请根据实际情况选择使用
  68. // NOLINTNEXTLINE(concurrency-mt-unsafe)
  69. struct hostent *phostinfo = gethostbyname(host);
  70. if (NULL == phostinfo)
  71. {
  72. result = RyanMqttNoRescourceError;
  73. goto __exit;
  74. }
  75. hostinfo = *phostinfo;
  76. }
  77. server_addr.sin_addr = *((struct in_addr *)hostinfo.h_addr_list[0]);
  78. }
  79. platformNetwork->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
  80. if (platformNetwork->socket < 0)
  81. {
  82. result = RyanSocketFailedError;
  83. goto __exit;
  84. }
  85. // 绑定套接字到主机地址和端口号
  86. if (connect(platformNetwork->socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0)
  87. {
  88. platformNetworkClose(userData, platformNetwork);
  89. result = RyanMqttSocketConnectFailError;
  90. goto __exit;
  91. }
  92. __exit:
  93. if (NULL != buf)
  94. platformMemoryFree(buf);
  95. if (RyanMqttSuccessError != result)
  96. rlog_e("socket连接失败: %d", result);
  97. return result;
  98. }
  99. /**
  100. * @brief Performs a non-blocking receive operation on the network socket with a specified timeout.
  101. *
  102. * Attempts to read up to recvLen bytes into recvBuf from the socket associated with platformNetwork. The function sets a receive timeout and returns immediately if no data is available or if the operation would block.
  103. *
  104. * @param recvBuf Buffer to store received data.
  105. * @param recvLen Maximum number of bytes to receive.
  106. * @param timeout Timeout in milliseconds for the receive operation.
  107. * @return int32_t Number of bytes received on success; 0 if no data is available or operation is interrupted; -1 if the socket is closed or a fatal error occurs.
  108. */
  109. int32_t platformNetworkRecvAsync(void *userData, platformNetwork_t *platformNetwork, char *recvBuf, size_t recvLen, int32_t timeout)
  110. {
  111. int32_t recvResult = 0;
  112. struct timeval tv = {0};
  113. if (-1 == platformNetwork->socket)
  114. {
  115. rlog_e("对端关闭socket连接");
  116. return -1;
  117. }
  118. tv.tv_sec = timeout / 1000;
  119. tv.tv_usec = timeout % 1000 * 1000;
  120. setsockopt(platformNetwork->socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); // 设置错做模式为非阻塞
  121. recvResult = recv(platformNetwork->socket, recvBuf, recvLen, 0);
  122. if (0 == recvResult)
  123. {
  124. rlog_e("对端关闭socket连接");
  125. return -1;
  126. }
  127. else if (recvResult < 0) // 小于零,表示错误,个别错误不代表socket错误
  128. {
  129. int32_t rt_errno = errno; // 似乎5.0.0以上版本需要使用 rt_get_errno
  130. // 下列表示没问题,但需要退出接收
  131. if (EAGAIN == rt_errno || // 套接字已标记为非阻塞,而接收操作被阻塞或者接收超时
  132. EWOULDBLOCK == rt_errno || // 发送时套接字发送缓冲区已满,或接收时套接字接收缓冲区为空
  133. EINTR == rt_errno || // 操作被信号中断
  134. ETIME == rt_errno) // 计时器过期
  135. {
  136. return 0;
  137. }
  138. // NOLINTNEXTLINE(concurrency-mt-unsafe)
  139. rlog_e("recvResult: %d, errno: %d str: %s", recvResult, rt_errno, strerror(rt_errno));
  140. return -1;
  141. }
  142. return recvResult;
  143. }
  144. /**
  145. * @brief Sends data asynchronously over a socket with a specified timeout.
  146. *
  147. * Attempts to send data from the buffer over the network socket in non-blocking mode, using a send timeout. Returns the number of bytes sent on success, 0 if the operation would block or times out, and -1 if the socket is closed or a fatal error occurs.
  148. *
  149. * @param sendBuf Pointer to the data buffer to send.
  150. * @param sendLen Number of bytes to send from the buffer.
  151. * @param timeout Timeout for the send operation in milliseconds.
  152. * @return int32_t Number of bytes sent on success, 0 if no data sent due to timeout or non-fatal error, -1 on socket closure or fatal error.
  153. */
  154. int32_t platformNetworkSendAsync(void *userData, platformNetwork_t *platformNetwork, char *sendBuf, size_t sendLen, int32_t timeout)
  155. {
  156. int32_t sendResult = 0;
  157. struct timeval tv = {0};
  158. if (-1 == platformNetwork->socket)
  159. {
  160. rlog_e("对端关闭socket连接");
  161. return -1;
  162. }
  163. tv.tv_sec = timeout / 1000;
  164. tv.tv_usec = timeout % 1000 * 1000;
  165. setsockopt(platformNetwork->socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval)); // 设置错做模式为非阻塞
  166. sendResult = send(platformNetwork->socket, sendBuf, sendLen, 0);
  167. if (0 == sendResult)
  168. {
  169. rlog_e("对端关闭socket连接");
  170. return -1;
  171. }
  172. else if (sendResult < 0) // 小于零,表示错误,个别错误不代表socket错误
  173. {
  174. int32_t rt_errno = errno; // 似乎5.0.0以上版本需要使用 rt_get_errno
  175. // 下列表示没问题,但需要退出发送
  176. if (EAGAIN == rt_errno || // 套接字已标记为非阻塞,而接收操作被阻塞或者接收超时
  177. EWOULDBLOCK == rt_errno || // 发送时套接字发送缓冲区已满,或接收时套接字接收缓冲区为空
  178. EINTR == rt_errno || // 操作被信号中断
  179. ETIME == rt_errno) // 计时器过期
  180. {
  181. return 0;
  182. }
  183. // NOLINTNEXTLINE(concurrency-mt-unsafe)
  184. rlog_e("sendResult: %d, errno: %d str: %s", sendResult, rt_errno, strerror(rt_errno));
  185. return -1;
  186. }
  187. return sendResult;
  188. }
  189. /**
  190. * @brief Closes the network socket and resets the socket descriptor.
  191. *
  192. * If the socket is open, closes it and sets the descriptor to -1.
  193. *
  194. * @return RyanMqttSuccessError on successful closure.
  195. */
  196. RyanMqttError_e platformNetworkClose(void *userData, platformNetwork_t *platformNetwork)
  197. {
  198. if (platformNetwork->socket >= 0)
  199. {
  200. close(platformNetwork->socket);
  201. rlog_w("platformNetworkClose socket close %d", platformNetwork->socket);
  202. platformNetwork->socket = -1;
  203. }
  204. return RyanMqttSuccessError;
  205. }