STM32 lwip freeRTOS实现ping
程序员文章站
2024-02-22 18:40:52
...
一、STM32CubeMX配置:
这里展示几个关键配置
1.打开ICMP功能,这个也是默认打开的。ping使用的就是ICMP协议
2.设置RAW模式接收字节数,这里默认是0,设置成和TCP、UDP一样
3.设置以太网中断优先级,这里需要调低,不然在freeRTOS中卡住,裸机中可不用设置
二、生成的代码中更改一些配置:
1.打开RAW功能
2.打开接收超时
三、代码
1.变量定义
typedef struct icmp_hdr
{
unsigned char icmp_type; // 消息类型
unsigned char icmp_code; // 代码
unsigned short icmp_checksum; // 校验和
// 下面是回显头
unsigned short icmp_id; // 用来惟一标识此请求的ID号
unsigned short icmp_sequence; // ***
unsigned long icmp_timestamp; // 时间戳
} ICMP_HDR, *PICMP_HDR;
typedef struct _IPHeader// 20字节的IP头
{
uint8_t iphVerLen; // 版本号和头长度(各占4位)
uint8_t ipTOS; // 服务类型
uint16_t ipLength; // 封包总长度,即整个IP报的长度
uint16_t ipID; // 封包标识,惟一标识发送的每一个数据报
uint16_t ipFlags; // 标志
uint8_t ipTTL; // 生存时间,就是TTL
uint8_t ipProtocol; // 协议,可能是TCP、UDP、ICMP等
uint16_t ipChecksum; // 校验和
uint32_t ipSource; // 源IP地址
uint32_t ipDestination; // 目标IP地址
} IPHeader, *PIPHeader;
typedef int SOCKET;
2.实现代码
int ETH_PingWork(const char * szDestIp)
{
long nRet = 0;
/*****************第一步:申请SOCKET************************************/
uint32_t ip = inet_addr(szDestIp);
SOCKET sRaw = lwip_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if(sRaw == -1)
{
printf("Cannot create socket! Error %d\r\n", errno);
return errno;
}
// 设置接收超时
struct timeval tv_out;
tv_out.tv_sec = 1;
tv_out.tv_usec = 0;
nRet = setsockopt(sRaw, SOL_SOCKET,SO_RCVTIMEO, &tv_out, sizeof(tv_out));
if(-1 == nRet)
{
printf("Cannot set timeout! %d \r\n",errno);
}
/*****************第二步:组ICMP包************************************/
// 创建ICMP封包
char buff[sizeof(ICMP_HDR) + 32];
ICMP_HDR* pIcmp = (ICMP_HDR*)buff;
// 填写ICMP封包数据
pIcmp->icmp_type = 8; // 请求一个ICMP回显
pIcmp->icmp_code = 0;
pIcmp->icmp_id = (uint16_t)0x1234;
pIcmp->icmp_checksum = 0;
pIcmp->icmp_sequence = 0;
pIcmp->icmp_timestamp = 0xffff;
// 填充数据部分,可以为任意
memset(&buff[sizeof(ICMP_HDR)], 'E', 32);
/*****************第三步:发送ICMP包************************************/
//设置目的地址
struct sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_port = htons(0);
dest.sin_addr.s_addr = ip;
//pIcmp->icmp_checksum = checksum((uint16_t*)buff, sizeof(ICMP_HDR) + 32); //如果ping过去没回应需要屏蔽这条
//printf("icmp_checksum = %x\r\n",pIcmp->icmp_checksum);
nRet = (long)sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (struct sockaddr *)&dest, sizeof(dest));
if(nRet == -1)
{
printf(" sendto() failed: %d \r\n", errno);
return -1;
}
printf("sendto = %ld\r\n",nRet);
/*****************第四步:接收ICMP包************************************/
char recvBuf[1024];
struct sockaddr_in from;
socklen_t nLen = sizeof(from);
nRet = (long)recvfrom(sRaw, recvBuf, 1024, 0, (struct sockaddr *)&from, &nLen);
if(nRet == -1)
{
printf(" recvfrom() failed: %d\r\n", errno);
return -1;
}
printf("recvfrom = %ld\r\n",nRet);
/*****************第五步:解析ICMP包************************************/
if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR))
{
printf(" Too few bytes from %s \r\n", inet_ntoa(from.sin_addr));
}
#if 0 // IP头解析
IPHeader * header = (IPHeader*)recvBuf;
struct in_addr a;
a.s_addr = header->ipSource;
printf("source ip %s\n", inet_ntoa(a));
a.s_addr = header->ipDestination;
printf("dest ip %s\n", inet_ntoa(a));
#endif
ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); // 接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头
if(pRecvIcmp->icmp_type != 0)// 回显
{
printf("nonecho type %d recvd \r\n", pRecvIcmp->icmp_type);
return -1;
}
if(pRecvIcmp->icmp_id != 0x1234)
{
printf(" someone else's packet! \r\n");
return -1;
}
printf(" %d bytes from %s:", (int)nRet, inet_ntoa(from.sin_addr));
printf(" icmp_seq = %d. ", pRecvIcmp->icmp_sequence);
printf(" \r\n");
return 0;
}