0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C言語 NTPサーバーより時間を取得

Posted at

時間取得

ubuntu@ubuntu:~/kaihatsu/net$ gcc server.c -o server
ubuntu@ubuntu:~/kaihatsu/net$ ./server
現在時刻: 2025-05-22 12:27:11 UTC
ubuntu@ubuntu:~/kaihatsu/net$ 

Cコード

server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>

// 定数の定義
#define NTP_SERVER "pool.ntp.org"       // NTPサーバーを自動選択
#define NTP_PORT 123                     // NTP標準ポート
#define NTP_TIMESTAMP_DELTA 2208988800ULL // 1900年と1970年の時間差(秒数)
/* 詳細計算:
   365日/年 × 70年 = 25550日
   閏年17日分を追加 = 25550 + 17 = 25567日
   25567日 × 86400秒/日 = 2,208,988,800秒
*/

int main() {
    // ソケット関連変数
    int sockfd;                          // 通信に使うソケットのID
    struct sockaddr_in serv_addr;        // サーバー情報格納用
    char ntp_packet[48] = {0x1B};        // NTPリクエストデータ
    /* NTPパケット先頭バイトの解析:
       0b00 (閏秒指示子: 警告なし)
       0b011 (バージョン番号: NTPv3)
       0b011 (モード: クライアントモード)
       バイナリ組み合わせ 00 011 011 → 0x1B
    */
    char recv_buffer[48];                 // 受信バッファ
    struct timeval timeout = {5, 0};      // 5秒受信タイムアウト

    // UDPソケットの作成
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
        perror("ソケット作成失敗");
        exit(EXIT_FAILURE);  // 一般的なエラーコードは1
    }

    // タイムアウト設定(応答がない場合の待機時間)
    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
        perror("タイムアウト設定失敗");
        close(sockfd);       // 作成済みソケットを閉じる
        exit(EXIT_FAILURE);
    }

    // サーバー情報設定
    memset(&serv_addr, 0, sizeof(serv_addr));  // アドレス構造体をゼロ初期化
    serv_addr.sin_family = AF_INET;            // IPv4アドレスファミリー
    serv_addr.sin_port = htons(NTP_PORT);      // ポート番号設定
    
    // ドメイン名→IPアドレス変換
    struct hostent *server = gethostbyname(NTP_SERVER);
    if (server == NULL) {
        fprintf(stderr, "ホスト名解決失敗: %s\n", NTP_SERVER);
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    // 解決されたIPアドレスをアドレス構造体に格納
    memcpy(&serv_addr.sin_addr, server->h_addr_list[0], server->h_length);

    // NTPサーバーにリクエスト送信
    if (sendto(sockfd, ntp_packet, sizeof(ntp_packet), 0, 
               (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("リクエスト送信失敗");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // サーバーからの応答受信
    socklen_t addr_len = sizeof(serv_addr);  // アドレス構造体の長さ
    int recv_len = recvfrom(sockfd, recv_buffer, sizeof(recv_buffer), 0,
                           (struct sockaddr*)&serv_addr, &addr_len);
    if (recv_len < 0) {
        perror("レスポンス受信失敗");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    // 時刻データ処理
    uint32_t ntp_time;  // 時刻データ格納用
    memcpy(&ntp_time, recv_buffer + 40, 4);  // 受信データの40-43バイト目に時刻情報が格納されている
    ntp_time = ntohl(ntp_time);  // ネットワーク形式→自PC形式に変換(ビッグエンディアン→リトルエンディアンなど)

    // NTP時刻(1900年基準)→UNIX時刻(1970年基準)に変換
    time_t unix_time = ntp_time - NTP_TIMESTAMP_DELTA;

    // 表示する時間形式に変換
    struct tm *timeinfo = gmtime(&unix_time);  // UTC時間構造体へ変換
    char buffer[80];
    // 人間が読める形式に変換
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S UTC", timeinfo);
    printf("現在時刻: %s\n", buffer);

    // ソケットを閉じる
    close(sockfd);
    return 0;
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?