TCP Loop Connect

146 篇文章 12 订阅

TCP state machine allows that TCP client establishes connection using connect() to itself. Look on diagram and focus on Simultaneous Open part.

Preconditions

Following conditions are needed for the loop:

  1. Connection is established from HOST to HOST on the same IP
  2. Destination TCP Port has to be from range of ephemeral ports configured on OS
  3. Listen port is unbound

Probability

Preconditions don’t automatically guarantee the loop situation. But they are needed. Loop happens when OS bind the TCP port to client attempting to connect on the same. It can happen only for ephemeral ports.

The correctly designed TCP client/server application should not use the ephemeral ports for service side listener. Otherwise there is a chance it will establish unwanted connection to itself - loop.

In general it depends on various conditions like how OS assign ephemeral ports and how TCP client constructs sockets, but probability increases when:

  1. TCP client creates for each connection new socket and OS assigns new ephemeral port (done usually in some high-level languages by programmers)
/* 
 * Try establish connection with 127.0.0.1:33333
 * THIS IS BAD EXAMPLE because it behaves like infinite loop at various 
 * conditions 
 */

do {  
    socket = getSockFromSystem();      /* it may use always different ephemeral port */
    connectTo(socket, 127.0.0.1, 33333);  /* it may connect to itself if ephemeral port == 33333 */ 
} while(isNotConnected(socket));

/* ... continue with established connection ... */

Ephemeral ports

For the details please read article describing them, basically on linux they can be displayed using:

$ cat /proc/sys/net/ipv4/ip_local_port_range 
32768	61000

Do not use for TCP lister port the value from that range.

Example TCP Client

Compile as gcc -o tcploop tcploop.c

/* tcploop.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <netdb.h>
#include <string.h>

int main(int argc, char *argv[])
{
  int port = 0;
  int sckfd = 0;
  int opt = 1;
  struct sockaddr_in *remote;
  char *ip = "127.0.0.1";
  int rc;
  long n = 0;

  if(argc != 2){
    printf("Usage: %s <listen port>\n", argv[0]);
    exit(1);
  }
  port = atoi(argv[1]);

  /* Set to Localhost:Port */
  remote = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in *));
  remote->sin_family = AF_INET;
  assert(inet_pton(AF_INET, ip, (void *)(&(remote->sin_addr.s_addr))) > 0);
  remote->sin_port = htons(port);

  /* create socket */
  assert((sckfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) > 0);
  assert(setsockopt(sckfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) >= 0);

  printf("Trying to connect..."); fflush(stdout);
  while(1) {
    n++;
    rc = connect(sckfd, (struct sockaddr *)remote, sizeof(struct sockaddr)); 
    if(rc < 0){
      if(n % 1000 == 0) { printf("."); fflush(stdout); }
      continue;
    }
    else {
      printf("Connected after %ld tries\n", n);
      break;
    }
  }
  /* Wait for Enter */
  printf("Press Enter to Continue...\n");
  getchar();
  close(sckfd);
  return 0;
}

Example output

Three commands outputs:

  1. Show how Ephemeral port range is configured
  2. Invocation of tcploop with port from that range (33333)
  3. lsof output for port 33333 in the other terminal
$ cat /proc/sys/net/ipv4/ip_local_port_range 
32768	61000

$ ./tcploop 33333
Trying to connect..............Connected after 11263 tries
Press Enter to Continue...

$ lsof -n | grep 33333
COMMAND     PID  USER   FD      TYPE     DEVICE  SIZE/OFF       NODE NAME
tcploop    3440 dixie    3u     IPv4     163396       0t0        TCP 127.0.0.1:33333->127.0.0.1:33333 (ESTABLISHED)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ESP32可以通过TCP/IP协议进行通信。以下是使用ESP-IDF框架进行TCP通信的基本步骤: 1. 创建TCP服务器或客户端 使用`tcpip_adapter_init()`初始化TCP/IP协议栈,然后使用`tcpip_adapter_dhcpc_start()`启动DHCP客户端以获取IP地址。然后,使用`tcp_new()`函数创建一个TCP服务器或客户端。服务器使用`tcp_bind()`绑定IP地址和端口号,客户端使用`tcp_connect()`连接服务器。 2. 监听和接受连接 对于服务器,使用`tcp_listen()`开始监听连接请求,然后使用`tcp_accept()`接受连接请求并创建新的TCP连接。服务器可以使用`tcp_write()`和`tcp_read()`函数发送和接收数据。对于客户端,可以使用`tcp_write()`和`tcp_read()`函数发送和接收数据。 3. 关闭连接 使用`tcp_close()`函数关闭TCP连接。 以下是一个简单的ESP32 TCP服务器示例代码: ```c #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "esp_wifi.h" #include "esp_event_loop.h" #include "esp_log.h" #include "nvs_flash.h" #include "lwip/err.h" #include "lwip/sys.h" #include "lwip/api.h" #define PORT 80 static const char *TAG = "tcp_server"; void tcp_server_task(void *pvParameters) { struct netconn *conn, *newconn; err_t err; char buffer[1024]; int len; conn = netconn_new(NETCONN_TCP); netconn_bind(conn, NULL, PORT); netconn_listen(conn); do { err = netconn_accept(conn, &newconn); if (err == ERR_OK) { ESP_LOGI(TAG, "New client connected"); do { len = netconn_recv(newconn, buffer, sizeof(buffer), NETCONN_COPY); if (len > 0) { buffer[len] = 0; ESP_LOGI(TAG, "Received data: %s", buffer); netconn_write(newconn, buffer, len, NETCONN_COPY); } } while (len > 0); netconn_close(newconn); netconn_delete(newconn); ESP_LOGI(TAG, "Client disconnected"); } } while (err == ERR_OK); netconn_close(conn); netconn_delete(conn); } void app_main() { nvs_flash_init(); tcpip_adapter_init(); tcpip_adapter_dhcpc_start(TCPIP_ADAPTER_IF_STA); xTaskCreate(tcp_server_task, "tcp_server", 2048, NULL, 5, NULL); } ``` 该示例代码创建一个TCP服务器,监听端口80。当有新的客户端连接到服务器时,服务器将接收客户端发送的数据,并将数据发送回客户端。在ESP-IDF中,可以使用`netconn`结构体和`lwip/api.h`头文件来进行TCP通信。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值