异常断开模拟
1、单片机端做服务端(只监听一个客户端),电脑做客户端连接
2、尝试连接确定通信正常,断开网线。电脑客户端点击断开
3、经过一段时间(超过tcp/ip 3次握手时间)
4、接回网线后发现可以连接上但通信异常
原因分析
void StartDefaultTask(void *argument)
{/* init code for LWIP */MX_LWIP_Init();/* USER CODE BEGIN StartDefaultTask */struct sockaddr_in server_addr,client_addr;socklen_t sin_size;int recv_data_len;static uint8_t recv_data[RECV_DATA];again:sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){LWIP_TCP_DEBUG("Socket error\n");close(sockfd);vTaskDelay(100);goto again;}//server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(LOCAL_PORT);memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){LWIP_TCP_DEBUG("Unable to bind\n");close(sockfd);vTaskDelay(100);goto again;}if (listen(sockfd, BACKLOG) == -1){LWIP_TCP_DEBUG("Listen error\n");close(sockfd);vTaskDelay(100);goto again;}/* Infinite loop */for(;;){sin_size = sizeof(struct sockaddr_in);connected = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);LWIP_TCP_DEBUG("new client connected from (%s, %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));int tcp_nodelay = 1;//don't delay send to coalesce packetssetsockopt(connected,IPPROTO_TCP,TCP_NODELAY,(void *) &tcp_nodelay,sizeof(int));while(1){recv_data_len = recv(connected, recv_data, RECV_DATA, 0);if (recv_data_len <= 0){break;}// write(connected,recv_data,recv_data_len);writeToRxBuf(recv_data, recv_data_len);}if (connected >= 0){close(connected);}connected = -1;//osDelay(1);}/* USER CODE END StartDefaultTask */
}
服务端未收到正常断开消息导致一直阻塞recv_data_len = recv(connected, recv_data, RECV_DATA, 0);
解决方案
TCP的keepalive机制
STM32 LWIP Server、Client如何判断网络异常_lwip检测网络状态-CSDN博客
void StartDefaultTask(void *argument)
{/* init code for LWIP */MX_LWIP_Init();/* USER CODE BEGIN StartDefaultTask */struct sockaddr_in server_addr,client_addr;socklen_t sin_size;int recv_data_len;static uint8_t recv_data[RECV_DATA];int so_keepalive_val = 1; //使能心跳机制int tcp_keepalive_idle = 3; //发�?�心跳空闲周�? 单位:秒int tcp_keepalive_intvl = 3; //发�?�心跳间�? 单位:秒int tcp_keepalive_cnt = 3; //重发次数
// int tcp_nodelay = 1; //不延时发送到合并�?int err = 0;again:sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){LWIP_TCP_DEBUG("Socket error\n");close(sockfd);vTaskDelay(100);goto again;}//使能心跳机制,默认没有使�?err = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &so_keepalive_val, sizeof(int));if(err){}//server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(LOCAL_PORT);memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1){LWIP_TCP_DEBUG("Unable to bind\n");close(sockfd);vTaskDelay(100);goto again;}if (listen(sockfd, BACKLOG) == -1){LWIP_TCP_DEBUG("Listen error\n");close(sockfd);vTaskDelay(100);goto again;}/* Infinite loop */for(;;){sin_size = sizeof(struct sockaddr_in);connected = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);//配置心跳�?测参数,默认参数时间很长。必须在accept之后,因为不是同�?个socket�?err = setsockopt(connected, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepalive_idle, sizeof(int));err = setsockopt(connected, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepalive_intvl, sizeof(int));err = setsockopt(connected, IPPROTO_TCP, TCP_KEEPCNT, &tcp_keepalive_cnt, sizeof(int));LWIP_TCP_DEBUG("new client connected from (%s, %d)\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));int tcp_nodelay = 1;//don't delay send to coalesce packetssetsockopt(connected,IPPROTO_TCP,TCP_NODELAY,(void *) &tcp_nodelay,sizeof(int));while(1){recv_data_len = recv(connected, recv_data, RECV_DATA, 0);// recv_data_len = recv(connected, recv_data, RECV_DATA, MSG_DONTWAIT);// if (recv_data_len == -1)// {// if (errno == EAGAIN || errno == EWOULDBLOCK)// {// osDelay(1);// continue;// }perror("read");exit(-1);// break;// }else if(recv_data_len > 0){// writeToRxBuf(recv_data, recv_data_len);printf("recv client data : %s\n", recv_buf);// }else if(recv_data_len == 0){printf("client closed\n");// break;// }if (recv_data_len <= 0){break;}// write(connected,recv_data,recv_data_len);writeToRxBuf(recv_data, recv_data_len);}if (connected >= 0){close(connected);}connected = -1;//osDelay(1);}/* USER CODE END StartDefaultTask */
}