OK, I think I have gotten things to work better. Here's my revised code:
Code: Select all
int setup_listen_sock(int *ptr_listen_sock)
{
char addr_str[128];
int addr_family;
int ip_protocol;
int err = 0;
#ifdef CONFIG_EXAMPLE_IPV4
struct sockaddr_in destAddr;
destAddr.sin_addr.s_addr = htonl(INADDR_ANY);
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);
#else // IPV6
struct sockaddr_in6 destAddr;
bzero(&destAddr.sin6_addr.un, sizeof(destAddr.sin6_addr.un));
destAddr.sin6_family = AF_INET6;
destAddr.sin6_port = htons(PORT);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
#endif
*ptr_listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if (*ptr_listen_sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
return errno;
}
ESP_LOGI(TAG, "Socket created");
err = bind(*ptr_listen_sock, (struct sockaddr *)&destAddr, sizeof(destAddr));
if (err != 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
return err;
}
ESP_LOGI(TAG, "Socket binded");
err = listen(*ptr_listen_sock, 1);
if (err != 0) {
ESP_LOGE(TAG, "Error occured during listen: errno %d", errno);
return err;
}
ESP_LOGI(TAG, "Socket listening");
return 0;
}
static void tcp_server_task(void *pvParameters)
{
char addr_str[128];
char rx_buffer[128];
int listen_sock;
int err = setup_listen_sock(&listen_sock);
if(err !=0) {
ESP_LOGE(TAG, "Listening socket unestablished. Server task will be deleted.");
vDeleteTask(NULL);
}
while (1) {
struct sockaddr_in6 sourceAddr; // Large enough for both IPv4 or IPv6
uint addrLen = sizeof(sourceAddr);
int sock = accept(listen_sock, (struct sockaddr *)&sourceAddr, &addrLen);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket accepted");
while (1) {
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
// Error occured during receiving
if (len < 0) {
ESP_LOGE(TAG, "recv failed: errno %d", errno);
break;
}
// Connection closed
else if (len == 0) {
ESP_LOGI(TAG, "Connection closed");
break;
}
// Data received
else {
// Get the sender's ip address as string
if (sourceAddr.sin6_family == PF_INET) {
inet_ntoa_r(((struct sockaddr_in *)&sourceAddr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
} else if (sourceAddr.sin6_family == PF_INET6) {
inet6_ntoa_r(sourceAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
}
rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
ESP_LOGI(TAG, "%s", rx_buffer);
// --- YOUR CODE HERE ---
int err = send(sock, rx_buffer, strlen(rx_buffer), 0);
if (err < 0) {
ESP_LOGE(TAG, "Error occured during sending: errno %d", errno);
break;
}
}
}
if (sock != -1) {
ESP_LOGI(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
vTaskDelay(5);
}
}
vTaskDelete(NULL);
}
In all honesty I don't know how to write to TCP server, so I'm winging it a bit.
I've done a few things here:
* set up the listening socket in a separate function to modularise the code a bit
* if setting up the listening socket fails, then we forego an opportunity to serve TCP. We could presumably improve on this by looping on setup_listen_sock() if necessary. But I think that putting it in the while(1) loop is a bad idea.
* removed mention of shutdown(listen_socket, 0), close(listen_sock) within the sock != -1 block. Espressiv didn't have that code, but many people have suggested it. From what I can make of it, including it is a Bad idea (TM).
* I have changed LOGE to LOGI in the line "Shutting down socket and restarting...". I don't think it's an error; the client might decide to disconnect, and this isn't anything unusual.
Like I say, although the code above isn't perfect, I think it's better than what we had before.
The server won't respond to multiple TCP requests, but at least it won't mess up like it did before. Presumably we'd have to do some additional xTaskCreate's if we wanted to handle multiple connections.
Comments?