어떻게 간단 한 ping 프로그램 을 씁 니까?

오늘 하 드 디스크 를 뒤 졌 을 때 자신 이 오래 전에 쓴 ping 프로그램 을 발견 했다.갑자기 핑 프로그램 을 올 려 놓 고 해 보 는 것 도 예전 에 자신 이 쓴 것 을 다시 보 게 하 는 것 이 라 고 생각 했 습 니 다. 그 때 는 많은 시간 을 들 여 썼 고 감회 가 많 았 습 니 다.
우선, ping 이 사용 하 는 프로 토 콜 은 네트워크 계층 의 ICMP 프로 토 콜 입 니 다. ICMP 메 시 지 를 보 내 거나 받 는 것 은 ICMP 메시지 입 니 다. 최종 형식 은 IP 메시지 로 네트워크 에서 전송 되 는 것 입 니까?다음은 IP 헤드 와 ICMP 프로 토 콜 의 관련 데이터 구 조 를 정의 합 니 다.
IP 헤드
                       IP    
0            8           16    19                   31
+------------+------------+-------------------------+
| ver + hlen |        |                      |
+------------+------------+----+--------------------+
|                      |flag|        (13 )  |
+------------+------------+----+--------------------+
|        |       |                    |
+------------+------------+-------------------------+
|                     IP                         |
+---------------------------------------------------+
|                     IP                        |
+---------------------------------------------------+

데이터 구 조 는 비트 필드 를 사용 하여 프로 토 콜 형식 을 호 환 하고 최종 컴 파일 할 때 1 바이트 로 정렬 합 니 다.
struct IPhead
{
    //     C     ,     version            4bit,   8bit
    uint8_t     version : 4; //IP    
    uint8_t     headLength : 4;//    
    uint8_t     serverce;//    
    uint16_t    totalLength;//   
    uint16_t    flagbit;//  
    uint16_t    flag : 3;//  
    uint16_t    fragmentOffset : 13;//   
    char        timetoLive;//    (  )
    uint8_t     protocol;//    
    uint16_t    headcheckSum;//     
    uint32_t    srcIPadd;// IP
    uint32_t    dstIPadd;//  IP
    //            
};

ICMP 프로 토 콜 관련:
ICMP     
+--------+--------+---------------+
|      |       |            |
+--------+--------+---------------+
|    8   |    0   |           |
+--------+--------+---------------+
|   10   |    0   |          |
+--------+--------+---------------+
|   13   |    0   |          |
+--------+--------+---------------+
|   15   |    0   |     (  )|
+--------+--------+---------------+
|   17   |    0   |         |
+--------+--------+---------------+

ICMP     
+--------+--------+---------------+
|      |      |             |
+--------+--------+---------------+
|    0   |    0   |           |
+--------+--------+---------------+
|    9   |    0   |          |
+--------+--------+---------------+
|   14   |    0   |          |
+--------+--------+---------------+
|   16   |    0   |     (  )|
+--------+--------+---------------+
|   18   |    0   |         |
+--------+--------+---------------+

ICMP    /      
0        8       16                32
+--------+--------+-----------------+
|       |       |            |
+--------+--------+-----------------+
|               |              |
+-----------------+-----------------+
|                               |
+-----------------+-----------------+

ICMP         /         。                    ,ICMP     ,    ,    。         ,     

ICMP 관련 데이터 구조
//ICMP 
struct ICMPhead
{
    uint8_t type;//  
    uint8_t code;//  
    uint16_t checkSum;//   
    uint16_t ident;//     
    uint16_t seqNum;//  
};
//ICMP      (   )
struct ICMPrecvReq
{
    ICMPhead icmphead;//  
    uint32_t timeStamp;//   
    char     data[32];//  
};
//ICMP    (   )
struct ICMPansReply
{
    IPhead iphead;//IP 
    ICMPrecvReq icmpanswer;//ICMP  
    char data[1024];//            
};

실현 해 야 할 함수 도 많 지 않 고 6 개 만 있 으 면 기본 적 인 ping 프로그램 을 만족 시 킬 수 있 습 니 다.
//     ,  1:      ,        。  2:       。     
uint16_t getCheckSum(void* protocol,char* type);
//ping  ,  1:   IP  。        .  2:   -t,    
bool ping(const char* dstIPaddr,const char* param='\0');
//  ICMP      ,  1:   .  2:    .  3: num   
bool sendICMPReq(SOCKET &mysocket, sockaddr_in &dstAddr, unsigned int num);
//  ICMP    ,  1:   .  2:     .  3:  
uint32_t readICMPanswer(SOCKET &mysocket, sockaddr_in &srcAddr, char &TTL);
//  socket         
int waitForSocket(SOCKET &mysocket);
//    ping  ,  1:   .  2:   .  3:    。  4  ping    
void doPing(SOCKET &mysocket,sockaddr_in &srcAddr,sockaddr_in &dstAddr,int num);
//        ,       NULL  ,         param ,    
char* isParamEmpty(char *buffer, char *param);
//        
void get_ctrl_stop(int signal);

이 프로그램 은 windows 플랫폼 에서 실 현 된 액 입 니 다. winsock 을 사용 하려 면 winsock 을 사용 할 수 있 는 lib 의 사용 을 위 한 사전 처리 명령 #pragma comment(lib,"ws2_32.lib") 을 추가 해 야 합 니 다. 다음은 주제 입 니 다. ICMP 메 시 지 는 TCP 나 UDP 의 패 키 징 을 거치 지 않 고 sock 계층 의 API 는 사용자 (프로그래머) 를 위 한 것 임 을 알 고 있 습 니 다. 일반적인 sock 은 사용자 가 읽 을 때 입 니 다.시스템 은 IP 헤드 의 정 보 를 제거 해 줍 니 다. 그러면 전송 층 이하 의 프로 토 콜 에 관 한 정 보 를 얻 을 수 없습니다. 따라서 우 리 는 일반적인 sock 을 사용 하여 ping 프로그램 을 작성 할 수 없습니다. 원본 소켓 SOCK_RAW 을 사용 하려 면 원본 소켓 을 사용 하면 sock 이 데 이 터 를 요청 하고 싶 을 때 시스템 은 sock 에 대해 너무 많은 처 리 를 하지 않 습 니 다.우 리 는 최소한 데이터 링크 층 의 데 이 터 를 얻 을 수 있 기 때문에 네트워크 층 의 IP 헤더 의 데이터 도 얻 을 수 있다.
전체 ping 프로그램의 기본 절 차 는 다음 과 같 습 니 다. RAW 소켓 을 만 듭 니 다. - > ping 대기 주소 와 매개 변수 수신 - > 추출 매개 변수 - > 매개 변수 에 따라 해당 하 는 ping (일반 ping 4 회 또는 무한 순환 ping) - > ping 대기 주소 에 ICMP 응답 요청 메 시 지 를 보 냅 니 다. - > 답장 메시지 수신 - > 해당 정 보 를 출력 합 니 다.그 중에서 무한 ping 에서 캡 처 Ctrl+C 종료 명령 을 처리 하 는 신호 함수 도 있 습 니 다. 그것 은 큰 절차 에 영향 을 주지 않 고 절차 에서 말 하지 않 습 니 다.
8 개의 API 만 있 으 면 ping 프로그램 을 실현 할 수 있 기 때문에 저 는 하나씩 API 를 풀 려 고 합 니 다. 구체 적 인 프로젝트 코드 는 github 에 두 고 필요 한 사람 이 직접 가 져 가세 요.
파라미터 검사 및 추출
char *isParamEmpty(char *buffer, char *param)
{
    char *temp = NULL;
    temp = buffer;
    while (*temp != '\0')
    {
        if (*temp == ' ')
        {
            *temp = '\0';
            param = ++temp;
        }
        temp++;
    }
    return param;
}

계산 검사 합
uint16_t getCheckSum(void * protocol, char * type)
{
    uint32_t checkSum = 0;
    uint16_t* word = (uint16_t*)protocol;
    uint32_t size = 0;
    if (type == "ICMP") {//        
        size = (sizeof(ICMPrecvReq));
    }
    while (size >1)// 32           16             ,           16      
    {
        checkSum += *word++;
        size -=2;
    }
    if (size == 1) 
    {
        checkSum += *(uint8_t*)word;
    }
    //         ,                    ,          
    //          
    while (checkSum >> 16) checkSum = (checkSum >> 16) + (checkSum & 0xffff);
    //  
    return (~checkSum);
}

ping 프로 세 스 선택
bool ping(const char * dstIPaddr,const char* param)
{
    SOCKET      rawSocket;//socket
    sockaddr_in srcAddr;//socket   
    sockaddr_in dstAddr;//socket    
    int         Ret;//     
    char        TTL = '0';//  

    //       
    //TCP/IP   ,RAW  ,ICMP  
    //RAW           ,               ,         IP          。
    rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    //    IP  
    dstAddr.sin_addr.S_un.S_addr = inet_addr(dstIPaddr);
    //  
    dstAddr.sin_port = htons(0);
    //   
    dstAddr.sin_family = AF_INET;

    //    
    std::cout << "   Ping " << inet_ntoa(dstAddr.sin_addr) << "    " << sizeof(ICMPrecvReq::data) << "      :" << std::endl;
    if(!strcmp(param,"-t"))
    {
        uint32_t i=0;

        while (keepping)
        {
            //    ping  
            doPing(rawSocket, srcAddr, dstAddr,i);
            //  1ms
            Sleep(1000);
            i++;
        }
        keepping = 1;
    }
    else if(!strcmp(param,"no_param"))
    {   //  4 ping
        for (int i = 0; i < 4; i++)
        {
            doPing(rawSocket, srcAddr, dstAddr, i);
            Sleep(1000);
         }
    }

    Ret = closesocket(rawSocket);
    if (Ret == SOCKET_ERROR)
    {
        std::cerr << "socket       :" << WSAGetLastError() << std::endl;
    }

    return true;
}

ping 실행
void doPing(SOCKET & mysocket, sockaddr_in & srcAddr, sockaddr_in & dstAddr, int num)
{
    uint32_t    timeSent;//      
    uint32_t    timeElapsed;//    
    char        TTL;//  
    //  ICMP    
    sendICMPReq(mysocket, dstAddr, num);
    //    
    int Ret = waitForSocket(mysocket);
    if (Ret == SOCKET_ERROR)
    {
        std::cerr << "socket    :" << WSAGetLastError() << std::endl;
        return;
    }
    if (!Ret)
    {
        std::cout << "    :" << std::endl;
        return;
    }
    timeSent = readICMPanswer(mysocket, srcAddr, TTL);
    if (timeSent != -1) {
        timeElapsed = GetTickCount() - timeSent;
        //    ,  TTL  ASCII ,     
        std::cout << "   " << inet_ntoa(srcAddr.sin_addr) << "    :   = " << sizeof(ICMPrecvReq::data) << "   = " << timeElapsed << "ms TTL= " << fabs((int)TTL) << std::endl;
    }
    else {
        std::cout << "    " << std::endl;
    }
}

ICMP 메시지 보 내기
bool sendICMPReq(SOCKET &mysocket,sockaddr_in &dstAddr,unsigned int num)
{
    //  ICMP      
    //      
    ICMPrecvReq myIcmp;//ICMP    
    myIcmp.icmphead.type = 8;
    myIcmp.icmphead.code = 0;
    //        0
    myIcmp.icmphead.checkSum = 0;
    //        
    myIcmp.icmphead.ident = (uint16_t)GetCurrentProcessId();
    //       0
    myIcmp.icmphead.seqNum = ++num;
    //      
    myIcmp.timeStamp = GetTickCount();
    //         
    myIcmp.icmphead.checkSum = getCheckSum((void*)&myIcmp, "ICMP");
    //    
    int Ret = sendto(mysocket, (char*)&myIcmp, sizeof(ICMPrecvReq), 0, (sockaddr*)&dstAddr, sizeof(sockaddr_in));

    if (Ret == SOCKET_ERROR)
    {
        std::cerr << "socket     :" <std::endl;
        return false;
    }
    return true;
}

ICMP 메시지 의 답장 을 기다 리 고 있 습 니 다.
int waitForSocket(SOCKET &mysocket)
{
    //5S           
    timeval timeOut;
    fd_set  readfd;
    readfd.fd_count = 1;
    readfd.fd_array[0] = mysocket;
    timeOut.tv_sec = 5;
    timeOut.tv_usec = 0;
    return (select(1, &readfd, NULL, NULL, &timeOut));
}

답장 한 ICMP 메시지 읽 기
uint32_t readICMPanswer(SOCKET &mysocket, sockaddr_in &srcAddr, char &TTL)
{
    ICMPansReply icmpReply;//      
    int addrLen = sizeof(sockaddr_in);
    //    
    int Ret = recvfrom(mysocket, (char*)&icmpReply, sizeof(ICMPansReply), 0, (sockaddr*)&srcAddr, &addrLen);
    if (Ret == SOCKET_ERROR)
    {
        std::cerr << "socket     :" << WSAGetLastError() << std::endl;
        return false;
    }
    //           
    uint16_t checkSum = icmpReply.icmpanswer.icmphead.checkSum;
    //               0
    icmpReply.icmpanswer.icmphead.checkSum = 0;
    //    
    if (checkSum== getCheckSum((void*)&icmpReply.icmpanswer, "ICMP")) {
        //  TTL 
        TTL = icmpReply.iphead.timetoLive;
        return icmpReply.icmpanswer.timeStamp;
    }

    return -1;
}

처리 종료 신호 함수
static volatile int keepping = 1;
//        ,      ping    
void get_ctrl_stop(int signal)
{
    if (signal == SIGINT)
    {
        keepping = 0;
    }
}

모든 코드 를 원한 다 면 아래 링크 에서 ping 프로그램 전체 프로젝트 를 가 져 올 수 있 습 니 다.

좋은 웹페이지 즐겨찾기