经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » TCP/IP和HTTP » 查看文章
CH395实现主动ping对端功能(代码及说明)
来源:cnblogs  作者:lqlq123  时间:2023/12/18 16:18:15  对本文有异议
目录
  • PING原理

1.简介

  PING是基于ICMP(Internet Control Message Protocol)协议工作的。ICMP报文是封装在IP包中,工作在网络层。

2.协议

  ICMP包头的类型字段有2类,一类是查询报文,用于检测通信链路是否正常;一类是差错报文,通知出错原因。

  回送消息是用于进行通信的主机间,判断所发的数据包是否已经成功到达对端的一种消息,ping命令就是利用这个消息实现的:可以向对端发送回送请求的消息(request,8),也可以接收对端主机发送回来的应答(reply,0)

Type(8位) Code(8位)
Checksum(16位)
Identifier(16位)
Sequence Number(16位)
选项数据

 

Type(类型):0代表回送应答,8代表回送请求

Code(代码):值为0

Checksum(校验和)

 Identifier(标识符):Unix系统在实现ping程序时,把ICMP报文中的标识符字段设置成发送进程的PID号

Sequence Number(序号):从0开始,每发送一次新的请求就加1

 抓包:

请求包(Response frame:40 表示应答此请求的reply包是N0.40这一包)

 应答包(Request frame:37 表示此包是对N0.37这一请求包的应答)

 3.通信流程

  ①执行ping 192.168.x.x命令后,源主机会先构建一个ICMP请求消息数据包(类型为8)

  ②由ICMP协议将这个数据包连同192.168.x.x这一地址一起交给IP层。IP层将以192.168.x.x作为目的地址,本地ip地址作为源地址,协议字段置为1表示是ICMP协议,再加上一些其他的信息构建成一个IP数据包。

  ③之后,需要加入MAC头,如果在本地ARP映射表中能直接查到192.168.x.x这一地址所对应的MAC地址,则可以直接使用。在获取MAC地址后,由数据链路层构建一个数据帧,其中,目的地址是刚刚找到的MAC地址,源地址是本机的MAC地址。

  ④对端设备收到这个数据帧后,将其中的目的MAC地址和自己的本地MAC地址对比,如果符合则接收,不符合就丢弃。如果MAC地址符合,那么提取出IP数据包,经IP层检查后,再提取出有用的相关信息给ICMP协议。

  ⑤识别出这是一个ping请求帧后,将构建一个ICMP应答包(类型为0),经过同样的流程传给发送请求包的主机。

  • 代码解释(CH395.c)

1.设置395相关网络参数及对端,将ip包协议字段置为1,表示为Internet控制消息 (ICMP)协议类型。

  1. 1 /* CH395相关定义 */
  2. 2 const UINT8 CH395IPAddr[4] = {192,168,1,126}; /* CH395IP地址 */
  3. 3 const UINT8 CH395GWIPAddr[4] = {192,168,1,1}; /* CH395网关 */
  4. 4 const UINT8 CH395IPMask[4] = {255,255,255,0}; /* CH395子网掩码 */
  5. 5 const UINT8 DestIPAddr[4] = {192,168,1,23}; /* 目的IP */
  6. 6 const UINT8 IPRawProto = 1; /* IP包协议类型 */

 2.初始化395和socket的参数,并将socket设置为ipraw模式。(关于ipraw模式的介绍和使用可以查阅CH395的官方手册)

  1. 1 /**********************************************************************************
  2. 2 * Function Name : InitCH395InfParam
  3. 3 * Description : 初始化CH395Inf参数
  4. 4 * Input : None
  5. 5 * Output : None
  6. 6 * Return : None
  7. 7 **********************************************************************************/
  8. 8 void InitCH395InfParam(void)
  9. 9 {
  10. 10 memset(&CH395Inf,0,sizeof(CH395Inf)); /* 将CH395Inf全部清零*/
  11. 11 memcpy(CH395Inf.IPAddr,CH395IPAddr,sizeof(CH395IPAddr)); /* 将IP地址写入CH395Inf中 */
  12. 12 memcpy(CH395Inf.GWIPAddr,CH395GWIPAddr,sizeof(CH395GWIPAddr)); /* 将网关IP地址写入CH395Inf中 */
  13. 13 memcpy(CH395Inf.MASKAddr,CH395IPMask,sizeof(CH395IPMask)); /* 将子网掩码写入CH395Inf中 */
  14. 14 }
  15. 15
  16. 16 /**********************************************************************************
  17. 17 * Function Name : InitSocketParam
  18. 18 * Description : 初始化socket
  19. 19 * Input : None
  20. 20 * Output : None
  21. 21 * Return : None
  22. 22 **********************************************************************************/
  23. 23 void InitSocketParam(void)
  24. 24 {
  25. 25 memset(&SockInf,0,sizeof(SockInf)); /* 将SockInf[0]全部清零*/
  26. 26 memcpy(SockInf.IPAddr,DestIPAddr,sizeof(DestIPAddr)); /* 将目的IP地址写入 */
  27. 27 SockInf.ProtoType = PROTO_TYPE_IP_RAW; /* IP RAW模式 */
  28. 28 SockInf.IPRAWProtoType = IPRawProto;
  29. 29 }
  30. 30
  31. 31 /**********************************************************************************
  32. 32 * Function Name : CH395SocketInitOpen
  33. 33 * Description : 配置CH395 socket 参数,初始化并打开socket
  34. 34 * Input : None
  35. 35 * Output : None
  36. 36 * Return : None
  37. 37 **********************************************************************************/
  38. 38 void CH395SocketInitOpen(void)
  39. 39 {
  40. 40 UINT8 i;
  41. 41 /* socket 0为IP RAW模式 */
  42. 42 CH395SetSocketDesIP(0,SockInf.IPAddr); /* 设置socket 0目标IP地址 */
  43. 43 CH395SetSocketProtType(0,SockInf.ProtoType); /* 设置socket 0协议类型 */
  44. 44 CH395SetSocketIPRAWProto(0,SockInf.IPRAWProtoType); /* 设置协议字段 */
  45. 45
  46. 46 i = CH395OpenSocket(0); /* 打开socket 0 */
  47. 47 mStopIfError(i); /* 检查是否成功 */ /* 检查是否成功 */
  48. 48 }

 3.在socket的接收中断中,调用 CH395GetRecvLength()函数获取当前缓冲区中的数据长度,在调用CH395GetRecvData()函数读取数据,最后调用 CH395IcmpRecvData()函数对收到的ping包处理和分析。

  1. 1 /**********************************************************************************
  2. 2 * Function Name : CH395SocketInterrupt
  3. 3 * Description : CH395 socket 中断,在全局中断中被调用
  4. 4 * Input : sockindex
  5. 5 * Output : None
  6. 6 * Return : None
  7. 7 **********************************************************************************/
  8. 8 void CH395SocketInterrupt(UINT8 sockindex)
  9. 9 {
  10. 10 UINT8 sock_int_socket;
  11. 11
  12. 12 UINT16 len;
  13. 13
  14. 14 sock_int_socket = CH395GetSocketInt(sockindex); /* 获取socket 的中断状态 */
  15. 15 // printf("SOCK status : %02x\n",sock_int_socket);
  16. 16 if(sock_int_socket & SINT_STAT_SENBUF_FREE) /* 发送缓冲区空闲,可以继续写入要发送的数据 */
  17. 17 {
  18. 18 }
  19. 19 if(sock_int_socket & SINT_STAT_SEND_OK) /* 发送完成中断 */
  20. 20 {
  21. 21 IcmpSuc++;
  22. 22 }
  23. 23 if(sock_int_socket & SINT_STAT_RECV) /* 接收中断 */
  24. 24 {
  25. 25 printf("recv back!\r\n");
  26. 26 len = CH395GetRecvLength(sockindex); /* 获取当前缓冲区内数据长度 */
  27. 27 if(len == 0)return;
  28. 28 if(len > 512) len = 512;
  29. 29 CH395GetRecvData(sockindex,len,MyBuffer); /* 读取数据 */
  30. 30 CH395IcmpRecvData(len,MyBuffer);
  31. 31 }
  32. 32 if(sock_int_socket & SINT_STAT_CONNECT) /* 连接中断,仅在TCP模式下有效*/
  33. 33 {
  34. 34
  35. 35 }
  36. 36 if(sock_int_socket & SINT_STAT_DISCONNECT) /* 断开中断,仅在TCP模式下有效 */
  37. 37 {
  38. 38 }
  39. 39 if(sock_int_socket & SINT_STAT_TIM_OUT) /* 超时中断 */
  40. 40 {
  41. 41 }
  42. 42 }

 4.在CH395PINGInit()函数中,进行引脚初始化、复位395、初始化395、初始化socket、打开socket等操作。

调用 CH395EnablePing(1)函数开启ping功能(默认开启)

InitParameter()--- PING参数变量初始化

InitPing()--- PING初始化,生成ping查询报文

  1. 1 /**********************************************************************************
  2. 2 * Function Name : main
  3. 3 * Description : main主函数
  4. 4 * Input : None
  5. 5 * Output : None
  6. 6 * Return : None
  7. 7 **********************************************************************************/
  8. 8 void CH395PINGInit(void)
  9. 9 {
  10. 10 UINT8 i;
  11. 11 Delay_Ms(100);
  12. 12 printf("CH395EVT Test Demo\n");
  13. 13 CH395_PORT_INIT();
  14. 14 CH395CMDReset(); /* 复位CH395芯片 */
  15. 15 Delay_Ms(1000); /* 延时1000毫秒,要分开写,否则无效 */
  16. 16
  17. 17 i = CH395CMDGetVer(); /*获取芯片以及固件版本号 */
  18. 18 printf("固件版本号:%02x\n",i);
  19. 19 InitCH395InfParam(); /* 初始化CH395相关变量 */
  20. 20 i = CH395Init(); /* 初始化CH395芯片 */
  21. 21 printf("CH395Init:%02x\n",i);
  22. 22 mStopIfError(i);
  23. 23
  24. 24 while(1)
  25. 25 { /* 等待以太网连接成功*/
  26. 26 if(CH395CMDGetPHYStatus() == PHY_DISCONN) /* 查询CH395是否连接 */
  27. 27 {
  28. 28 printf("CH395 DISCONN\n");
  29. 29 Delay_Ms(200); /* 未连接则等待200MS后再次查询 */
  30. 30 }
  31. 31 else
  32. 32 {
  33. 33 printf("CH395 Connect Ethernet\n"); /* CH395芯片连接到以太网,此时会产生中断 */
  34. 34 break;
  35. 35 }
  36. 36 }
  37. 37 InitSocketParam(); // /* 初始化socket相关变量 */
  38. 38 CH395SocketInitOpen();//
  39. 39
  40. 40 //CH395EnablePing(1);//默认开启
  41. 41 InitParameter();
  42. 42 InitPing();
  43. 43 printf("!!\r\n");
  44. 44 // TIM2_Init();
  45. 45 // Intervalometer_4ms();
  46. 46 }
  1. 1 /**********************************************************************************
  2. 2 * Function Name : InitParameter
  3. 3 * Description : Ping参数初始化
  4. 4 * Input : None
  5. 5 * Output : None
  6. 6 * Return : None
  7. 7 **********************************************************************************/
  8. 8 void InitParameter( void )
  9. 9 {
  10. 10 UNREACH_COUNT=0;
  11. 11 TIMOUT_COUNT=0;
  12. 12 SUCRECV_COUNT=0;
  13. 13 IcmpCont=0;
  14. 14 IcmpSeq=0;
  15. 15 IcmpSuc=0;///
  16. 16 icmp_tmp=0;
  17. 17 CH395GetIPInf(CH395INF_BUF);
  18. 18 }
  19. 19
  20. 20 /**********************************************************************************
  21. 21 * Function Name : InitPing
  22. 22 * Description : Ping初始化
  23. 23 * Input : None
  24. 24 * Output : None
  25. 25 * Return : None
  26. 26 **********************************************************************************/
  27. 27 void InitPing( void )
  28. 28 {
  29. 29 IcmpHeader head;
  30. 30 UINT32 check_sum=0;
  31. 31 UINT8 i;
  32. 32
  33. 33 IcmpCont++;
  34. 34 IcmpSeq += 1;
  35. 35 head.i_type = ICMP_HEAD_TYPE;//8
  36. 36 head.i_code = ICMP_HEAD_CODE;//0
  37. 37 head.i_id = ICMP_HEAD_ID;//512
  38. 38 head.i_seq = ICMP_HEAD_SEQ+IcmpSeq;//100+
  39. 39 memset(head.i_data,0,sizeof(head.i_data));
  40. 40
  41. 41 for( i=0;i<ICMP_DATA_BYTES;i++ ){//32
  42. 42 if(i<26)
  43. 43 head.i_data[i] = i + 'a';
  44. 44 else
  45. 45 head.i_data[i] = i + 'a' - 26;
  46. 46 if(i%2==0)
  47. 47 check_sum += head.i_data[i]<<8;
  48. 48 else
  49. 49 check_sum += head.i_data[i];
  50. 50 }
  51. 51 check_sum += head.i_type<<8;
  52. 52 check_sum += head.i_code;
  53. 53 check_sum += head.i_id;
  54. 54 check_sum += head.i_seq;
  55. 55 head.i_cksum = check_sum>>16;
  56. 56 head.i_cksum += check_sum&0xffff;
  57. 57 head.i_cksum = 0xffff - head.i_cksum;
  58. 58 memset(SEND_BUF,0,sizeof(SEND_BUF));
  59. 59 memcpy(SEND_BUF,&head,sizeof(head));
  60. 60
  61. 61 SEND_BUF[2] = head.i_cksum >> 8;
  62. 62 SEND_BUF[3] = head.i_cksum & 0xff;
  63. 63 SEND_BUF[4] = head.i_id >> 8;
  64. 64 SEND_BUF[5] = head.i_id & 0xff;
  65. 65 SEND_BUF[6] = head.i_seq >> 8;
  66. 66 SEND_BUF[7] = head.i_seq & 0xff;
  67. 67 }

 5.在main函数中,通过 CH395SendData()函数发送 InitPing()函数生成的40字节的数据,简单实现按键按一下发送一次ping请求包。

  1. 1 /**********************************************************************************
  2. 2 * Function Name : main
  3. 3 * Description : main主函数
  4. 4 * Input : None
  5. 5 * Output : None
  6. 6 * Return : None
  7. 7 **********************************************************************************/
  8. 8 int main(void)
  9. 9 {
  10. 10
  11. 11 Delay_Init();
  12. 12 USART_Printf_Init(115200);
  13. 13 Delay_Ms(100);
  14. 14 /* 延时100毫秒 */
  15. 15 GPIO_Toggle_INIT_C();
  16. 16 CH395PINGInit();
  17. 17 //
  18. 18 //Delay_Ms(2000);
  19. 19 //CH395SendData(0,MyBuffer1,2);
  20. 20 //Delay_Ms(20);
  21. 21 // CH395SendData( 0,SEND_BUF,40 );
  22. 22 // Delay_Ms(20);
  23. 23 // CH395SendData( 0,SEND_BUF,40 );
  24. 24 //
  25. 25 // Delay_Ms(20);
  26. 26 // CH395SendData( 0,SEND_BUF,40 );
  27. 27 // Delay_Ms(20);
  28. 28 // CH395SendData( 0,SEND_BUF,40 );
  29. 29 while(1)
  30. 30 {
  31. 31 if(CH395_INT_WIRE == 0)
  32. 32 {
  33. 33 CH395GlobalInterrupt();
  34. 34 }
  35. 35 if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) == RESET)
  36. 36 {
  37. 37 Delay_Ms(2);
  38. 38 if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) == RESET)
  39. 39 {
  40. 40 printf("PING %d times\r\n",++t);
  41. 41 CH395SendData( 0,SEND_BUF,40 );
  42. 42 while(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_12) == RESET);
  43. 43 }
  44. 44 }
  45. 45 // CH395SendData( 0,SEND_BUF,40 );
  46. 46 // Delay_Ms(1500);
  47. 47 /*查询状态执行相应命令*/
  48. 48 // CH395_PINGCmd();
  49. 49 // InitPing();
  50. 50 }
  51. 51 }

效果如下,按下按键发送一次ping请求包,对端reply一包数据。(一共发了3次ping_request,成功进入socket接收中断接收处理回包)


 

工程代码及抓包:https://files.cnblogs.com/files/blogs/808422/CH395_%E4%B8%BB%E5%8A%A8ping.zip?t=1702869711&download=true

原文链接:https://www.cnblogs.com/wchwchlq/p/17910432.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号