一.套接字
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在这里