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

devtools/2025/3/25 11:24:53/

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

服务器

#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 64int 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;
}

文章来源:https://blog.csdn.net/2301_77654321/article/details/146405916
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/devtools/169187.html

相关文章

深入理解Java虚拟机(学习笔记)

什么是JVM&#xff1f; 定义&#xff1a;Java Virtual Machine-java程序运行环境&#xff08;java二进制字节码运行环境&#xff09; 好处&#xff1a; 一次编写&#xff0c;到处运行。自动内存管理&#xff0c;垃圾回收功能数组下标越界检查多态 比较&#xff1a;JDK JRE J…

接收与发送ipv6数据包

一、ipv6的概念 IPv6 是英文 “Internet Protocol Version 6”&#xff08;互联网协议第 6 版&#xff09;的缩写&#xff0c;是互联网工程任务组&#xff08;IETF&#xff09;设计的用于替代 IPv4 的下一代 IP 协议&#xff0c;其地址数量号称可以为全世界的每一粒沙子编上…

软件工程之软件验证计划Software Verification Plan

个人主页&#xff1a;云纳星辰怀自在 座右铭&#xff1a;“所谓坚持&#xff0c;就是觉得还有希望&#xff01;” 本文为基于ISO26262软件验证计划模板&#xff0c;仅供参考。 软件验证计划&#xff0c;包括&#xff1a; 1. 软件需求验证计划 2. 软件架构设计验证计划 3. 软件单…

四.ffmpeg对yuv数据进行h264编码

一.windows 1.什么是h264&#xff1f; 2.h264编码流程 3.主要整体代码 int open_coder(AVCodecContext **codec_ctx) {// 编码器const AVCodec *codex avcodec_find_encoder_by_name("libfdk_aac");// codex->capabilities AV_CODEC_CAP_VARIABLE_FRAME_SIZE;…

NFS 安装与测试

NFS 安装与测试 服务器 # NFS 共享目录 mkdir -p ${HOME}/Share/nfs && sudo chown -R nobody:nogroup ${HOME}/Share/nfs# 安装 NFS Server sudo apt install nfs-kernel-server# 配置 NFS 目录 sudo cp -arf /etc/exports /etc/exports.bak sudo tee -a /etc/exports…

《Python实战进阶》No26: CI/CD 流水线:GitHub Actions 与 Jenkins 集成

No26: CI/CD 流水线&#xff1a;GitHub Actions 与 Jenkins 集成 摘要 持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;是现代软件开发中不可或缺的实践&#xff0c;能够显著提升开发效率、减少错误并加速交付流程。本文将探讨如何利用 GitHub Actio…

枚举的定义及其使用

在Java中&#xff0c;enum&#xff08;枚举&#xff09;是一个特殊的类&#xff0c;用于表示一组常量。enum类型在Java中提供了一种类型安全的方式来定义常量&#xff0c;相比传统的常量&#xff08;如public static final变量&#xff09;&#xff0c;它更加简洁、类型安全&am…

Vue3 基础语法指南:响应式系统与 Ref 应用

1、Reactive 的深度响应式 1.1、基本用法 vue <script setup> import { reactive } from vueconst state reactive({count: 0,user: {name: Alice,age: 30} })const increment () > state.count const updateName () > state.user.name Bob </script>1…