1 /* BSD Socket API Example
2
3 This example code is in the Public Domain (or CC0 licensed, at your option.)
4
5 Unless required by applicable law or agreed to in writing, this
6 software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
7 CONDITIONS OF ANY KIND, either express or implied.
8 */
9 #include <string.h>
10 #include <sys/param.h>
11 #include "freertos/FreeRTOS.h"
12 #include "freertos/task.h"
13 #include "esp_system.h"
14 #include "esp_wifi.h"
15 #include "esp_event.h"
16 #include "esp_log.h"
17 #include "nvs_flash.h"
18 #include "esp_netif.h"
19 #include "protocol_examples_common.h"
20
21 #include "lwip/err.h"
22 #include "lwip/sockets.h"
23 #include "lwip/sys.h"
24 #include <lwip/netdb.h>
25
26
27 #define PORT CONFIG_EXAMPLE_PORT
28 #define KEEPALIVE_IDLE CONFIG_EXAMPLE_KEEPALIVE_IDLE
29 #define KEEPALIVE_INTERVAL CONFIG_EXAMPLE_KEEPALIVE_INTERVAL
30 #define KEEPALIVE_COUNT CONFIG_EXAMPLE_KEEPALIVE_COUNT
31
32 static const char *TAG = "example";
33
do_retransmit(const int sock)34 static void do_retransmit(const int sock)
35 {
36 int len;
37 char rx_buffer[128];
38
39 do {
40 len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
41 if (len < 0) {
42 ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
43 } else if (len == 0) {
44 ESP_LOGW(TAG, "Connection closed");
45 } else {
46 rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
47 ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);
48
49 // send() can return less bytes than supplied length.
50 // Walk-around for robust implementation.
51 int to_write = len;
52 while (to_write > 0) {
53 int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
54 if (written < 0) {
55 ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
56 }
57 to_write -= written;
58 }
59 }
60 } while (len > 0);
61 }
62
tcp_server_task(void * pvParameters)63 static void tcp_server_task(void *pvParameters)
64 {
65 char addr_str[128];
66 int addr_family = (int)pvParameters;
67 int ip_protocol = 0;
68 int keepAlive = 1;
69 int keepIdle = KEEPALIVE_IDLE;
70 int keepInterval = KEEPALIVE_INTERVAL;
71 int keepCount = KEEPALIVE_COUNT;
72 struct sockaddr_storage dest_addr;
73
74 if (addr_family == AF_INET) {
75 struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
76 dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
77 dest_addr_ip4->sin_family = AF_INET;
78 dest_addr_ip4->sin_port = htons(PORT);
79 ip_protocol = IPPROTO_IP;
80 }
81 #ifdef CONFIG_EXAMPLE_IPV6
82 else if (addr_family == AF_INET6) {
83 struct sockaddr_in6 *dest_addr_ip6 = (struct sockaddr_in6 *)&dest_addr;
84 bzero(&dest_addr_ip6->sin6_addr.un, sizeof(dest_addr_ip6->sin6_addr.un));
85 dest_addr_ip6->sin6_family = AF_INET6;
86 dest_addr_ip6->sin6_port = htons(PORT);
87 ip_protocol = IPPROTO_IPV6;
88 }
89 #endif
90
91 int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
92 if (listen_sock < 0) {
93 ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
94 vTaskDelete(NULL);
95 return;
96 }
97 int opt = 1;
98 setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
99 #if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
100 // Note that by default IPV6 binds to both protocols, it is must be disabled
101 // if both protocols used at the same time (used in CI)
102 setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
103 #endif
104
105 ESP_LOGI(TAG, "Socket created");
106
107 int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
108 if (err != 0) {
109 ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
110 ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
111 goto CLEAN_UP;
112 }
113 ESP_LOGI(TAG, "Socket bound, port %d", PORT);
114
115 err = listen(listen_sock, 1);
116 if (err != 0) {
117 ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
118 goto CLEAN_UP;
119 }
120
121 while (1) {
122
123 ESP_LOGI(TAG, "Socket listening");
124
125 struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
126 socklen_t addr_len = sizeof(source_addr);
127 int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
128 if (sock < 0) {
129 ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
130 break;
131 }
132
133 // Set tcp keepalive option
134 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
135 setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
136 setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
137 setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
138 // Convert ip address to string
139 if (source_addr.ss_family == PF_INET) {
140 inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
141 }
142 #ifdef CONFIG_EXAMPLE_IPV6
143 else if (source_addr.ss_family == PF_INET6) {
144 inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
145 }
146 #endif
147 ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
148
149 do_retransmit(sock);
150
151 shutdown(sock, 0);
152 close(sock);
153 }
154
155 CLEAN_UP:
156 close(listen_sock);
157 vTaskDelete(NULL);
158 }
159
app_main(void)160 void app_main(void)
161 {
162 ESP_ERROR_CHECK(nvs_flash_init());
163 ESP_ERROR_CHECK(esp_netif_init());
164 ESP_ERROR_CHECK(esp_event_loop_create_default());
165
166 /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
167 * Read "Establishing Wi-Fi or Ethernet Connection" section in
168 * examples/protocols/README.md for more information about this function.
169 */
170 ESP_ERROR_CHECK(example_connect());
171
172 #ifdef CONFIG_EXAMPLE_IPV4
173 xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET, 5, NULL);
174 #endif
175 #ifdef CONFIG_EXAMPLE_IPV6
176 xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET6, 5, NULL);
177 #endif
178 }
179