심장 박동

심박수 패키지는 클라이언트와 서버 간에 상대방에게 자신의 상태를 정시에 알리는 자신이 정의한 명령어로 일정한 시간 간격으로 발송되며 심박수와 유사하기 때문에 심박수 패키지라고 한다.
상대방(설비, 프로세스 또는 기타 네트워크)이 정상적으로 작동하는지 판단하기 위해 정해진 시간에 간단한 통신 패키지를 발송하고 지정된 시간 내에 상대방의 응답을 받지 못하면 상대방이 이미 오프라인 상태임을 판단한다.TCP의 비정상적인 분리를 감지하는 데 사용됩니다.기본적인 원인은 서버 측이 클라이언트의 온라인 여부를 효과적으로 판단하지 못하기 때문이다. 즉, 서버는 클라이언트가 장시간 비어 있는지, 이미 오프라인 상태인지 구분할 수 없다.이른바 심장 박동 패키지란 클라이언트가 시간에 맞춰 간단한 메시지를 서버에 보내서 내가 아직 있다는 것을 알려주는 것이다.코드는 몇 분 간격으로 고정된 정보를 서버에 보내는 것이다. 서버가 받은 후에 고정된 정보를 회신하면 서버가 몇 분 안에 클라이언트의 정보를 받지 못하면 클라이언트가 끊어진다.
예를 들어 어떤 통신 소프트웨어는 장시간 사용하지 않고 그 상태가 온라인인지 오프라인인지 알고 싶으면 심장 박동 패키지가 필요하고 정시에 패키지를 보내야 한다.하청업체: 고객일 수도 있고 서비스일 수도 있고 어느 쪽이 편리하고 합리적인지 보면 보통 클라이언트입니다.서버도 정시에 심장이 두근거릴 수 있다.일반적으로 효율적인 고려에서 보면 클라이언트가 주동적으로 서버 측에 하청을 하는 것이지 서버가 클라이언트에게 보내는 것이 아니다.클라이언트가 일정 시간마다 가방을 보내고 TCP를 사용하는,send로 보내고 UDP를 사용하는,sendto로 보내면 서버가 받은 후에 현재 클라이언트가'살아있다'는 상태를 알 수 있다. 그렇지 않으면 일정 시간 동안 이런 가방을 받지 못하면 서버는 클라이언트 측이 이미 끊어졌다고 생각하고 해당하는 클라이언트 분리 논리 처리를 한다.
 
하트비트가 하트비트라고 하는 이유는 하트비트처럼 고정된 시간에 한 번씩 보내서 서버에 이 클라이언트가 살아있다는 것을 알려주기 때문이다.사실 이것은 긴 연결을 유지하기 위해서이다. 이 가방의 내용은 특별한 규정이 없지만 일반적으로 매우 작은 가방이나 가방 머리만 포함된 빈 가방이다.
TCP의 메커니즘 안에서 그 자체는 심장박동이 있는 메커니즘이 존재한다. 즉, TCP의 옵션: SO_KEEPALIVE.시스템은 기본적으로 설정된 2시간의 심장 박동 주파수입니다.그러나 기계의 단전, 네트워크 뽑기, 방화벽 등 단선을 검사할 수 없다.그리고 논리층 처리 단선도 그렇게 잘 처리되지 않을 수도 있다.일반적으로 단지 생존을 보장하는 데 쓰인다면 그래도 된다.
심장 박동 패키지는 일반적으로 논리층에서 빈 에코 패키지를 보내서 이루어진다.다음 타이머는 일정한 시간 간격으로 클라이언트에게 빈 패키지를 보내고 클라이언트가 똑같은 빈 패키지를 피드백해서 돌아온다. 서버가 일정 시간 안에 클라이언트가 보낸 피드백 패키지를 받지 못하면 오프라인이라고 인정할 수밖에 없다.
사실, 오프라인 판정을 하려면send나recv만 필요하고, 결과가 0이면 오프라인입니다.그러나 긴 연결에서 오랫동안 데이터 왕래가 없을 수도 있다.이론적으로 말하자면 이 연결은 줄곧 연결을 유지하지만 실제 상황에서 중간 노드에 어떤 고장이 발생하면 알 수 없다.더 심각한 것은 어떤 노드(방화벽)는 일정 시간 동안 데이터 상호작용이 없는 연결을 자동으로 끊어버리는 것이다.이럴 때 우리의 심장 박동수가 필요합니다. 상시 연결을 유지하고 활력을 유지하는 데 사용됩니다.
끊어진 것을 알게 된 후에 서버 논리는 몇 가지 일을 해야 할 수도 있다. 예를 들어 끊어진 데이터의 정리, 다시 연결이야... 물론 이것은 논리층이 수요에 따라 해야 한다.
전반적으로 말하면 심장 박동백은 주로 긴 연결의 보존과 단선 처리에 쓰인다.일반적인 응용에서 판정 시간은 30-40초가 비교적 좋다.만약 정말 요구가 높다면, 그것은 6-9초이다.
 
심장 박동수의 실현
본인의 심장 박동 패키지 처리는 이렇게 설계되었습니다. 프로그램을 시작한 후 즉시 심장 박동 라인을 열어 고객의 연결을 처리하는 데 사용됩니다.이 라인은 모든 클라이언트의 연결을 처리하는 데 사용되며, 그 중 한 고객이 보낸 요청이 20초에 이르지 않았을 때, 즉 오프라인 상태라고 여긴다.고객이 연결할 때 데이터를 한 번 보낸 후 바로 종료합니다.정시 처리를 확보하기 위해 대기 타이머와 이벤트 메커니즘을 가동했다.1. 심박수 라인은 이렇게 시작합니다: HANDLE hHeatBeat=CreateThread(NULL, 0, CHeartBeat::WaitProc, NULL, 0, NULL).CloseHandle(hHeatBeat); 2. 심장박동수의 주선
C/C++ code

    
    
    
    
DWORD CALLBACK CHeartBeat::WaitProc(LPVOID lpVoid) { HANDLE hTimer; BOOL bSuccess; __int64 qwDueTime; LARGE_INTEGER liDueTime; char szError[ 255 ]; DWORD dwResult; HANDLE * phTimers = NULL, * phEvents = NULL; while ( true ) { if (hbList.size() > 0 ) { // manage events phEvents = (HANDLE * )LocalAlloc(LPTR | LMEM_ZEROINIT, sizeof (HANDLE) * hbList.size()); for (vector < CHeartBeat *> ::size_type i = 0 ;i < hbList.size(); ++ i) { if (hbList[i] -> m_hEvent != NULL) phEvents[i] = hbList[i] -> m_hEvent; } // wait for one of event become signal, if there are no events become sigal,after 500 millisecond, it return dwResult = WaitForMultipleObjects((DWORD)hbList.size(),phEvents,FALSE, 500 ); if (dwResult >= WAIT_OBJECT_0 && dwResult <= WAIT_OBJECT_0 + hbList.size() - 1 ) { vector < CHeartBeat *> ::iterator iter = hbList.begin(); for (;iter != hbList.end(); ++ iter) { if (( * iter) -> m_hEvent == phEvents[dwResult - WAIT_OBJECT_0]) { if (( * iter) -> m_hTimer != NULL) { // when follow MCT health messages was recevied CancelWaitableTimer(( * iter) -> m_hTimer); bSuccess = SetWaitableTimer( ( * iter) -> m_hTimer, // Handle to the timer object & liDueTime, // When timer will become signaled 1200000 , // Periodic timer interval of 2 seconds NULL, NULL, FALSE ); // Do not restore a suspended system if ( ! bSuccess ) { sprintf( szError, " SetWaitableTimer failed with Error \ % d.
" , GetLastError() ); cout << szError << endl; CloseHandle( hTimer ); return false ; } } else { if ( hTimer = CreateWaitableTimer( NULL, // Default security attributes FALSE, // Create auto-reset timer NULL ) ) // Name of waitable timer { // Create an integer that will be used to signal the timer // 120 seconds from now. qwDueTime = - 20 * _SECOND; // Copy the relative time into a LARGE_INTEGER. liDueTime.LowPart = (DWORD) ( qwDueTime & 0xFFFFFFFF ); liDueTime.HighPart = (LONG) ( qwDueTime >> 32 ); bSuccess = SetWaitableTimer( hTimer, // Handle to the timer object & liDueTime, // When timer will become signaled 1200000 , // Periodic timer interval of 120 seconds NULL, NULL, FALSE ); // Do not restore a suspended system if ( ! bSuccess ) { sprintf( szError, " SetWaitableTimer failed with Error \ % d.
" , GetLastError() ); cout << szError << endl; CloseHandle( hTimer ); return false ; } ( * iter) -> m_hTimer = hTimer; } else { sprintf( szError, " CreateWaitableTimer failed with Error \ % d.
" , GetLastError() ); cout << szError << endl; return false ; } } ResetEvent(( * iter) -> m_hEvent); } } } else if (dwResult == WAIT_FAILED) { OutputDebugString(_T( " wait event singal failed
" )); } LocalFree((HLOCAL)phEvents); // manage waitable timers phTimers = (HANDLE * )LocalAlloc(LPTR | LMEM_ZEROINIT, sizeof (HANDLE) * (hbList.size())); for (vector < CHeartBeat *> ::size_type i = 0 ;i < hbList.size(); ++ i) { if (hbList[i] -> m_hTimer != NULL) phTimers[i] = hbList[i] -> m_hTimer; } // wait for one of event become signal, if there are no events become sigal,after 500 millisecond, it return DWORD dwResult = WaitForMultipleObjects((DWORD)(hbList.size()),phTimers,FALSE, 500 ); if (dwResult >= WAIT_OBJECT_0 && dwResult <= WAIT_OBJECT_0 + hbList.size() - 1 ) { // a client is power off vector < CHeartBeat *> ::iterator iter = hbList.begin(); int i = 0 ; for (;iter != hbList.end(); ++ iter) { i ++ ; if (( * iter) -> m_hTimer == phTimers[dwResult - WAIT_OBJECT_0]) { CHeartBeat * phb = ( * iter); char mbProjNum[ 1024 ]; strcpy(mbProjNum,phb -> m_strProj); printf( "
a power off event occuered on ProjNum:%s
" ,mbProjNum); // before remove element, clear event and timer SetEvent(( * iter) -> m_hEvent); WaitForSingleObject(( * iter) -> m_hEvent,INFINITE); CloseHandle(( * iter) -> m_hEvent); CloseHandle(( * iter) -> m_hTimer); // remove element hbList.erase(iter); break ; } } } LocalFree((HLOCAL)phTimers); } // #region 1 // heart beat thread will exist if (WaitForSingleObject(m_hExitHandle, 500 ) == WAIT_OBJECT_0) { if (hbList.size() > 0 ) { vector < CHeartBeat *> ::iterator iter = hbList.begin(); for (;iter != hbList.end(); ++ iter) { CHeartBeat * pHB = (CHeartBeat * )( * iter); if (pHB -> m_hEvent != NULL) { CloseHandle(pHB -> m_hEvent); SetEvent(pHB -> m_hEvent); WaitForSingleObject(pHB -> m_hEvent,INFINITE); } if (pHB -> m_hTimer != NULL) { CancelWaitableTimer(pHB -> m_hTimer); CloseHandle(pHB -> m_hTimer); } SAFE_DELETE(pHB); } hbList.clear(); break ; } } // #endregion } return true ; }

3. 클라이언트 연결이 있을 때 심장 박동 라인에 이벤트 신호를 보내고 클라이언트가 연결할 때마다 클라이언트는 심장 박동 패키지 대상을 만들어서 목록에 넣고, 클라이언트가 뒷순서로 연결할 때 클라이언트는 지정한 이벤트 신호만 설정한다
C/C++ code

    
    
    
    
bool bExist = false ; CHeartBeat * pHeartBeat = NULL; for (vector < CHeartBeat *> ::iterator iter = hbList.begin(); iter != hbList.end(); ++ iter) { if (strcmp(( * iter) -> GetProjNum(),pMessageInfo -> msg_base_info.szPlanNumber) == 0 ) { // IM has received health message previous for special plannumber bExist = true ; pHeartBeat =* iter; break ; } } if (bExist) { // notify CHeartBeat to reset waitable timer if (pHeartBeat != NULL) SetEvent(pHeartBeat -> GetEventHandle()); return true ; } else { CHeartBeat * pHeart = new CHeartBeat(); // notify CHeartBeat to create waitable timer if ( ! pHeart -> Start(pMessageInfo -> msg_base_info.szPlanNumber)) { SAFE_DELETE(pHeart); return false ; } hbList.push_back(pHeart); return true ; } return true ;

3. CHeartBeat 클래스의 시작 방법은 다음과 같습니다.
C/C++ code

    
    
    
    
bool CHeartBeat::Start( const char * projNum) { HANDLE handle; handle = CreateEvent(NULL,TRUE,FALSE,NULL); if (handle == NULL) { return false ; } m_hEvent = handle; SetEvent(m_hEvent); strcpy(m_strProj,projNum); return true ; }

4. 서비스 포트가 끝날 때 하트비트가 HANDLE hExitHeartBeat=CreateEvent(NULL,TRUE,TRUE,NULL)에서 종료될 때까지 기다립니다.CHeartBeat::SetExitEvent(hExitHeartBeat); WaitForSingleObject(hHeatBeat,INFINITE);,(1) 심박수 라인의 종료 처리용 코드 섹션 2의region 1에 대해 묻겠습니다. 왜 서비스 측이 Wait For Single Object(hHeat Beat, 인피니트)까지 실행할 때 이 함수는 돌아올 수 없고 심박수 라인은 분명히 종료할 수 있습니다.문장(1)을 Wait For Single Object(hHeatBeat, 1000)로 수정하기;프로그램이 때때로 메모리가 유출될 수 있습니까? 
 
드디어 해결되었습니다. 원래 라인을 끝낼 때 라인이 종료되었는지 확인한 다음 Wait For Single Object를 사용하여 라인이 신호가 될 때까지 기다려야 합니다.그리고 하나의 루틴이 끝날 때까지 기다릴 때, 루틴을 만든 후에 바로 핸들을 닫을 수 없고, 루틴이 신호 상태로 바뀌기를 기다린 후에 핸들을 닫아야 한다.따라서 코드 세그먼트 1은 코드 세그먼트 3과 결합됩니다.
C/C++ code

    
    
    
    
bool bExist = false ; CHeartBeat * pHeartBeat = NULL; for (vector < CHeartBeat *> ::iterator iter = hbList.begin(); iter != hbList.end(); ++ iter) { if (strcmp(( * iter) -> GetProjNum(),pMessageInfo -> msg_base_info.szPlanNumber) == 0 ) { // IM has received health message previous for special plannumber bExist = true ; pHeartBeat =* iter; break ; } } if (bExist) { // notify CHeartBeat to reset waitable timer if (pHeartBeat != NULL) SetEvent(pHeartBeat -> GetEventHandle()); return true ; } else { if (hbList.size() == 0 ) { g_hHeartBeat = CreateThread(NULL, 0 ,CHeartBeat::WaitProc,NULL, 0 ,NULL); } CHeartBeat * pHeart = new CHeartBeat(); // notify CHeartBeat to create waitable timer if ( ! pHeart -> Start(pMessageInfo -> msg_base_info.szPlanNumber)) { SAFE_DELETE(pHeart); return false ; } hbList.push_back(pHeart); return true ; }

코드 섹션 4:
C/C++ code

    
    
    
    
DWORD dwExitCode; GetExitCodeThread(g_hHeartBeat, & dwExitCode); if (dwExitCode == STILL_ACTIVE) { HANDLE hExitHeartBeat = CreateEvent(NULL,TRUE,TRUE,NULL); CHeartBeat::SetExitEvent(hExitHeartBeat); WaitForSingleObject(g_hHeartBeat,INFINITE); CloseHandle(g_hHeartBeat); }

메시지를 받았을 때만 심장 박동 라인을 시작합니다. 심장 박동 목록이 비어 있을 때 심장 박동 라인을 종료합니다.이렇게 하면 루틴이 계속 실행되는 것을 방지할 수 있다. 루틴이 WaitFor* 처리를 호출하지 않고 무한순환 문장을 실행할 때 프로그램은 CPU를 99% 까지 먹게 되기 때문이다.심장 박동 패키지의 처리는 새로운 라인을 시작해야 한다. 이 라인은 세 가지 핵심 대상을 관리해야 한다. 1.클라이언트 전원이 켜질 때마다 발생하는 이벤트 m_hEvent 2.고객 연결 요청을 수락할 때 타이머 m_hTimer 3.주 스레드가 종료되면 심박수 스레드 종료 이벤트를 알리는 m_hExitEvent; 4. 고객 목록(각 고객의 이벤트, 대기 타이머, ProjNum 포함), ProjNum은 각 고객 주소에 대해 첫 번째 고객이 접속할 때 이벤트 m_이벤트, 그리고 심박수 라인을 시작합니다. 심박수 라인에서 Wait For Mutiply Objects를 사용하여 이벤트가 신호가 될 때까지 기다립니다. 이벤트가 신호가 있을 때Create Waitable Timer를 사용하여 타이머를 만들고 Set Waitable Timer를 사용하여 타이머를 설정합니다.고객이 두 번째로 메시지를 보낼 때 m_hEvent는 신호 상태입니다. 이때 심박수 라인은 Waitfor를 사용하여 되돌아온 후 SetWaitable Timer를 사용하여 타이머의 대기 시간을 갱신하면 대기 시간은 다시 30초로 바뀝니다. 어떤 고객이 30초가 지나도 메시지를 보내지 않으면 타이머는 신호 상태가 됩니다. 이때 심꽝 라인은 WaitForMutiply Objects 대기 타이머를 사용하여 타이머 인덱스를 되돌려줍니다.이후 심박수 라인은 고객 목록에서 어떤 구성원을 제거하고 고객이 오프라인 상태일 때 특정한 메시지를 보내며 심박수 라인이 이 메시지를 처리할 때 고객을 목록에서 직접 제거합니다.모든 고객이 오프라인 상태일 때 주 스레드는 m_hExitEvent 알림 심박수 라인을 닫기 위해 주의: 이 예시에서 심박수 라인이 클라이언트에게 메시지를 보내는 처리가 없습니다. 추가할 때 m_를 받아야 합니다.hEvent 이후에 메시지를 보냅니다.Wait For Mutiply Objects는 최대 64개의 고객을 수신하고 더 많은 고객이 접속할 경우 고객 목록을 세그먼트로 나누어 기다려야 합니다.

좋은 웹페이지 즐겨찾기