对与一些基本的需求,必须俩设备之间简单的通信这样的需求,实际上只需要了解基本的用法和函数即可应对。但是要想更深入的了解socket的原理,以及处理更高级的socket需求,更高的数据安全性,那么就得继续了解socket的高级用法。
客户端与服务端保持长连接
什么是保持长连接?就是当客户端与服务端连接上之后就不再断开。双方可以各自检测自己的消息队列,如果有消息就及时发送。实现这个需求,有两个方案:
- 心跳包机制:直接借助TCP的心跳包去检测当前连接是否正常
- 应用程序实现“心跳包”:在应用程序中使用定时器实现一个心跳包机制。
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端做一定的动作,当收到客户端发送的“心跳包”之后,需要回复特定的语句从而助于客户端判断连接情况。