当前位置: 首页 > news >正文

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

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

服务器

#include <head.h>

#define MAX_CLIENTS 100  // 最大支持100个客户端
#define TIMEOUT 5        // 5秒超时

struct Client {
    struct sockaddr_in addr;
    time_t last_seen;  // 记录最后一次收到该客户端数据的时间
};

struct Client client_list[MAX_CLIENTS];
int client_count = 0;

// **更新客户端心跳时间**
void update_client(struct sockaddr_in *client_addr) {
    time_t now = time(NULL);

    for (int i = 0; i < client_count; i++) {
        if (memcmp(&client_list[i].addr, client_addr, sizeof(struct sockaddr_in)) == 0) {
            client_list[i].last_seen = now;  // 更新时间
            return;
        }
    }

    // **如果客户端不在列表中,则添加**
    if (client_count < MAX_CLIENTS) {
        client_list[client_count].addr = *client_addr;
        client_list[client_count].last_seen = now;
        client_count++;
    }
}

// **检查超时客户端**
void check_clients() {
    time_t now = time(NULL);
    for (int i = 0; i < client_count; i++) {
        if (now - client_list[i].last_seen > TIMEOUT) {
            printf("客户端 %s:%d 断开\n",
                   inet_ntoa(client_list[i].addr.sin_addr),
                   ntohs(client_list[i].addr.sin_port));

            // **移除客户端**
            for (int j = i; j < client_count - 1; j++) {
                client_list[j] = client_list[j + 1];
            }
            client_count--;
            i--;  // **继续检查下一个**
        }
    }
}

int main(int argc, const char *argv[]) {
    if (argc < 2) {
        printf("请输入端口号\n");
        return 1;
    }
    short port = atoi(argv[1]);

    // **创建 UDP 套接字**
    int receiver = socket(AF_INET, SOCK_DGRAM, 0);
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");

    if (bind(receiver, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        perror("bind");
        return 1;
    }

    printf("服务器启动,监听端口 %d\n", port);

    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);
    char buf[64];

    fd_set readfds;
    struct timeval timeout;

    while (1) {
        // **使用 select 进行超时检测**
        FD_ZERO(&readfds);
        FD_SET(receiver, &readfds);
        timeout.tv_sec = 1;  // 每秒检查一次
        timeout.tv_usec = 0;

        int activity = select(receiver + 1, &readfds, NULL, NULL, &timeout);

        if (activity > 0) {
            // **接收数据**
            memset(buf, 0, sizeof(buf));
            int len = recvfrom(receiver, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&client_addr, &addr_len);
            if (len > 0) {
                buf[len] = '\0';
                update_client(&client_addr);  // **更新心跳时间**

                // **如果是心跳包 "PING",不打印、不转发**
                if (strcmp(buf, "PING") == 0) {
                    continue;
                }

                printf("收到消息: %s\n", buf);

                // **转发消息给所有在线客户端**
                for (int i = 0; i < client_count; i++) {
                    sendto(receiver, buf, strlen(buf), 0, 
                           (struct sockaddr*)&client_list[i].addr, sizeof(client_list[i].addr));
                }
            }
        }

        // **检查超时客户端**
        check_clients();
    }

    return 0;
}

客户端

#include <head.h>
#include <pthread.h>

#define BUF_SIZE 64

int sender;  // 套接字

struct sockaddr_in addr;

void *heartbeat(void* arg)
{
    while(1)
    {
        // 发送空的心跳包
        sendto(sender, "", 1, 0, (struct sockaddr*)&addr, sizeof(addr));
        sleep(2);  // 每2秒发送一次心跳包
    }
}

int main(int argc, const char *argv[])
{
    if (argc < 2) {
        printf("请输入端口号\n");
        return 1;
    }
    short port = atoi(argv[1]);

    // 创建套接字
    sender = socket(AF_INET, SOCK_DGRAM, 0);
    if (sender == -1) {
        perror("创建套接字失败");
        return 1;
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr("192.168.128.20");  // 服务器的IP地址

    // 启动心跳包线程
    pthread_t heart;
    pthread_create(&heart, NULL, heartbeat, NULL);

    while (1) {
        char buf[BUF_SIZE] = "";
        printf("输入:");
        scanf("%s", buf);
        getchar();  // 读取输入并去掉换行符

        // 发送普通消息
        sendto(sender, buf, strlen(buf), 0, (struct sockaddr*)&addr, sizeof(addr));

        // 接收服务器的回复
        int len = recvfrom(sender, buf, BUF_SIZE - 1, 0, NULL, NULL);
        if (len > 0) {
            buf[len] = '\0';  // 确保字符串以 '\0' 结尾
            printf("接收到回复的消息: %s\n", buf);
        } else {
            printf("接收服务器消息失败\n");
        }
    }

    // 关闭套接字
    close(sender);
    return 0;
}

相关文章:

  • 调研报告:Hadoop 3.x Ozone 全景解析
  • 网络安全设备配置与管理-实验4-防火墙AAA服务配置
  • 仿新浪微博typecho主题源码
  • VulnHub-Web-Machine-N7通关攻略
  • 【DeepSeek学C++】移动构造函数
  • html5-qrcode前端打开摄像头扫描二维码功能
  • 【嵌入式学习】时钟 - 边缘触发锁存器
  • C# 零基础入门篇(19.DateTime 使用指南)
  • 【动态规划篇】91. 解码方法
  • Arduino示例代码讲解:Pitch follower 跟随
  • 舞狮表演(dp)
  • 基于32单片机的无人机直流电机闭环调速系统设计
  • xpath轴
  • git 子模块的使用
  • EMQX安装与配置
  • java项目之基于ssm的疫苗预约系统(源码+文档)
  • 基于分类算法的学习失败预警(上)
  • 力扣热题100(方便自己复习,自用)
  • 利用ffmpeg库实现音频Opus编解码
  • 车载以太网网络测试-18【传输层-DOIP协议-1】
  • 女孩患异食癖爱吃头发,一年后腹痛入院体内惊现“头发巨石”
  • 光明日报:家长孩子共同“息屏”,也要保证高质量陪伴
  • 秦洪看盘|指标股发力,A股渐有突破态势
  • 上海市国防动员办公室副主任吴斌接受审查调查
  • 阿坝州委书记徐芝文已任四川省政府党组成员
  • 缺字危机:一本书背后有多少“不存在”的汉字?