用C 语言实现 Ping

本文详细介绍了如何使用C语言通过SOCK_RAW和IPPROTO_ICMP套接字类型来发送ICMP请求,并接收回显应答报文,实现类似Ping的功能。通过理解这个DEMO,读者可以深入理解网络通信的基本原理。

一.套接字

 int socket(int domain,int type,int protocol);
 参数domain确定通信特性,它有4个域分别为
  AF_INET     IPV4英特网域
  AF_INET6    IPV6因特网域
  AF_UNIX     UNIX 域
  AF_UPSPEC   未指定

 参数 type 确定套接字类型,进一步确定通信特性
  SOCK_DGRAW  固定长度、无连接、不可靠的包文传递
  SOCK_RAW    IP 协议数据接口
  SOCK_SEQPACKET 固定长度的、有序的、可靠的、面向连接的报文传递
  SOCK_STREAM 有序的、可靠、双向的、面向连接的字节流

 参数 protocol 通常是0,表示为给定的域和套接字选择默认
 协议。AF_INT域SOCK_STREAM默认为传输控制协议 TCP,
 AF_INT域套接字类型 SOCK_DGRAM 的默认协议是 UDP。
 下面为英特网套接字定义的协议:
 IPPROTO_IP,     IPPROTO_IPV6,   IPPROTO_ICMP
 IPPROTO_RAW,    IPPROTO_TCP,    IPPROTO_UDP

 返回值为套接字描述符,本质为一个文化描述符

SOCK_RAW和IPPROTO_ICMP

SOCK_RAW套接字提供一个数据报接口,用于直接访问下面的网络层
因为其绕过了传输协议(如 TCP和 UDP),所以我们必须自己构造
协议头部。注意当我们创建这个套接字的时候我们必须有超级用
户特权。因此实现 Ping 我们先来定义 ICMP 头部

 struct ICMPHEAD{
       u_int8_t type;
       u_int8_t code;
       u_int16_t checkSum;
       u_int16_t id;
       u_int16_t seq;
};
我们实现 Ping 必须发送回显请求数据包,其类型代码为8,
代码报文为0.回显请求数据包类型为0,代码报文为0.id 给数
据包一个标示用于判断是否为我们所发送的数据包。checkSum
为检验和字段,包括数据在内的整个 ICMP 报文检验和。

ushort GenerationChecksum(ushort*pBuf,int iSize){
unsigned long cksum = 0;
while(iSize > 1){
    cksum+=*pBuf++;
    iSize-=sizeof(ushort);
}
if(iSize) cksum+=*pBuf++;
cksum = (cksum>>16)+(cksum&0xffff);
cksum += (cksum>>16);
return (ushort)(~cksum);
}
定义完我们的数据包之后,剩下就是定义一个目的地址。
struct sockaddr_in {
__uint8_t   sin_len;
sa_family_t sin_family;
in_port_t   sin_port;
struct  in_addr sin_addr;
char        sin_zero[8];
};
sin_family为地址族
sin_addr为目的地址
sin_port为端口号
struct sockaddr_in destSocketAddr;
destSocketAddr.sin_family = AF_INET;
destSocketAddr.sin_addr.s_addr = ulDestIP;
destSocketAddr.sin_port = htons(0);
memset(destSocketAddr.sin_zero, 0, sizeof(destSocketAddr.sin_zero));

发送ICMP

ssize_t sendto(int, const void *, size_t,int, const struct sockaddr *, socklen_t)
第一个参数为套接字描述符。
第二个参数为 ICMP 数据包
第三个参数为 ICMP 的大小
第四个参数为目的地址,为使不同的地址格式传入这个函数,因此
必须强转为 sockaddr。
第五个参数为目的地址大小
接受回显应答报文
ssize_t recvfrom(int, void *, size_t, int,struct sockaddr * __restrict,socklen_t * __restrict)
第一个参数为套接字描述符
第二个参数接受数据缓存区的首地址
第三个参数为数据缓存区大小
第四个参数为接受发送数据的地址信息
第五个参数为第四个参数的大小

接受的数据为 IP 数据报,因此我们可以定义一个结构体来方便
我们操作 IP 数据报
ip 头结构体
typedef struct tag_iphdr        
{
    u_int8_t   iphVerLen;//注意这个包括版本和首部长度
    u_int8_t   ipTOS;
    ushort  ipLength;
    ushort  ipID;
    ushort  ipFlags;
    u_int8_t   ipTTL;
    u_int8_t   ipProtacol;
    ushort  ipChecksum;
    int   ipSource;
    int   ipDestination;
} IPHDR;

利用 ip 头部中的首部长度信息获得我们需要的回显应答数据包。解析回显应答数据包我们的 Ping就完成了。

DEMO在这里

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值