平台NUCLEO-H723ZG & LAN8742
#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:检查没有连接的次数就会报错
这里使用的是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);
}
if (netif_is_link_up(&gnetif))
{
BSP_LED_Off(LED2);
}
else
{
BSP_LED_On(LED2);
}
注意,方法2和3判断的是Server的以太网硬件是否连接到了网络,之间检测服务器自己的故障。方法2和3我将其放到一个单独的Task中循环查询判断。这样并不会影响LWIP的正常工作。
断开网线后串口会显示断开,重新插上网线又会连接到服务器
#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);
}