socket系列(三)- 客户端与服务端保持长连接

[复制链接]
查看796 | 回复5 | 2023-9-17 14:37:47 | 显示全部楼层 |阅读模式

对与一些基本的需求,必须俩设备之间简单的通信这样的需求,实际上只需要了解基本的用法和函数即可应对。但是要想更深入的了解socket的原理,以及处理更高级的socket需求,更高的数据安全性,那么就得继续了解socket的高级用法。

客户端与服务端保持长连接

什么是保持长连接?就是当客户端与服务端连接上之后就不再断开。双方可以各自检测自己的消息队列,如果有消息就及时发送。实现这个需求,有两个方案:

  1. 心跳包机制:直接借助TCP的心跳包去检测当前连接是否正常
  2. 应用程序实现“心跳包”:在应用程序中使用定时器实现一个心跳包机制。

TCP心跳包机制

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <time.h>

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8888
#define HEARTBEAT_INTERVAL 30

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[1024];

    // 创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        exit(1);
    }

    // 设置套接字选项,启用心跳包
    int optval = 1;
    if (setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)) < 0) {
        perror("setsockopt");
        exit(1);
    }

    // 初始化服务器地址结构体
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);

    // 连接到服务器
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect");
        exit(1);
    }

    // 发送心跳包
    while (1) {
        // 获取当前时间
        time_t now;
        time(&now);
        struct tm *tm_now = localtime(&now);

        // 构造心跳包内容
        sprintf(buffer, "%d:%d:%d", tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);

        // 发送心跳包
        if (send(sockfd, buffer, strlen(buffer), 0) < 0) {
            perror("send");
            exit(1);
        }

        // 等待一段时间
        sleep(HEARTBEAT_INTERVAL);

        // 在这里处理服务器未响应的情况,一般进行重连
    }

    return 0;
}

应用程序实现“心跳”

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <time.h>
#include <pthread.h>

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8888
#define HEARTBEAT_INTERVAL 30

pthread_t heartbeat;

void * heartbeat_func(void * arg)
{
    char snd_buffer[1024];
    char rcv_buffer[1024]
    while (1) {
        // 获取当前时间
        time_t now;
        time(&now);
        struct tm *tm_now = localtime(&now);

        // 构造心跳包内容
        sprintf(snd_buffer, "%d:%d:%d", tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);

        // 发送心跳包
        if (send(sockfd, snd_buffer, strlen(snd_buffer), 0) < 0) {
            perror("send");
            exit(1);
        }

        // 等待一段时间
        sleep(HEARTBEAT_INTERVAL);

        if (recv(sockfd, rcv_buffer, 1024, 0) < 0) {
            perror("recv");
            exit(1);
        }

        if (strstr(rcv_buffer, snd_buffer)) {
            printf("活着!\n");
        }
    }
}

int main() {
    int sockfd;
    struct sockaddr_in server_addr;

    // 创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        exit(1);
    }

    // 设置套接字选项,启用心跳包
    int optval = 1;
    if (setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)) < 0) {
        perror("setsockopt");
        exit(1);
    }

    // 初始化服务器地址结构体
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);

    // 连接到服务器
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect");
        exit(1);
    }

    pthread_create(&heartbeat, NULL, heartbeat_func, NULL);
    pthread_join(heartbeat, NULL);

    return 0;
}

以上的两段实现都以客户端为主,从客户端主动的去判断连接是否存续。对于第二种方法:应用程序中的实现,还需要再server端做一定的动作,当收到客户端发送的“心跳包”之后,需要回复特定的语句从而助于客户端判断连接情况。

回复

使用道具 举报

496199544 | 2023-9-17 16:12:43 来自手机 | 显示全部楼层
学习了
回复

使用道具 举报

iiv | 2023-9-17 21:02:12 | 显示全部楼层
大佬666
回复

使用道具 举报

jkernet | 2023-9-17 21:15:54 | 显示全部楼层
心跳还有一个目的就是检测链接是否存活,某些情况的断开,无法触发socket的断开消息,比如拔网线等.
回复 支持 反对

使用道具 举报

ifwz1729 | 2023-9-22 10:38:57 | 显示全部楼层
学习
知足常乐
回复

使用道具 举报

san | 2024-1-16 00:06:21 | 显示全部楼层
学习
回复

使用道具 举报

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

本版积分规则