You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C语言UDP服务器中获取多哈等指定城市的当前时间?

处理多城市时区时间的UDP服务器实现方案

localtime() 默认使用系统本地时区,要生成指定城市的时间,得借助POSIX标准的时区环境变量和 tzset() 函数切换时区(直接计算UTC偏移需手动处理夏令时,规则易变,推荐前者)。

核心思路

  1. 利用系统自带时区数据库(通常位于 /usr/share/zoneinfo/),每个城市对应标准时区标识符。
  2. 通过设置 TZ 环境变量指定目标时区,调用 tzset() 加载时区配置。
  3. 使用线程安全的 localtime_r()(替代非线程安全的 localtime())获取对应时区的时间结构体。
  4. 格式化时间后通过UDP返回给客户端。

各城市对应时区标识符

  • 多哈:Asia/Qatar
  • 布拉格:Europe/Prague
  • 纽约:America/New_York
  • 柏林:Europe/Berlin

代码示例

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

#define BUF_SIZE 1024
#define PORT 12345

// 根据城市名匹配对应时区字符串
const char* get_timezone(const char* city) {
    if (strcmp(city, "多哈") == 0) return "Asia/Qatar";
    if (strcmp(city, "布拉格") == 0) return "Europe/Prague";
    if (strcmp(city, "纽约") == 0) return "America/New_York";
    if (strcmp(city, "柏林") == 0) return "Europe/Berlin";
    return NULL;
}

int main() {
    int sockfd;
    struct sockaddr_in serv_addr, cli_addr;
    socklen_t cli_len = sizeof(cli_addr);
    char buf[BUF_SIZE];
    char time_buf[BUF_SIZE];
    time_t raw_time;
    struct tm tm_info;
    const char* tz_str;
    char* old_tz;

    // 创建UDP套接字
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    memset(&cli_addr, 0, sizeof(cli_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(PORT);

    // 绑定端口
    if (bind(sockfd, (const struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("bind failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("UDP时间服务器启动,监听端口%d...\n", PORT);

    while (1) {
        // 接收客户端请求
        ssize_t n = recvfrom(sockfd, buf, BUF_SIZE-1, 0, (struct sockaddr *)&cli_addr, &cli_len);
        if (n < 0) {
            perror("recvfrom failed");
            continue;
        }
        buf[n] = '\0'; // 确保字符串终止
        printf("收到来自%s:%d的请求:%s\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), buf);

        // 获取对应时区
        tz_str = get_timezone(buf);
        if (!tz_str) {
            snprintf(time_buf, sizeof(time_buf), "错误:不支持的城市%s", buf);
            sendto(sockfd, time_buf, strlen(time_buf), 0, (struct sockaddr *)&cli_addr, cli_len);
            continue;
        }

        // 保存原时区,避免影响后续逻辑
        old_tz = getenv("TZ");
        if (old_tz) old_tz = strdup(old_tz);

        // 设置新时区并加载配置
        setenv("TZ", tz_str, 1);
        tzset();

        // 获取当前时间并转换为目标时区
        time(&raw_time);
        if (localtime_r(&raw_time, &tm_info) == NULL) {
            perror("localtime_r failed");
            snprintf(time_buf, sizeof(time_buf), "时间转换失败");
            sendto(sockfd, time_buf, strlen(time_buf), 0, (struct sockaddr *)&cli_addr, cli_len);
            // 恢复原时区
            if (old_tz) {
                setenv("TZ", old_tz, 1);
                free(old_tz);
            } else {
                unsetenv("TZ");
            }
            tzset();
            continue;
        }

        // 格式化时间(示例格式:YYYY-MM-DD HH:MM:SS)
        strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", &tm_info);

        // 发送响应
        sendto(sockfd, time_buf, strlen(time_buf), 0, (struct sockaddr *)&cli_addr, cli_len);

        // 恢复原时区
        if (old_tz) {
            setenv("TZ", old_tz, 1);
            free(old_tz);
        } else {
            unsetenv("TZ");
        }
        tzset();
    }

    close(sockfd);
    return 0;
}

关键注意点

  • 线程安全:多线程服务器必须用 localtime_r(),避免竞态条件。
  • 时区恢复:处理完请求后务必恢复原时区,防止影响其他逻辑。
  • 夏令时兼容:系统时区数据库会自动处理夏令时规则,无需手动计算偏移,可靠性更高。
  • 错误处理:覆盖请求解析失败、时区不支持、时间转换失败等场景,返回明确错误信息。

内容的提问来源于stack exchange,提问作者Elad Moshe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 07:45:49