在使用esp32 idf例程中的tcp_server和tcp_client通信测试时发现,
在tcp_server端,接收到一帧数据之后必须马上回复至少一个字节,才能保证每帧数据不粘包,
如果不回复操作,300ms以内的通信时延会导致tcp严重粘包,后续解析这些数据费时费力,
可能跟lwip的回环读写机制有关,这严重打乱了双向通信逻辑。
换一种方式,使用udp广播来作为数据传输通道,使用tcp连接来做状态检测,这样就可以
避免粘包问题。
udp广播服务如下
/**
* udp服务器,高速通信,控制器控制命令传输通道(不需要应答的)
* */
static void udp_server_task(void *pvParameters)
{
unsigned char rx_buffer[128];
char addr_str[128];
int addr_family = (int)pvParameters;//ipv4 or ipv6
int ip_protocol = 0;
struct sockaddr_in6 dest_addr;
while (1) {
if (addr_family == AF_INET) {
struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
/***
* 接收广播地址:
* 192.168.100.1
* 192.168.100.255
* 255.255.255.255
*/
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr_ip4->sin_family = AF_INET;
dest_addr_ip4->sin_port = htons(UDP_SERVER_PORT);
ip_protocol = IPPROTO_IP;
} else if (addr_family == AF_INET6) {
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(UDP_SERVER_PORT);
ip_protocol = IPPROTO_IPV6;
}
global_udpsock_handle = socket(addr_family, SOCK_DGRAM, ip_protocol);
if (global_udpsock_handle < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
/**因为正在创建的时候网络可能还没有完全连接上,不能退出*/
vTaskDelay(100 / portTICK_PERIOD_MS);//100ms
continue;
}
ESP_LOGI(TAG, "UDP Socket created");
#if defined(CONFIG_LWIP_NETBUF_RECVINFO) && !defined(CONFIG_EXAMPLE_IPV6)
int enable = 1;
lwip_setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable));
#endif
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
if (addr_family == AF_INET6) {
// Note that by default IPV6 binds to both protocols, it is must be disabled
// if both protocols used at the same time (used in CI)
int opt = 1;