皮皮网

【自适应单页面源码】【粒子特效js源码】【cocos排球游戏源码】ping 源码详解

时间:2024-11-25 08:31:42 分类:知识 来源:云储存外链源码

1.ping命令全链路分析(2)
2.c语言ping程序中文注释
3.windows和linux下ping命令使用的不同之处
4.开源的源码TCPing网络测试工具

ping 源码详解

ping命令全链路分析(2)

       本文使用 Zhihu On VSCode 创作并发布

       上篇文章对开源网络协议栈实现 tapip 触发进行了分析,探讨了执行 ping 命令时,详解数据包是源码如何到达网络协议栈的。本文将继续探讨 ping 命令与网络协议栈的详解联系。目前广泛使用的源码网络协议栈是五层协议划分:应用层、传输层、详解自适应单页面源码网络层、源码链路层和物理层。详解ping 命令采用的源码 ICMP 协议位于网络层,但特别之处在于 ICMP 报文是详解封装在 IP 报文之内的。下文将从 ICMP 协议开始分析。源码

       ICMP 协议

       ping 命令的详解执行过程实际上包含了源端向目的端发送 ICMP 请求报文和目的端向源端发送 ICMP 回复报文的过程。ICMP 报文头包含了 ICMP type、源码code、详解id、源码粒子特效js源码seq 等字段,报文头部为 字节,payload 部分数据长度为可变长度。

       ICMP 报文头部包含 8bit 类型码 type、8bit 代码 code 和 bit 校验和 checksum,其余部分内容和类型码 type 相关。ICMP 报文中定义 type 字段包含以下几种,cocos排球游戏源码type 字段与 code 的详细对应关系见附录 1:

       其中,ping 命令使用的报文类型为响应请求和响应应答,其报文格式如图:

       ICMP 响应请求

       在 tapip 中,ICMP 响应请求报文构造是在 ping.c:send_packet() 函数中完成的。ICMP 报文填充构建代码如下:

       根据上一篇文章的分析,tapip 采用一个 tap 设备作为虚拟网卡,ICMP 数据报文最终通过 wirte() 接口写入 tap 设备文件中,微擎 海报 源码最终被 Linux 内核中的网络协议栈处理。这里还是先从 tapip 出发,研究下网络协议栈中如何处理 ICMP 响应请求报文。在 tapip 源码中,处理 ICMP 响应请求报文在函数 icmp_echo_request() 中,其函数调用栈如下:

       在 Linux 系统中,数据包到达网络设备后会触发中断,android room源码解析网卡驱动程序将对应数据包传递到内核网络协议栈处理,处理结果通过系统调用接口返回给应用程序(ping 应用)。

       tapip 作为一种用户态实现,网络设备 net device 是通过 tap 设备模拟的,tap 设备文件描述符中被写入数据包就相当于网卡设备接收到网络数据包;

       网卡驱动程序的工作对应 tapip 中 netdev_interrupt() 到 veth_rx() 之间的过程:首先在中断处理函数中调用 veth_poll() 函数采用轮询的方式检查 tap 设备的文件描述符是否有写入事件;当发生写入事件时,veth_rx() 函数被调用,从文件描述符中读取数据包,并传递到网络协议栈中处理,此时,网络协议栈处理的入口 net_in() 被调用。

       网络协议栈按照网络分层模型进行处理:

       ICMP 响应回复

       ICMP 响应回复的处理过程与接收侧处理 ICMP 响应请求的流程基本一致,不同点在于最后 icmp 报文响应的处理,其 type 为 0,对应的处理函数为 icmp_echo_reply(),具体函数调用栈如下:

       总结

       本文主要分析了用户态网络协议栈 tapip 处理 ping 命令对应的 ICMP 报文的过程,后续将结合 Linux 内核分析这个过程在内核中是如何处理的,另外还会分析下 ARP 协议的实现。

       学海无涯,感觉 tapip 的实现逻辑清晰,读起来非常舒服,非常推荐对网络感兴趣的同学学习参考。

       (最近特别水逆,希望能早日走出困境,迎来光明吧。)

       附录 1: ICMP 报文类型表

        markdown

       | 类型 Type | 代码 Code | 描述 |

       | :------: | :------: | :--------------------------: |

       | 0 | 0 | 回显应答(ping 应答) |

       | 3 | 0 | 网络不可达 |

       | 3 | 1 | 主机不可达 |

       | 3 | 2 | 协议不可达 |

       | 3 | 3 | 端口不可达 |

       | ... | ... | ... |

       TODO:

c语言ping程序中文注释

       编写自己的一个ping程序,可以说是许多人迈出网络编程的第一步吧!!这个ping程序的源代码经过我的修改和调试,基本上可以取代windows中自带的ping程序. 各个模块后都有我的详细注释和修改日志,希望能够对大家的学习有所帮助!!

       /* 本程序的主要源代码来自MSDN网站, 笔者只是做了一些改进和注释! 另外需要注意的是在Build之前,必须加入ws2_.lib库文件,否则会提示"error LNK:"的错误!*/

       Version 1.1 修改记录:

        <1> 解决了socket阻塞的问题,从而能够正确地处理超时的请求!

        <2> 增加了由用户控制发送ICMP包的数目的功能(即命令的第二个参数)

        <3> 增加了对ping结果的统计功能.

       #pragma pack(4)

       #include "winsock2.h"

       #include "stdlib.h"

       #include "stdio.h"

       #define ICMP_ECHO 8

       #define ICMP_ECHOREPLY 0

       #define ICMP_MIN 8 // minimum 8 byte icmp packet (just header)

       /* The IP header */

       typedef struct iphdr {

       unsigned int h_len:4; // length of the header

       unsigned int version:4; // Version of IP

       unsigned char tos; // Type of service

       unsigned short total_len; // total length of the packet

       unsigned short ident; // unique identifier

       unsigned short frag_and_flags; // flags

       unsigned char ttl;

       unsigned char proto; // protocol (TCP, UDP etc)

       unsigned short checksum; // IP checksum

       unsigned int sourceIP;

       unsigned int destIP;

       }IpHeader;

       //

       // ICMP header

       //

       typedef struct icmphdr {

       BYTE i_type;

       BYTE i_code; /* type sub code */

       USHORT i_cksum;

       USHORT i_id;

       USHORT i_seq;

       /* This is not the std header, but we reserve space for time */

       ULONG timestamp;

       }IcmpHeader;

       #define STATUS_FAILED 0xFFFF

       #define DEF_PACKET_SIZE

       #define DEF_PACKET_NUMBER 4 /* 发送数据报的个数 */

       #define MAX_PACKET

       #define xmalloc(s) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,(s))

       #define xfree(p) HeapFree (GetProcessHeap(),0,(p))

       void fill_icmp_data(char *, int);

       USHORT checksum(USHORT *, int);

       int decode_resp(char *,int ,struct sockaddr_in *);

       void Usage(char *progname){

       fprintf(stderr,"Usage:\n");

       fprintf(stderr,"%s [number of packets] [data_size]\n",progname);

       fprintf(stderr,"datasize can be up to 1Kb\n");

       ExitProcess(STATUS_FAILED);

       }

       int main(int argc, char **argv){

       WSADATA wsaData;

       SOCKET sockRaw;

       struct sockaddr_in dest,from;

       struct hostent * hp;

       int bread,datasize,times;

       int fromlen = sizeof(from);

       int timeout = ;

       int statistic = 0; /* 用于统计结果 */

       char *dest_ip;

       char *icmp_data;

       char *recvbuf;

       unsigned int addr=0;

       USHORT seq_no = 0;

       if (WSAStartup(MAKEWORD(2,1),&wsaData) != 0){

       fprintf(stderr,"WSAStartup failed: %d\n",GetLastError());

       ExitProcess(STATUS_FAILED);

       }

       if (argc <2 ) {

       Usage(argv[0]);

       }

       sockRaw = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL, 0,WSA_FLAG_OVERLAPPED);

       //

       //注:为了使用发送接收超时设置(即设置SO_RCVTIMEO, SO_SNDTIMEO),

       // 必须将标志位设为WSA_FLAG_OVERLAPPED !

       //

       if (sockRaw == INVALID_SOCKET) {

       fprintf(stderr,"WSASocket() failed: %d\n",WSAGetLastError());

       ExitProcess(STATUS_FAILED);

       }

       bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,

       sizeof(timeout));

       if(bread == SOCKET_ERROR) {

       fprintf(stderr,"failed to set recv timeout: %d\n",WSAGetLastError());

       ExitProcess(STATUS_FAILED);

       }

       timeout = ;

       bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,

       sizeof(timeout));

       if(bread == SOCKET_ERROR) {

       fprintf(stderr,"failed to set send timeout: %d\n",WSAGetLastError());

       ExitProcess(STATUS_FAILED);

       }

       memset(&dest,0,sizeof(dest));

       hp = gethostbyname(argv[1]);

       if (!hp){

       addr = inet_addr(argv[1]);

       }

       if ((!hp) && (addr == INADDR_NONE) ) {

       fprintf(stderr,"Unable to resolve %s\n",argv[1]);

       ExitProcess(STATUS_FAILED);

       }

       if (hp != NULL)

       memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);

       else

       dest.sin_addr.s_addr = addr;

       if (hp)

       dest.sin_family = hp->h_addrtype;

       else

       dest.sin_family = AF_INET;

       dest_ip = inet_ntoa(dest.sin_addr);

       //

       // atoi函数原型是: int atoi( const char *string );

       // The return value is 0 if the input cannot be converted to an integer !

       //

       if(argc>2)

       {

       times=atoi(argv[2]);

       if(times == 0)

        times=DEF_PACKET_NUMBER;

       }

       else

        times=DEF_PACKET_NUMBER;

       if (argc >3)

       {

       datasize = atoi(argv[3]);

        if (datasize == 0)

        datasize = DEF_PACKET_SIZE;

       if (datasize >) /* 用户给出的数据包大小太大 */

       {

        fprintf(stderr,"WARNING : data_size is too large !\n");

        datasize = DEF_PACKET_SIZE;

       }

       }

       else

        datasize = DEF_PACKET_SIZE;

       datasize += sizeof(IcmpHeader);

       icmp_data = (char*)xmalloc(MAX_PACKET);

       recvbuf = (char*)xmalloc(MAX_PACKET);

       if (!icmp_data) {

       fprintf(stderr,"HeapAlloc failed %d\n",GetLastError());

       ExitProcess(STATUS_FAILED);

       }

       memset(icmp_data,0,MAX_PACKET);

       fill_icmp_data(icmp_data,datasize);

       //

       //显示提示信息

       //

       fprintf(stdout,"\nPinging %s ....\n\n",dest_ip);

       for(int i=0;i{

       int bwrote;

       ((IcmpHeader*)icmp_data)->i_cksum = 0;

       ((IcmpHeader*)icmp_data)->timestamp = GetTickCount();

       ((IcmpHeader*)icmp_data)->i_seq = seq_no++;

       ((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data,datasize);

       bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest));

       if (bwrote == SOCKET_ERROR){

       if (WSAGetLastError() == WSAETIMEDOUT) {

       printf("Request timed out.\n");

       continue;

       }

       fprintf(stderr,"sendto failed: %d\n",WSAGetLastError());

       ExitProcess(STATUS_FAILED);

       }

       if (bwrote < datasize ) {

       fprintf(stdout,"Wrote %d bytes\n",bwrote);

       }

       bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,&fromlen);

       if (bread == SOCKET_ERROR){

       if (WSAGetLastError() == WSAETIMEDOUT) {

       printf("Request timed out.\n");

       continue;

       }

       fprintf(stderr,"recvfrom failed: %d\n",WSAGetLastError());

       ExitProcess(STATUS_FAILED);

       }

       if(!decode_resp(recvbuf,bread,&from))

       statistic++; /* 成功接收的数目++ */

       Sleep();

       }

       /

*

       Display the statistic result

       */

       fprintf(stdout,"\nPing statistics for %s \n",dest_ip);

       fprintf(stdout," Packets: Sent = %d,Received = %d, Lost = %d (%2.0f%% loss)\n",times,

        statistic,(times-statistic),(float)(times-statistic)/times*);

       WSACleanup();

       return 0;

       }

       /*

       The response is an IP packet. We must decode the IP header to locate

       the ICMP data

       */

       int decode_resp(char *buf, int bytes,struct sockaddr_in *from) {

       IpHeader *iphdr;

       IcmpHeader *icmphdr;

       unsigned short iphdrlen;

       iphdr = (IpHeader *)buf;

       iphdrlen = (iphdr->h_len) * 4 ; // number of -bit words *4 = bytes

       if (bytes < iphdrlen + ICMP_MIN) {

       printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));

       }

       icmphdr = (IcmpHeader*)(buf + iphdrlen);

       if (icmphdr->i_type != ICMP_ECHOREPLY) {

       fprintf(stderr,"non-echo type %d recvd\n",icmphdr->i_type);

       return 1;

       }

       if (icmphdr->i_id != (USHORT)GetCurrentProcessId()) {

       fprintf(stderr,"someone else''s packet!\n");

       return 1;

       }

       printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr));

       printf(" icmp_seq = %d. ",icmphdr->i_seq);

       printf(" time: %d ms ",GetTickCount()-icmphdr->timestamp);

       printf("\n");

       return 0;

       }

       USHORT checksum(USHORT *buffer, int size) {

       unsigned long cksum=0;

       while(size >1) {

       cksum+=*buffer++;

       size -=sizeof(USHORT);

       }

       if(size) {

       cksum += *(UCHAR*)buffer;

       }

       cksum = (cksum >> ) + (cksum & 0xffff);

       cksum += (cksum >>);

       return (USHORT)(~cksum);

       }

       /*

       Helper function to fill in various stuff in our ICMP request.

       */

       void fill_icmp_data(char * icmp_data, int datasize){

       IcmpHeader *icmp_hdr;

       char *datapart;

       icmp_hdr = (IcmpHeader*)icmp_data;

       icmp_hdr->i_type = ICMP_ECHO;

       icmp_hdr->i_code = 0;

       icmp_hdr->i_id = (USHORT)GetCurrentProcessId();

       icmp_hdr->i_cksum = 0;

       icmp_hdr->i_seq = 0;

       datapart = icmp_data + sizeof(IcmpHeader);

       //

       // Place some junk in the buffer.

       //

       memset(datapart,''E'', datasize - sizeof(IcmpHeader));

       }

       /

copyright © 2016 powered by 皮皮网   sitemap