如何在C语言UDP服务器中获取多哈等指定城市的当前时间?
处理多城市时区时间的UDP服务器实现方案
localtime() 默认使用系统本地时区,要生成指定城市的时间,得借助POSIX标准的时区环境变量和 tzset() 函数切换时区(直接计算UTC偏移需手动处理夏令时,规则易变,推荐前者)。
核心思路
- 利用系统自带时区数据库(通常位于
/usr/share/zoneinfo/),每个城市对应标准时区标识符。 - 通过设置
TZ环境变量指定目标时区,调用tzset()加载时区配置。 - 使用线程安全的
localtime_r()(替代非线程安全的localtime())获取对应时区的时间结构体。 - 格式化时间后通过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
相关产品推荐
相关产品推荐

