non_blocking_socket_example.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /* BSD non-blocking socket example
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. #include <string.h>
  8. #include "freertos/FreeRTOS.h"
  9. #include "freertos/task.h"
  10. #include "sys/socket.h"
  11. #include "netdb.h"
  12. #include "errno.h"
  13. #include "esp_system.h"
  14. #include "esp_event.h"
  15. #include "esp_log.h"
  16. #include "nvs_flash.h"
  17. #include "protocol_examples_common.h"
  18. /**
  19. * @brief Indicates that the file descriptor represents an invalid (uninitialized or closed) socket
  20. *
  21. * Used in the TCP server structure `sock[]` which holds list of active clients we serve.
  22. */
  23. #define INVALID_SOCK (-1)
  24. /**
  25. * @brief Time in ms to yield to all tasks when a non-blocking socket would block
  26. *
  27. * Non-blocking socket operations are typically executed in a separate task validating
  28. * the socket status. Whenever the socket returns `EAGAIN` (idle status, i.e. would block)
  29. * we have to yield to all tasks to prevent lower priority tasks from starving.
  30. */
  31. #define YIELD_TO_ALL_MS 50
  32. /**
  33. * @brief Utility to log socket errors
  34. *
  35. * @param[in] tag Logging tag
  36. * @param[in] sock Socket number
  37. * @param[in] err Socket errno
  38. * @param[in] message Message to print
  39. */
  40. static void log_socket_error(const char *tag, const int sock, const int err, const char *message)
  41. {
  42. ESP_LOGE(tag, "[sock=%d]: %s\n"
  43. "error=%d: %s", sock, message, err, strerror(err));
  44. }
  45. /**
  46. * @brief Tries to receive data from specified sockets in a non-blocking way,
  47. * i.e. returns immediately if no data.
  48. *
  49. * @param[in] tag Logging tag
  50. * @param[in] sock Socket for reception
  51. * @param[out] data Data pointer to write the received data
  52. * @param[in] max_len Maximum size of the allocated space for receiving data
  53. * @return
  54. * >0 : Size of received data
  55. * =0 : No data available
  56. * -1 : Error occurred during socket read operation
  57. * -2 : Socket is not connected, to distinguish between an actual socket error and active disconnection
  58. */
  59. static int try_receive(const char *tag, const int sock, char * data, size_t max_len)
  60. {
  61. int len = recv(sock, data, max_len, 0);
  62. if (len < 0) {
  63. if (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK) {
  64. return 0; // Not an error
  65. }
  66. if (errno == ENOTCONN) {
  67. ESP_LOGW(tag, "[sock=%d]: Connection closed", sock);
  68. return -2; // Socket has been disconnected
  69. }
  70. log_socket_error(tag, sock, errno, "Error occurred during receiving");
  71. return -1;
  72. }
  73. return len;
  74. }
  75. /**
  76. * @brief Sends the specified data to the socket. This function blocks until all bytes got sent.
  77. *
  78. * @param[in] tag Logging tag
  79. * @param[in] sock Socket to write data
  80. * @param[in] data Data to be written
  81. * @param[in] len Length of the data
  82. * @return
  83. * >0 : Size the written data
  84. * -1 : Error occurred during socket write operation
  85. */
  86. static int socket_send(const char *tag, const int sock, const char * data, const size_t len)
  87. {
  88. int to_write = len;
  89. while (to_write > 0) {
  90. int written = send(sock, data + (len - to_write), to_write, 0);
  91. if (written < 0 && errno != EINPROGRESS && errno != EAGAIN && errno != EWOULDBLOCK) {
  92. log_socket_error(tag, sock, errno, "Error occurred during sending");
  93. return -1;
  94. }
  95. to_write -= written;
  96. }
  97. return len;
  98. }
  99. #ifdef CONFIG_EXAMPLE_TCP_CLIENT
  100. static void tcp_client_task(void *pvParameters)
  101. {
  102. static const char *TAG = "nonblocking-socket-client";
  103. static const char *payload = "GET / HTTP/1.1\r\n\r\n";
  104. static char rx_buffer[128];
  105. struct addrinfo hints = { .ai_socktype = SOCK_STREAM };
  106. struct addrinfo *address_info;
  107. int sock = INVALID_SOCK;
  108. int res = getaddrinfo(CONFIG_EXAMPLE_TCP_CLIENT_CONNECT_ADDRESS, CONFIG_EXAMPLE_TCP_CLIENT_CONNECT_PORT, &hints, &address_info);
  109. if (res != 0 || address_info == NULL) {
  110. ESP_LOGE(TAG, "couldn't get hostname for `%s` "
  111. "getaddrinfo() returns %d, addrinfo=%p", CONFIG_EXAMPLE_TCP_CLIENT_CONNECT_ADDRESS, res, address_info);
  112. goto error;
  113. }
  114. // Creating client's socket
  115. sock = socket(address_info->ai_family, address_info->ai_socktype, address_info->ai_protocol);
  116. if (sock < 0) {
  117. log_socket_error(TAG, sock, errno, "Unable to create socket");
  118. goto error;
  119. }
  120. ESP_LOGI(TAG, "Socket created, connecting to %s:%s", CONFIG_EXAMPLE_TCP_CLIENT_CONNECT_ADDRESS, CONFIG_EXAMPLE_TCP_CLIENT_CONNECT_PORT);
  121. // Marking the socket as non-blocking
  122. int flags = fcntl(sock, F_GETFL);
  123. if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
  124. log_socket_error(TAG, sock, errno, "Unable to set socket non blocking");
  125. }
  126. if (connect(sock, address_info->ai_addr, address_info->ai_addrlen) != 0) {
  127. if (errno == EINPROGRESS) {
  128. ESP_LOGD(TAG, "connection in progress");
  129. fd_set fdset;
  130. FD_ZERO(&fdset);
  131. FD_SET(sock, &fdset);
  132. // Connection in progress -> have to wait until the connecting socket is marked as writable, i.e. connection completes
  133. res = select(sock+1, NULL, &fdset, NULL, NULL);
  134. if (res < 0) {
  135. log_socket_error(TAG, sock, errno, "Error during connection: select for socket to be writable");
  136. goto error;
  137. } else if (res == 0) {
  138. log_socket_error(TAG, sock, errno, "Connection timeout: select for socket to be writable");
  139. goto error;
  140. } else {
  141. int sockerr;
  142. socklen_t len = (socklen_t)sizeof(int);
  143. if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)(&sockerr), &len) < 0) {
  144. log_socket_error(TAG, sock, errno, "Error when getting socket error using getsockopt()");
  145. goto error;
  146. }
  147. if (sockerr) {
  148. log_socket_error(TAG, sock, sockerr, "Connection error");
  149. goto error;
  150. }
  151. }
  152. } else {
  153. log_socket_error(TAG, sock, errno, "Socket is unable to connect");
  154. goto error;
  155. }
  156. }
  157. ESP_LOGI(TAG, "Client sends data to the server...");
  158. int len = socket_send(TAG, sock, payload, strlen(payload));
  159. if (len < 0) {
  160. ESP_LOGE(TAG, "Error occurred during socket_send");
  161. goto error;
  162. }
  163. ESP_LOGI(TAG, "Written: %.*s", len, payload);
  164. // Keep receiving until we have a reply
  165. do {
  166. len = try_receive(TAG, sock, rx_buffer, sizeof(rx_buffer));
  167. if (len < 0) {
  168. ESP_LOGE(TAG, "Error occurred during try_receive");
  169. goto error;
  170. }
  171. vTaskDelay(pdMS_TO_TICKS(YIELD_TO_ALL_MS));
  172. } while (len == 0);
  173. ESP_LOGI(TAG, "Received: %.*s", len, rx_buffer);
  174. error:
  175. if (sock != INVALID_SOCK) {
  176. close(sock);
  177. }
  178. free(address_info);
  179. vTaskDelete(NULL);
  180. }
  181. #endif // CONFIG_EXAMPLE_TCP_CLIENT
  182. #ifdef CONFIG_EXAMPLE_TCP_SERVER
  183. /**
  184. * @brief Returns the string representation of client's address (accepted on this server)
  185. */
  186. static inline char* get_clients_address(struct sockaddr_storage *source_addr)
  187. {
  188. static char address_str[128];
  189. char *res = NULL;
  190. // Convert ip address to string
  191. if (source_addr->ss_family == PF_INET) {
  192. res = inet_ntoa_r(((struct sockaddr_in *)source_addr)->sin_addr, address_str, sizeof(address_str) - 1);
  193. }
  194. #ifdef CONFIG_LWIP_IPV6
  195. else if (source_addr->ss_family == PF_INET6) {
  196. res = inet6_ntoa_r(((struct sockaddr_in6 *)source_addr)->sin6_addr, address_str, sizeof(address_str) - 1);
  197. }
  198. #endif
  199. if (!res) {
  200. address_str[0] = '\0'; // Returns empty string if conversion didn't succeed
  201. }
  202. return address_str;
  203. }
  204. static void tcp_server_task(void *pvParameters)
  205. {
  206. static char rx_buffer[128];
  207. static const char *TAG = "nonblocking-socket-server";
  208. SemaphoreHandle_t *server_ready = pvParameters;
  209. struct addrinfo hints = { .ai_socktype = SOCK_STREAM };
  210. struct addrinfo *address_info;
  211. int listen_sock = INVALID_SOCK;
  212. const size_t max_socks = CONFIG_LWIP_MAX_SOCKETS - 1;
  213. static int sock[CONFIG_LWIP_MAX_SOCKETS - 1];
  214. // Prepare a list of file descriptors to hold client's sockets, mark all of them as invalid, i.e. available
  215. for (int i=0; i<max_socks; ++i) {
  216. sock[i] = INVALID_SOCK;
  217. }
  218. // Translating the hostname or a string representation of an IP to address_info
  219. int res = getaddrinfo(CONFIG_EXAMPLE_TCP_SERVER_BIND_ADDRESS, CONFIG_EXAMPLE_TCP_SERVER_BIND_PORT, &hints, &address_info);
  220. if (res != 0 || address_info == NULL) {
  221. ESP_LOGE(TAG, "couldn't get hostname for `%s` "
  222. "getaddrinfo() returns %d, addrinfo=%p", CONFIG_EXAMPLE_TCP_SERVER_BIND_ADDRESS, res, address_info);
  223. goto error;
  224. }
  225. // Creating a listener socket
  226. listen_sock = socket(address_info->ai_family, address_info->ai_socktype, address_info->ai_protocol);
  227. if (listen_sock < 0) {
  228. log_socket_error(TAG, listen_sock, errno, "Unable to create socket");
  229. goto error;
  230. }
  231. ESP_LOGI(TAG, "Listener socket created");
  232. // Marking the socket as non-blocking
  233. int flags = fcntl(listen_sock, F_GETFL);
  234. if (fcntl(listen_sock, F_SETFL, flags | O_NONBLOCK) == -1) {
  235. log_socket_error(TAG, listen_sock, errno, "Unable to set socket non blocking");
  236. goto error;
  237. }
  238. ESP_LOGI(TAG, "Socket marked as non blocking");
  239. // Binding socket to the given address
  240. int err = bind(listen_sock, address_info->ai_addr, address_info->ai_addrlen);
  241. if (err != 0) {
  242. log_socket_error(TAG, listen_sock, errno, "Socket unable to bind");
  243. goto error;
  244. }
  245. ESP_LOGI(TAG, "Socket bound on %s:%s", CONFIG_EXAMPLE_TCP_SERVER_BIND_ADDRESS, CONFIG_EXAMPLE_TCP_SERVER_BIND_PORT);
  246. // Set queue (backlog) of pending connections to one (can be more)
  247. err = listen(listen_sock, 1);
  248. if (err != 0) {
  249. log_socket_error(TAG, listen_sock, errno, "Error occurred during listen");
  250. goto error;
  251. }
  252. ESP_LOGI(TAG, "Socket listening");
  253. xSemaphoreGive(*server_ready);
  254. // Main loop for accepting new connections and serving all connected clients
  255. while (1) {
  256. struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
  257. socklen_t addr_len = sizeof(source_addr);
  258. // Find a free socket
  259. int new_sock_index = 0;
  260. for (new_sock_index=0; new_sock_index<max_socks; ++new_sock_index) {
  261. if (sock[new_sock_index] == INVALID_SOCK) {
  262. break;
  263. }
  264. }
  265. // We accept a new connection only if we have a free socket
  266. if (new_sock_index < max_socks) {
  267. // Try to accept a new connections
  268. sock[new_sock_index] = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
  269. if (sock[new_sock_index] < 0) {
  270. if (errno == EWOULDBLOCK) { // The listener socket did not accepts any connection
  271. // continue to serve open connections and try to accept again upon the next iteration
  272. ESP_LOGV(TAG, "No pending connections...");
  273. } else {
  274. log_socket_error(TAG, listen_sock, errno, "Error when accepting connection");
  275. goto error;
  276. }
  277. } else {
  278. // We have a new client connected -> print it's address
  279. ESP_LOGI(TAG, "[sock=%d]: Connection accepted from IP:%s", sock[new_sock_index], get_clients_address(&source_addr));
  280. // ...and set the client's socket non-blocking
  281. flags = fcntl(sock[new_sock_index], F_GETFL);
  282. if (fcntl(sock[new_sock_index], F_SETFL, flags | O_NONBLOCK) == -1) {
  283. log_socket_error(TAG, sock[new_sock_index], errno, "Unable to set socket non blocking");
  284. goto error;
  285. }
  286. ESP_LOGI(TAG, "[sock=%d]: Socket marked as non blocking", sock[new_sock_index]);
  287. }
  288. }
  289. // We serve all the connected clients in this loop
  290. for (int i=0; i<max_socks; ++i) {
  291. if (sock[i] != INVALID_SOCK) {
  292. // This is an open socket -> try to serve it
  293. int len = try_receive(TAG, sock[i], rx_buffer, sizeof(rx_buffer));
  294. if (len < 0) {
  295. // Error occurred within this client's socket -> close and mark invalid
  296. ESP_LOGI(TAG, "[sock=%d]: try_receive() returned %d -> closing the socket", sock[i], len);
  297. close(sock[i]);
  298. sock[i] = INVALID_SOCK;
  299. } else if (len > 0) {
  300. // Received some data -> echo back
  301. ESP_LOGI(TAG, "[sock=%d]: Received %.*s", sock[i], len, rx_buffer);
  302. len = socket_send(TAG, sock[i], rx_buffer, len);
  303. if (len < 0) {
  304. // Error occurred on write to this socket -> close it and mark invalid
  305. ESP_LOGI(TAG, "[sock=%d]: socket_send() returned %d -> closing the socket", sock[i], len);
  306. close(sock[i]);
  307. sock[i] = INVALID_SOCK;
  308. } else {
  309. // Successfully echoed to this socket
  310. ESP_LOGI(TAG, "[sock=%d]: Written %.*s", sock[i], len, rx_buffer);
  311. }
  312. }
  313. } // one client's socket
  314. } // for all sockets
  315. // Yield to other tasks
  316. vTaskDelay(pdMS_TO_TICKS(YIELD_TO_ALL_MS));
  317. }
  318. error:
  319. if (listen_sock != INVALID_SOCK) {
  320. close(listen_sock);
  321. }
  322. for (int i=0; i<max_socks; ++i) {
  323. if (sock[i] != INVALID_SOCK) {
  324. close(sock[i]);
  325. }
  326. }
  327. free(address_info);
  328. vTaskDelete(NULL);
  329. }
  330. #endif // CONFIG_EXAMPLE_TCP_SERVER
  331. void app_main(void)
  332. {
  333. ESP_ERROR_CHECK(nvs_flash_init());
  334. ESP_ERROR_CHECK(esp_netif_init());
  335. ESP_ERROR_CHECK(esp_event_loop_create_default());
  336. /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
  337. * Read "Establishing Wi-Fi or Ethernet Connection" section in
  338. * examples/protocols/README.md for more information about this function.
  339. */
  340. ESP_ERROR_CHECK(example_connect());
  341. #ifdef CONFIG_EXAMPLE_TCP_SERVER
  342. SemaphoreHandle_t server_ready = xSemaphoreCreateBinary();
  343. assert(server_ready);
  344. xTaskCreate(tcp_server_task, "tcp_server", 4096, &server_ready, 5, NULL);
  345. xSemaphoreTake(server_ready, portMAX_DELAY);
  346. vSemaphoreDelete(server_ready);
  347. #endif // CONFIG_EXAMPLE_TCP_SERVER
  348. #ifdef CONFIG_EXAMPLE_TCP_CLIENT
  349. xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);
  350. #endif // CONFIG_EXAMPLE_TCP_CLIENT
  351. }