日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C/C++:Winsock网络编程—ping命令的简单实现

發布時間:2025/3/15 c/c++ 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C/C++:Winsock网络编程—ping命令的简单实现 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Winsock網絡編程—ping命令的簡單實現

前言

先聲明 博主實現的是Windows平臺的ping命令的簡單實現,沒有做域名解析,只能直接ping ip。我們要實現ping 肯定得先知道ping的實現原理,ping 發送的 ICMP報文。實際上的落腳點 就是對 ICMP協議和IP協議 結構的學習 以及 如何使用Winsock API 來實現ICMP報文的組包和解包。需要使用wireshark 抓包軟件 配合學習,這樣可以驗證你分析的對不對。

網絡協議基礎知識

ip協議結構圖:

  • ping 中有顯示 TTL 值,這個就是從ip頭部中取得。
  • ip頭部的長度是從 IHL 中的值。
  • icmp協議結構圖:

    類型和代碼字段 所有情況如下,我們主要用的是 請求回顯 和 回顯應答

    字節序問題

    只有當 數據類型長度超過1個字節是 才會出現字節序問題。

    網絡傳輸的字節序和本地存儲的字節序 有可能是一樣 也有可能不一樣。

    網絡字節序 是大端對齊模式(低地址 放高字節,高地址 放低字節)。

    本地字節序 就要分CPU架構了,一般小型計算機 都是小端對齊模式(低地址 放低字節,高地址 放高字節)。

    Winsock api 函數

    完成 icmp報文的發送和接受 使用的api 函數有:

  • setsockopt
  • https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-setsockopt

    int WSAAPI setsockopt(
    SOCKET s,
    int level, // 選項級別
    int optname, // 選項名
    const char *optval, // 選項值
    int optlen // 選項的長度
    );

  • sendto
  • https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-sendto

    int WSAAPI sendto(
    SOCKET s,
    const char *buf,
    int len,
    int flags, // 調用模式flag
    const sockaddr *to,
    int tolen
    );

  • recvfrom
  • https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-recvfrom

    int recvfrom(
    SOCKET s,
    char *buf,
    int len,
    int flags,
    sockaddr *from, // 寫出參數,為源地址
    int *fromlen
    );

  • u_short ntohs(u_short hostshort); 網絡字節序 轉本地字節序
  • 實現效果

    先看下效果 再說

    和 wireshark 抓包數據對比

    實現代碼

    一次ICMP 報文請求的核心代碼

    int ping(char *szDestIp) {//printf("destIp = %s\n",szDestIp);int bRet = 1;WSADATA wsaData;int nTimeOut = 1000;//1s 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_checksum = 0;// 填充數據,可以任意 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);int ret,flag = 0; DWORD start = GetTickCount();ret = sendto(s, szBuff, sizeof(szBuff), 0, (SOCKADDR *)&dest_addr, sizeof(SOCKADDR));//printf("ret = %d ,errorCode:%d\n",ret ,WSAGetLastError() ); int i = 0; //這里一定要用while循環,因為recvfrom 會接受到很多報文,包括 發送出去的報文也會被收到! 不信你可以用 wireshark 抓包查看,這個問題糾結來了一晚上 才猜想出來! while(1){if(i++ > 5){// icmp報文 如果到不了目標主機,是不會返回報文,多嘗試幾次接受數據,如果都沒收到 即請求失敗 flag = 1;break;}memset(szRecvBuff,0,1024);//printf("errorCode1:%d\n",WSAGetLastError() ); int ret = recvfrom(s, szRecvBuff, MAXBYTE, 0, (SOCKADDR *)&from_addr, &nLen);//printf("errorCode2:%d\n",WSAGetLastError() ); //printf("ret=%d,%s\n",ret,inet_ntoa(from_addr.sin_addr)) ; //接受到 目標ip的 報文 if( strcmp(inet_ntoa(from_addr.sin_addr),szDestIp) == 0) {respNum++;break;}} DWORD end = GetTickCount();DWORD time = end -start; if(flag){printf("請求超時。\n");return bRet;}sumTime += time;if( minTime > time){minTime = time;}if( maxTime < time){maxTime = time;}// Windows的原始套接字 開發,系統沒有去掉IP協議頭,需要程序自己處理。// ip頭部的第一個字節(只有1個字節不涉及大小端問題),前4位 表示 ip協議版本號,后4位 表示IP 頭部長度(單位為4字節)char ipInfo = szRecvBuff[0];// ipv4頭部的第9個字節為TTL的值char ttl = szRecvBuff[8];//printf("ipInfo = %x\n",ipInfo);int ipVer = ipInfo >> 4;int ipHeadLen = ((char)( ipInfo << 4) >> 4) * 4;if( ipVer == 4) {//ipv4 //printf("ipv4 len = %d\n",ipHeadLen);// 跨過ip協議頭,得到ICMP協議頭的位置,不過是網絡字節序。 // 網絡字節序 是大端模式 低地址 高位字節 高地址 低位字節。-> 轉換為 本地字節序 小端模式 高地址高字節 低地址低字節 icmp_header* icmp_rep = (icmp_header*)(szRecvBuff + ipHeadLen);//由于校驗和是 2個字節 涉及大小端問題,需要轉換字節序 unsigned short checksum_host = ntohs(icmp_rep->icmp_checksum);// 轉主機字節序 和wireshark 抓取的報文做比較 //printf("type = %d ,checksum_host = %x\n",icmp_rep,checksum_host);if(icmp_rep->icmp_type == 0){ //回顯應答報文 //來自 61.135.169.121 的回復: 字節=32 時間=1ms TTL=57printf("來自 %s 的回復:字節=32 時間=%2dms TTL=%d checksum=0x%x \n", szDestIp, time, ttl, checksum_host);} else{bRet = 0;printf("請求超時。type = %d\n",icmp_rep->icmp_type);} }else{// ipv6 icmpv6 和 icmpv4 不一樣,要做對應的處理 //printf("ipv6 len = %d\n",ipLen); } return bRet; }

    代碼下載

    就一個C文件 可以用IDE打開運行,需要鏈接 ws2_32.lib 庫。博主用的devc++,完整工程代碼下載,或者Github最新代碼。

    總結

    以上是生活随笔為你收集整理的C/C++:Winsock网络编程—ping命令的简单实现的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。