网络编程之排除udp判断客户端是否断开

打印 上一主题 下一主题

主题 998|帖子 998|积分 2994

思路:每几秒发送一条不显示的信息,客户端断开则不再发送信息,超时则表现客户端断开毗连。(心跳包)

服务器
  1. #include <head.h>
  2. #define MAX_CLIENTS 100  // 最大支持100个客户端
  3. #define TIMEOUT 5        // 5秒超时
  4. struct Client {
  5.     struct sockaddr_in addr;
  6.     time_t last_seen;  // 记录最后一次收到该客户端数据的时间
  7. };
  8. struct Client client_list[MAX_CLIENTS];
  9. int client_count = 0;
  10. // **更新客户端心跳时间**
  11. void update_client(struct sockaddr_in *client_addr) {
  12.     time_t now = time(NULL);
  13.     for (int i = 0; i < client_count; i++) {
  14.         if (memcmp(&client_list[i].addr, client_addr, sizeof(struct sockaddr_in)) == 0) {
  15.             client_list[i].last_seen = now;  // 更新时间
  16.             return;
  17.         }
  18.     }
  19.     // **如果客户端不在列表中,则添加**
  20.     if (client_count < MAX_CLIENTS) {
  21.         client_list[client_count].addr = *client_addr;
  22.         client_list[client_count].last_seen = now;
  23.         client_count++;
  24.     }
  25. }
  26. // **检查超时客户端**
  27. void check_clients() {
  28.     time_t now = time(NULL);
  29.     for (int i = 0; i < client_count; i++) {
  30.         if (now - client_list[i].last_seen > TIMEOUT) {
  31.             printf("客户端 %s:%d 断开\n",
  32.                    inet_ntoa(client_list[i].addr.sin_addr),
  33.                    ntohs(client_list[i].addr.sin_port));
  34.             // **移除客户端**
  35.             for (int j = i; j < client_count - 1; j++) {
  36.                 client_list[j] = client_list[j + 1];
  37.             }
  38.             client_count--;
  39.             i--;  // **继续检查下一个**
  40.         }
  41.     }
  42. }
  43. int main(int argc, const char *argv[]) {
  44.     if (argc < 2) {
  45.         printf("请输入端口号\n");
  46.         return 1;
  47.     }
  48.     short port = atoi(argv[1]);
  49.     // **创建 UDP 套接字**
  50.     int receiver = socket(AF_INET, SOCK_DGRAM, 0);
  51.     struct sockaddr_in addr = {0};
  52.     addr.sin_family = AF_INET;
  53.     addr.sin_port = htons(port);
  54.     addr.sin_addr.s_addr = inet_addr("0.0.0.0");
  55.     if (bind(receiver, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
  56.         perror("bind");
  57.         return 1;
  58.     }
  59.     printf("服务器启动,监听端口 %d\n", port);
  60.     struct sockaddr_in client_addr;
  61.     socklen_t addr_len = sizeof(client_addr);
  62.     char buf[64];
  63.     fd_set readfds;
  64.     struct timeval timeout;
  65.     while (1) {
  66.         // **使用 select 进行超时检测**
  67.         FD_ZERO(&readfds);
  68.         FD_SET(receiver, &readfds);
  69.         timeout.tv_sec = 1;  // 每秒检查一次
  70.         timeout.tv_usec = 0;
  71.         int activity = select(receiver + 1, &readfds, NULL, NULL, &timeout);
  72.         if (activity > 0) {
  73.             // **接收数据**
  74.             memset(buf, 0, sizeof(buf));
  75.             int len = recvfrom(receiver, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&client_addr, &addr_len);
  76.             if (len > 0) {
  77.                 buf[len] = '\0';
  78.                 update_client(&client_addr);  // **更新心跳时间**
  79.                 // **如果是心跳包 "PING",不打印、不转发**
  80.                 if (strcmp(buf, "PING") == 0) {
  81.                     continue;
  82.                 }
  83.                 printf("收到消息: %s\n", buf);
  84.                 // **转发消息给所有在线客户端**
  85.                 for (int i = 0; i < client_count; i++) {
  86.                     sendto(receiver, buf, strlen(buf), 0,
  87.                            (struct sockaddr*)&client_list[i].addr, sizeof(client_list[i].addr));
  88.                 }
  89.             }
  90.         }
  91.         // **检查超时客户端**
  92.         check_clients();
  93.     }
  94.     return 0;
  95. }
复制代码
客户端
  1. #include <head.h>
  2. #include <pthread.h>
  3. #define BUF_SIZE 64
  4. int sender;  // 套接字
  5. struct sockaddr_in addr;
  6. void *heartbeat(void* arg)
  7. {
  8.     while(1)
  9.     {
  10.         // 发送空的心跳包
  11.         sendto(sender, "", 1, 0, (struct sockaddr*)&addr, sizeof(addr));
  12.         sleep(2);  // 每2秒发送一次心跳包
  13.     }
  14. }
  15. int main(int argc, const char *argv[])
  16. {
  17.     if (argc < 2) {
  18.         printf("请输入端口号\n");
  19.         return 1;
  20.     }
  21.     short port = atoi(argv[1]);
  22.     // 创建套接字
  23.     sender = socket(AF_INET, SOCK_DGRAM, 0);
  24.     if (sender == -1) {
  25.         perror("创建套接字失败");
  26.         return 1;
  27.     }
  28.     addr.sin_family = AF_INET;
  29.     addr.sin_port = htons(port);
  30.     addr.sin_addr.s_addr = inet_addr("192.168.128.20");  // 服务器的IP地址
  31.     // 启动心跳包线程
  32.     pthread_t heart;
  33.     pthread_create(&heart, NULL, heartbeat, NULL);
  34.     while (1) {
  35.         char buf[BUF_SIZE] = "";
  36.         printf("输入:");
  37.         scanf("%s", buf);
  38.         getchar();  // 读取输入并去掉换行符
  39.         // 发送普通消息
  40.         sendto(sender, buf, strlen(buf), 0, (struct sockaddr*)&addr, sizeof(addr));
  41.         // 接收服务器的回复
  42.         int len = recvfrom(sender, buf, BUF_SIZE - 1, 0, NULL, NULL);
  43.         if (len > 0) {
  44.             buf[len] = '\0';  // 确保字符串以 '\0' 结尾
  45.             printf("接收到回复的消息: %s\n", buf);
  46.         } else {
  47.             printf("接收服务器消息失败\n");
  48.         }
  49.     }
  50.     // 关闭套接字
  51.     close(sender);
  52.     return 0;
  53. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用户云卷云舒

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表