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

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