という感じになります。
- ソケットの作成を行う。
- 受信ポートを指定する。
- NTPサーバにパケットを送信する。
- NTPサーバから時刻の情報を受信する。
- ソケットを破棄する。
#define WSOCK_GETHOST WM_USER + 1 /* ホスト情報のイベントを通知するメッセージ */ #define WSOCK_SELECT WM_USER + 2 /* ソケットイベントを通知するメッセージ */ /*--------------------------------------------------- ソケットイベントの通知を設定する関数 ---------------------------------------------------*/ BOOL SocketSelect(int sSoc,HWND hWnd) { /* 受信のソケットイベントを通知させるように設定する */ /* ソケットイベントは、ウィンドウにWSOCK_SELECTメッセージで通知されるように設定する */ if(WSAAsyncSelect(sSoc, hWnd, WSOCK_SELECT, FD_READ) == SOCKET_ERROR){ return FALSE; } return TRUE; } /*--------------------------------------------------- サーバにアドレスにIPアドレスとポート番号を設定する ---------------------------------------------------*/ void SetSockaddr(unsigned long cIPaddr,int cPort) { /* サーバのアドレスの構造体にサーバのIPアドレスとポート番号を設定します */ serversockaddr.sin_family = AF_INET; serversockaddr.sin_addr.s_addr = cIPaddr; /* IPアドレス */ serversockaddr.sin_port = htons((unsigned short)cPort); /* ポート番号 */ memset(serversockaddr.sin_zero,(int)0,sizeof(serversockaddr.sin_zero)); } /*--------------------------------------------------- NTPパケットを送信する ---------------------------------------------------*/ BOOL SendSNTPPacket(int sSoc) { struct NTP_Packet NTP_Send; /* 送信するNTPパケット */ /* NTPパケットをSNTP用に初期化する */ NTP_Send.Control_Word = htonl(0x0B000000); NTP_Send.root_delay = 0; NTP_Send.root_dispersion = 0; NTP_Send.reference_identifier = 0; NTP_Send.reference_timestamp = 0; NTP_Send.originate_timestamp = 0; NTP_Send.receive_timestamp = 0; NTP_Send.transmit_timestamp_seconds = 0; NTP_Send.transmit_timestamp_fractions = 0; /* サーバを指定してNTPパケットを送信する */ if(sendto(sSoc,(const char *)&NTP_Send, sizeof( NTP_Send ),0,(struct sockaddr *)&serversockaddr,sizeof(serversockaddr)) == SOCKET_ERROR){ return FALSE; } return TRUE; } /*--------------------------------------------------- ソケットを破棄する関数 ---------------------------------------------------*/ void SocketClose(HWND hWnd) { /* タイムアウト用のタイマーを破棄する */ KillTimer(hWnd,TIMER_ID); /* WSAAsyncGetHostByName関数の非同期なタスクがある場合はキャンセルする */ if(hGetHost != NULL){ WSACancelAsyncRequest(hGetHost); hGetHost = NULL; } /* ソケットイベントの通知を取り消す */ WSAAsyncSelect(soc, hWnd, 0,0); /* ソケットを破棄する */ closesocket(soc); soc = -1; if(sync_flag == FALSE){ SetWindowText(hBTNGET,"GET"); }else{ SetWindowText(hBTNSYNC,"SYNC"); } } /*--------------------------------------------------- ソケットのエラー処理関数 ---------------------------------------------------*/ void SocketError(HWND hWnd,char *msgbuf) { /* ソケットを破棄する */ SocketClose(hWnd); /* エラーメッセージ */ MessageBox(hWnd,msgbuf,"Error",MB_OK | MB_ICONERROR); } /*--------------------------------------------------- SNTPセッションの開始 ---------------------------------------------------*/ void SNTPStart(HWND hWnd,char *buf) { unsigned long serveraddr; /* サーバのIPアドレス */ /* 入力文字からサーバ名とパスを取得する */ Port = GetServerPort(buf,SvName); if(Port == -1){ MessageBox(hWnd, "サーバ名が入力されていません。", "Error", MB_OK | MB_ICONERROR); if(sync_flag == FALSE){ SetWindowText(hBTNGET,"GET"); }else{ SetWindowText(hBTNSYNC,"SYNC"); } return; } /* socにソケットを作成します */ soc = socket(PF_INET, SOCK_DGRAM, 0); if(soc == INVALID_SOCKET){ MessageBox(hWnd, "Socketの作成に失敗しました。", "Error", MB_OK | MB_ICONERROR); soc = -1; if(sync_flag == FALSE){ SetWindowText(hBTNGET,"GET"); }else{ SetWindowText(hBTNSYNC,"SYNC"); } return; } /* svNameにドットで区切った10進数のIPアドレスが入っている場合、serveraddrに32bit整数のIPアドレスが返ります */ serveraddr = inet_addr((char*)SvName); if(serveraddr == -1){ /* サーバ名からサーバのホスト情報を取得する */ /* ホスト情報が取得できると、ウィンドウにWSOCK_GETHOSTが通知されるようにする */ hGetHost = WSAAsyncGetHostByName(hWnd,WSOCK_GETHOST,SvName,gHostEnt, MAXGETHOSTSTRUCT); if(hGetHost == 0){ SocketError(hWnd,"サーバのIPアドレスの取得に失敗しました。"); return; } return; } /* 接続、受信、切断のイベントをウィンドウメッセージで通知されるようにする */ if(SocketSelect(soc, hWnd) == FALSE){ SocketError(hWnd,"Socketイベントを通知させる設定に失敗しました。"); return; } SetSockaddr(serveraddr,Port); if(SendSNTPPacket(soc) == FALSE){ SocketError(hWnd,"送信に失敗しました。"); return; } /* タイムアウト用のタイマーをセットする */ SetTimer(hWnd, TIMER_ID, 3000, NULL); } /*--------------------------------------------------- IPアドレスを取得してデータを送信する ---------------------------------------------------*/ void GetHostToIPaddr(HWND hWnd, WPARAM wParam, LPARAM lParam) { struct hostent FAR *HostEntry; /* ホスト情報 */ unsigned long serveraddr; /* サーバのIPアドレス */ /* エラーの判定 */ if(WSAGETASYNCERROR(lParam) != 0){ SocketError(hWnd,"サーバのIPアドレスの取得に失敗しました。"); return; } /* WSAAsyncGetHostByNameの戻り値である非同期なタスクのハンドルか判定*/ if(hGetHost != (HANDLE)wParam){ return; } HostEntry = (struct hostent FAR *)gHostEnt; serveraddr = *((unsigned long *)((HostEntry->h_addr_list)[0])); /* IPアドレスを取得する */ hGetHost = NULL; /* ソケットイベントをウィンドウメッセージで通知されるようにする */ if(SocketSelect(soc, hWnd) == FALSE){ SocketError(hWnd,"Socketイベントを通知させる設定に失敗しました。"); return; } SetSockaddr(serveraddr,Port); if(SendSNTPPacket(soc) == FALSE){ SocketError(hWnd,"送信に失敗しました。"); return; } /* タイムアウト用のタイマーをセットする */ SetTimer(hWnd, TIMER_ID, 3000, NULL); } /*--------------------------------------------------- SNTPのデータを受信する ---------------------------------------------------*/ void SNTPRecvData(HWND hWnd) { struct NTP_Packet NTP_Recv; /* 受信するNTPパケット */ int sockaddr_Size; /* サーバのアドレスのサイズ */ char buf[BUFSIZE]; time_t Cur_time; /* ローカルマシンの時刻 */ time_t ntp_time; /* NTPサーバから取得した時刻 */ struct tm *lpNewLocalTime; /* 現地時刻に変換したNTPサーバの時刻 */ SYSTEMTIME Timecall; /* ローカルマシンに設定する時刻 */ float Splitseconds; /* サーバを指定して受信を行う */ sockaddr_Size = sizeof(serversockaddr); if(recvfrom(soc, (char *)&NTP_Recv, sizeof(NTP_Recv), 0,(struct sockaddr *)&serversockaddr,&sockaddr_Size) == SOCKET_ERROR ){ SocketError(hWnd,"受信に失敗しました。"); return; } /* ソケットを破棄する */ SocketClose(hWnd); /* ローカルマシンの時刻を取得する */ Cur_time = time(NULL); /* NTPサーバから取得した時刻を現地時間に変換する */ ntp_time = ntohl(NTP_Recv.transmit_timestamp_seconds) - 2208988800; /* 1970/01/01 からの秒数に変換 */ lpNewLocalTime = localtime((unsigned int *)&ntp_time); if(lpNewLocalTime == NULL){ MessageBox(hWnd,"時刻の取得に失敗しました。","Error",MB_OK | MB_ICONERROR); return; } /* 同期フラグが立っている場合は、時刻の同期を行う */ if(sync_flag == TRUE){ /* ローカル時刻の計算 */ Timecall.wYear = lpNewLocalTime->tm_year + 1900; Timecall.wMonth = lpNewLocalTime->tm_mon + 1; Timecall.wDay = lpNewLocalTime->tm_mday; Timecall.wHour = lpNewLocalTime->tm_hour; Timecall.wMinute = lpNewLocalTime->tm_min; Timecall.wSecond = lpNewLocalTime->tm_sec; Splitseconds = (float)ntohl(NTP_Recv.transmit_timestamp_fractions); Splitseconds = (float)0.000000000200 * Splitseconds; Splitseconds = (float)1000.0 * Splitseconds; Timecall.wMilliseconds = (unsigned short)Splitseconds; /* ローカル時刻をNTPサーバから取得した時刻に設定する */ if(SetLocalTime(&Timecall) == FALSE){ MessageBox(hWnd,"時刻の設定に失敗しました。","Error",MB_OK | MB_ICONERROR); }else{ MessageBox(hWnd,"NTPサーバの時刻に同期しました。","info",MB_OK | MB_ICONINFORMATION); } } /* ローカル時刻を表示する */ wsprintf(buf,"%s",ctime((unsigned int *)&Cur_time)); buf[strlen(buf) - 1] = '\0'; SendMessage(LocalEdit,WM_SETTEXT,0,(LPARAM) ((LPSTR)buf)); /* NTPサーバから取得した時刻を表示する */ wsprintf(buf,"%s",ctime((unsigned int *)&ntp_time)); buf[strlen(buf) - 1] = '\0'; SendMessage(NTPEdit,WM_SETTEXT,0,(LPARAM) ((LPSTR)buf)); } /*--------------------------------------------------- メインウィンドウのプロシージャ ---------------------------------------------------*/ LONG APIENTRY MainProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { char buf[BUFSIZE]; switch (uMsg) { case WM_CREATE: /* ウィンドウ作成時 */ /* ウィンドウ内のコントロールを作成する */ CreateWindow("STATIC",(LPSTR)"Server:",WS_CHILD | WS_VISIBLE,5,10,50,25,hWnd,(HMENU)NULL,g_hinst,NULL); CreateWindow("STATIC",(LPSTR)"Local:",WS_CHILD | WS_VISIBLE,5,50,50,25,hWnd,(HMENU)NULL,g_hinst,NULL); CreateWindow("STATIC",(LPSTR)"NTP:",WS_CHILD | WS_VISIBLE,5,80,50,25,hWnd,(HMENU)NULL,g_hinst,NULL); ServerEdit = CreateWindowEx(WS_EX_CLIENTEDGE,"EDIT",(LPSTR)NULL,WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,60,10,300,25,hWnd,(HMENU)ID_EDIT_SERVER,g_hinst,NULL); LocalEdit = CreateWindowEx(WS_EX_CLIENTEDGE,"EDIT",(LPSTR)NULL,WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY,60,50,420,25,hWnd,(HMENU)ID_EDIT_CUR,g_hinst,NULL); NTPEdit = CreateWindowEx(WS_EX_CLIENTEDGE,"EDIT",(LPSTR)NULL,WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | ES_READONLY,60,80,420,25,hWnd,(HMENU)ID_EDIT_NTP,g_hinst,NULL); hBTNGET = CreateWindow("BUTTON",(LPSTR)"GET",WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,370,10,50,25,hWnd,(HMENU)ID_GET,g_hinst,NULL); hBTNSYNC = CreateWindow("BUTTON",(LPSTR)"SYNC",WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,430,10,50,25,hWnd,(HMENU)ID_SYNC,g_hinst,NULL); SetFocus(ServerEdit); /* 初期化 */ soc = -1; hGetHost = NULL; break; case WSOCK_GETHOST: /* サーバのホスト情報関数のイベント */ /* ホスト情報からIPアドレスを取得して接続を開始する */ GetHostToIPaddr(hWnd,wParam,lParam); break; case WSOCK_SELECT: /* ソケットイベントのメッセージの場合(WSAAsyncSelect関数にて登録) */ /* エラーの判定 */ if(soc != -1 && WSAGETSELECTERROR(lParam) != 0){ SocketError(hWnd,"Socketイベントの通知でエラーが発生しました。"); break; } /* 処理すべきソケットか判定 */ if(soc != (int)wParam){ break; } /* 受信のイベントの場合は処理を行う */ if(WSAGETSELECTEVENT(lParam) == FD_READ){ /* データを受信する */ SNTPRecvData(hWnd); } break; case WM_COMMAND: switch(LOWORD(wParam)){ case ID_GET: /* 取得するボタンが押されたときの処理 */ if(soc != -1){ /* 既に通信中の場合はキャンセルする */ /* ソケットを破棄する */ SocketClose(hWnd); }else{ /* 通信を開始する */ /* サーバ名を取得する */ SendMessage(ServerEdit,WM_GETTEXT,BUFSIZE - 1,(LPARAM)buf); SetWindowText(hBTNGET,"Cancel"); /* 同期フラグを偽に設定する */ sync_flag = FALSE; /* SNTTPセッションを開始する */ SNTPStart(hWnd,buf); } break; case ID_SYNC: /* 同期するボタンが押されたときの処理 */ if(soc != -1){ /* 既に通信中の場合はキャンセルする */ /* ソケットを破棄する */ SocketClose(hWnd); }else{ /* 通信を開始する */ /* サーバ名を取得する */ SendMessage(ServerEdit,WM_GETTEXT,BUFSIZE - 1,(LPARAM)buf); SetWindowText(hBTNSYNC,"Cancel"); /* 同期フラグを真に設定する */ sync_flag = TRUE; /* SNTPセッションを開始する */ SNTPStart(hWnd,buf); } break; } break; case WM_TIMER: /* タイムアウト時の処理 */ if(wParam == TIMER_ID){ /* タイムアウトのメッセージを表示する */ SocketError(hWnd,"タイムアウトしました。"); break; } break; : :
sntpcex00.zipこのプログラムはGUIベースで動作します。
- sntpcex.c
- sntpcex.rc
- resource.h
Res
- icon1.ico
- sntpcex.exe