Windows網路程式設計(七):原始套接字開發
阿新 • • 發佈:2018-11-27
在呼叫socket()函式時,如果將第二個引數填為SOCK_RAW,代表建立的是原始套接字型別,第三個引數可以選擇IPPROTO_ICMP、IPPROTO_TCP、IPPROTO和IPPROTO_RAW。
#include <winsock2.h>
#pragma comment (lib, "ws2_32")
struct icmp_header
{
unsigned char icmp_type; // 訊息型別
unsigned char icmp_code; // 程式碼
unsigned short icmp_checksum; // 校驗和
unsigned short icmp_id; // 用來惟一標識此請求的ID號,通常設定為程序ID
unsigned short icmp_sequence; // 序列號
unsigned long icmp_timestamp; // 時間戳
};
#define ICMP_HEADER_SIZE sizeof(icmp_header)
#define ICMP_ECHO_REQUEST 0x08
#define ICMP_ECHO_REPLY 0x00
// 計算校驗和
unsigned short chsum(struct icmp_header *picmp, int len)
{
long sum = 0;
unsigned short *pusicmp = (unsigned short *)picmp;
while (len > 1)
{
sum += *(pusicmp++);
if (sum & 0x80000000)
{
sum = (sum & 0xffff) + (sum >> 16);
}
len -= 2;
}
if (len)
{
sum += (unsigned short)*(unsigned char *)pusicmp;
}
while (sum >> 16)
{
sum = (sum & 0xffff) + (sum >> 16);
}
return (unsigned short)~sum;
}
BOOL MyPing(char *szDestIp)
{
BOOL bRet = TRUE;
WSADATA wsaData;
int nTimeOut = 1000;
char szBuff[ICMP_HEADER_SIZE + 32] = { 0 };
icmp_header *pIcmp = (icmp_header *)szBuff;
char icmp_data[32] = { 0 };
WSAStartup(MAKEWORD(2, 2), &wsaData);
// 建立原始套接字
SOCKET s = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
// 設定接收超時
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char const*)&nTimeOut, sizeof(nTimeOut));
// 設定目的地址
sockaddr_in dest_addr;
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.S_un.S_addr = inet_addr(szDestIp);
dest_addr.sin_port = htons(0);
// 構造ICMP封包
pIcmp->icmp_type = ICMP_ECHO_REQUEST;
pIcmp->icmp_code = 0;
pIcmp->icmp_id = (USHORT)::GetCurrentProcessId();
pIcmp->icmp_sequence = 0;
pIcmp->icmp_timestamp = 0;
pIcmp->icmp_checksum = 0;
// 拷貝資料
// 這裡的資料可以是任意的
// 這裡使用abc是為了和系統提供的看起來一樣
memcpy((szBuff + ICMP_HEADER_SIZE), "abcdefghijklmnopqrstuvwabcdefghi", 32);
// 計算校驗和
pIcmp->icmp_checksum = chsum((struct icmp_header *)szBuff, sizeof(szBuff));
sockaddr_in from_addr;
char szRecvBuff[1024];
int nLen = sizeof(from_addr);
sendto(s, szBuff, sizeof(szBuff), 0, (SOCKADDR *)&dest_addr, sizeof(SOCKADDR));
recvfrom(s, szRecvBuff, MAXBYTE, 0, (SOCKADDR *)&from_addr, &nLen);
// 判斷接收到的是否是自己請求的地址
if (lstrcmp(inet_ntoa(from_addr.sin_addr), szDestIp))
{
bRet = FALSE;
}
else
{
struct icmp_header *pIcmp1 = (icmp_header *)(szRecvBuff + 20);
printf("%s\r\n", inet_ntoa(from_addr.sin_addr));
}
return bRet;
}
int main(int argc, char **argv)
{
int i = 0;
while (i < 1000)
{
MyPing("8.8.4.4");
Sleep(500);
i++;
}
return 0;
}