当前位置:网站首页>STM32 LWIP Server、Client如何判断网络异常
STM32 LWIP Server、Client如何判断网络异常
2022-08-07 10:36:00 【喵喵锤锤你小可爱】
平台NUCLEO-H723ZG & LAN8742
方法1:利用TCP的keepalive机制
- 在lwipopts.h中添加宏#define LWIP_TCP_KEEPALIVE 1 来使能keeplive机制。

- 代码如下,主要是使能心跳包机制以及设置相应的时间的次数。当拔掉网线后约1s多时间后串口显示客户端断开连接。
#define PORT 5001
#define RECV_BUF_SIZE (1024)
static void
tcp_socket_echo_server_thread(void *arg)
{
struct sockaddr_in server_addr, client_addr;
int server_fd = -1, cliend_fd = -1;
socklen_t sin_size;
int recv_buf_len,write_buf_len;
int so_keepalive_val = 1; //使能心跳机制
int tcp_keepalive_idle = 1; //发送心跳空闲周期 单位:秒
int tcp_keepalive_intvl = 1; //发送心跳间隔 单位:秒
int tcp_keepalive_cnt = 1; //重发次数
int tcp_nodelay = 1; //不延时发送到合并包
int err = 0;
char *recv_data = (char *)pvPortMalloc(RECV_BUF_SIZE);
if (recv_data == NULL)
{
LOGI("No memory\n");
goto __exit;
}
server_fd = socket(AF_INET, SOCK_STREAM, 0);
//使能心跳机制,默认没有使能
err = setsockopt(server_fd, SOL_SOCKET, SO_KEEPALIVE, &so_keepalive_val, sizeof(int));
LOGI("err : %d\r\n", err);
if (server_fd < 0)
{
LOGI("Socket error\n");
goto __exit;
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
LOGI("Unable to bind\n");
goto __exit;
}
if (listen(server_fd, 5) == -1)
{
LOGI("Listen error\n");
goto __exit;
}
while (1)
{
sin_size = sizeof(struct sockaddr_in);
cliend_fd = accept(server_fd, (struct sockaddr *)&client_addr, &sin_size);
LOGI("new client connected from (%s, %d)\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
//配置心跳检测参数,默认参数时间很长。必须在accept之后,因为不是同一个socket。
err = setsockopt(cliend_fd, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepalive_idle, sizeof(int));
err = setsockopt(cliend_fd, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepalive_intvl, sizeof(int));
err = setsockopt(cliend_fd, IPPROTO_TCP, TCP_KEEPCNT, &tcp_keepalive_cnt, sizeof(int));
//err = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay, sizeof(int));
while (1)
{
recv_buf_len = read(cliend_fd, recv_data, RECV_BUF_SIZE);
if (recv_buf_len <= 0)
{
LOGI("read error!\r\n");
break;
}
LOGI("recv %d len data\n", recv_buf_len);
write_buf_len = write(cliend_fd, recv_data, recv_buf_len);
if (write_buf_len <= 0)
{
LOGI("write error!\r\n");
break;
}
}
if (cliend_fd >= 0)
{
LOGI("close %d\n", cliend_fd);
close(cliend_fd);
}
cliend_fd = -1;
}
__exit:
if (server_fd >= 0)
close(server_fd);
if (recv_data)
vPortFree(recv_data);
vTaskDelete(NULL);
printf("Task exited!\r\n");
}
void tcp_socket_echo_server_init(void)
{
sys_thread_new("tcp_socket_echo_server_thread", tcp_socket_echo_server_thread, NULL, 2048, osPriorityLow);
}
也可以直接修改全局的默认时间,但是不推荐,这里主要是这三个量的含义:
TCP_KEEPIDLE_DEFAULT: 空闲多少时间内没有数据传输,就会发送keepalive的package来检查是否连接
TCP_KEEPINTVL_DEFAULT:发送package的时间间隔
TCP_KEEPCNT_DEFAULT:检查没有连接的次数就会报错
方法2:读于PHY芯片的BSR寄存器判断
这里使用的是NUCLEO-H723ZG的开发板,PHY芯片型号为LAN8742。
uint32_t regvalue = 0;
HAL_ETH_ReadPHYRegister(&EthHandle, LAN8742A_PHY_ADDRESS, LAN8742_BSR, ®value);
if(regvalue & LAN8742_BSR_LINK_STATUS)
{
BSP_LED_On(LED3);
}
else
{
BSP_LED_Off(LED3);
}
方法3:利用netif接口
if (netif_is_link_up(&gnetif))
{
BSP_LED_Off(LED2);
}
else
{
BSP_LED_On(LED2);
}
注意,方法2和3判断的是Server的以太网硬件是否连接到了网络,之间检测服务器自己的故障。方法2和3我将其放到一个单独的Task中循环查询判断。这样并不会影响LWIP的正常工作。
Client使keepalive来判断连接是否正常
断开网线后串口会显示断开,重新插上网线又会连接到服务器
#define SERVER_PORT (6666UL) //目标地址端口号
#define SERVER_IP "192.168.1.1" /*目标地址IP*/
static void
tcp_socket_echo_client_thread(void *arg)
{
struct sockaddr_in server_addr, client_addr;
int server_fd = -1, cliend_fd = -1;
socklen_t sin_size;
int recv_buf_len, write_buf_len;
int so_keepalive_val = 1; //使能心跳机制
int tcp_keepalive_idle = 1; //发送心跳空闲周期 单位:秒
int tcp_keepalive_intvl = 1; //发送心跳间隔 单位:秒
int tcp_keepalive_cnt = 1; //重发次数
int tcp_nodelay = 1; //不延时发送到合并包
int err = 0;
char *recv_data = (char *)pvPortMalloc(RECV_BUF_SIZE);
if (recv_data == NULL)
{
LOGI("No memory\n");
goto __exit;
}
while (1)
{
server_fd = socket(AF_INET, SOCK_STREAM, 0);
//使能心跳机制,默认没有使能
err = setsockopt(server_fd, SOL_SOCKET, SO_KEEPALIVE, &so_keepalive_val, sizeof(int));
LOGI("err : %d\r\n", err);
if (server_fd < 0)
{
LOGI("socket error\n");
goto __exit;
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
server_addr.sin_port = htons(SERVER_PORT);
//err = setsockopt(server_fd, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepalive_idle, sizeof(int));
//err = setsockopt(server_fd, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepalive_intvl, sizeof(int));
//err = setsockopt(server_fd, IPPROTO_TCP, TCP_KEEPCNT, &tcp_keepalive_cnt, sizeof(int));
if (connect(server_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)) < 0)
{
LOGI("connect error\r\n");
close(server_fd);
continue;
}
else
{
//配置心跳检测参数,默认参数时间很长,不配置的话要等待很长时间。
//可以在connect之前配置。如果断网,read就会阻塞相应的次数后就不阻塞了,就会一直报错,直到网线重新连接。
err = setsockopt(server_fd, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepalive_idle, sizeof(int));
err = setsockopt(server_fd, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepalive_intvl, sizeof(int));
err = setsockopt(server_fd, IPPROTO_TCP, TCP_KEEPCNT, &tcp_keepalive_cnt, sizeof(int));
}
while (1)
{
recv_buf_len = read(server_fd, recv_data, RECV_BUF_SIZE);
if (recv_buf_len <= 0)
{
close(server_fd);
LOGI("read error!\r\n");
break;
}
LOGI("recv %d len data\n", recv_buf_len);
write_buf_len = write(server_fd, recv_data, recv_buf_len);
if (write_buf_len <= 0)
{
LOGI("write error!\r\n");
break;
}
}
vTaskDelay(100);
}
__exit:
if (server_fd >= 0)
close(server_fd);
if (recv_data)
vPortFree(recv_data);
vTaskDelete(NULL);
printf("Task exited!\r\n");
}
void tcp_socket_echo_client_init(void)
{
sys_thread_new("tcp_socket_echo_client_thread", tcp_socket_echo_client_thread, NULL, 2048, osPriorityLow);
}
边栏推荐
- 大学三年狂拿国内外十几个3D挑战赛大奖?!国内CG新星崛起
- Mastering Andriod Reverse in 100 Days - Day 1: ADB Principles and Common Commands
- The difference and connection between process and thread
- Material Design
- Product "free gift" + premium investment, Sanofi fired the first shot of bottom-hunting China's biotech assets
- PG core technology articles--MVCC
- NProgress插件(进度条)
- 《bug记录》在利用TCP协议创建【服务器-客户端交互程序】中出现的一些问题
- 公钥、私钥、证书、加密、解密、加签、验签
- formData
猜你喜欢

ctf (easy_md5)

Flask Framework - Application Error Handling

【图像分类】2022-RepLKNet CVPR 31x31卷积了解一下

企业内容建站系统 ModStartCMS v4.5.0 后台登录改版,登录安全增强

Flask框架——应用错误处理

During the meeting, I was scolded by the leader for more than 10 minutes. The scolding made me almost cry, and my voice was shaking.

NProgress插件(进度条)

Apipost协作功能详解

使用对象流传输Student类

China's mobile phones continue to innovate, and the full screen goes further, leading mobile phone innovation
随机推荐
【图像分类】2022-RepLKNet CVPR 31x31卷积了解一下
Flask Framework - Application Error Handling
nodejs环境搭建
质量、重力和重量
Assignment of major and minor device numbers for character devices
Solve the problem that the file content of typora is copied to csdn and the picture cannot be displayed
近期五款高热度真无线降噪耳机横评:国产品牌才是性价比之选
[生物信息]临床研究统计分析成长营14天班
Shell 正则及其命令
For high-performance, ultra-large-scale model training, this combination "debuts"
UGUI系列-Button绑定事件的多种实现
如何仿造websocket请求?
Apipost协作功能详解
100天精通Andriod逆向——第1天:ADB原理及其常用命令
Canvas和SVG的区别
[Flash programming of Autosar memory stack Memory Stack 4.Tc397]
企业内容建站系统 ModStartCMS v4.5.0 后台登录改版,登录安全增强
Niu Ke Duo School-Eezie and Pie- (multiplication + difference on the tree)
Project optimization performance optimization (Unity3D)
Automatic processing software for geometric data topology errors based on FME