level 5
西安恩仪联教育
楼主
本节目录:
1、长连接和短连接概念
2、tcp通信--心跳包概述
3、心跳包编程方法
4、心跳包示例代码
一、长连接和短连接概念
长连接(Persistent Connection)
长连接指的是客户端和服务器之间的TCP连接在一段时间内保持打开状态,可以被多次重复利用而不是每次请求都需要建立新的连接。长连接通常用于减少连接建立和断开的开销,提高通信效率,在长连接中,客户端和服务器之间的TCP连接会一直保持打开状态,直到其中一方发送FIN来关闭连接。这样的连接在某些应用场景下很有用,例如Web服务器上的HTTP长连接,数据库服务器上的持久连接等。
短连接(Non-Persistent Connection)
短连接指的是客户端和服务器之间的TCP连接在每次请求之后都会立即关闭,不会被复用。每次请求都需要重新建立一个新的连接。在短连接中,客户端和服务器之间的TCP连接在完成一次请求-响应循环后立即关闭,这种模式适用于些临时性的通信需求,如一次性的HTTP请求等。
TCP处理长连接和短连接的方式:
连接管理: 对于长连接,TCP会保持连接状态,直到一方显式关闭连接;而对于短连接,TCP会1在每次请求-响应完成后立即关闭连接。
资源消耗: 长连接会占用服务器端的资源(如内存),因为连接需要保持打开状态;而短连接在每次请求之后都会释放资源,不会长时间占用服务器端资源。
性能影响: 长连接可以减少连接建立和断开的开销,降低通信延迟,提高性能;而短连接需要频繁地建立和断开连接,可能会增加一些额外的开销和延迟。
总的来说,长连接适用于需要频繁通信的场景,可以减少连接建立和断开的开销;而短连接适用于-次性的通信需求,可以及时释放资源,避免长时间占用服务器端资源。选择长连接还是短连接取决于具体的应用场景和性能需求。
二、tcp通信---心跳包概述
在 TCP 协议只中,数据被分成多个数据包进行传输,每个数据包都有序号和确认应答机制,以保证数据的完整性和正确性。当通信双方完成数据传输后,可以通过四次挥手断开连接。如果应用程序需要保持长连接,通常需要在客户端和服务器端都设置一个超时时间,如果在超时时间内没有收到数据,则会发送心跳包来保持连接状态。
在实际应用中,例如基于 HTTP 协议的长轮询和 Websocket 协议等,都是在 TCP 协议之上实现的长连接技术。这些长连接技术可以大大减少网络连接的开销,提高数据传输的效率,适用于实时数据传输和在线游戏等对数据传输响应速度要求较高的场景。
如果客户端不发送数据请求,服务器端通常会在接收到客户端的连接请求后进入等待状态,使用recv或者recvfrom函数来接收数据。如果在指定时间内没有接收到数据,服务器可以关闭这个连接。
TCP 的 保活机制(KeepAliveQ机制)。这个机制的原理是这样的:
定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个「探测报文」,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。
心跳包
很多应用层协议只都有HeartBeat机制,通常是客户端每隔一小段时间向服务器发送一个数据包,通知服务器自己仍然在线,并传输一些可能必要的数据。使用心跳包的典型协议是IM,比如QQ/MSN/飞信等协议。
心跳包只之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。
三、心跳包编程
在Socket心跳机制中,心跳包可以由服务器发送给客户端,也可以由客户端发送给服务器,不过比较起来,前者开销可能较大。本文实现的是由客户端给服务器发送心跳包,服务器不必返回应答包,而是通过判断客户在线会话记录中的计数标志值来实现心跳异常的检测,以此决定客户端是否已经断开连接以及删除其在线会话记录。
基本思路:
①客户端定时给服务器发送心跳包(案例中定时时间为3秒);
②服务器创建一个心跳检测的线程,线程中每隔3秒对用户在线会话记录中的计数器进行加1操作(初始值为0);
③服务器每次收到客户端的心跳包后,都将其在线会话记录中的计数器清零;
④当心跳检测线程中检测到某用户计数器已经累加到数值为5时(说明已经有15秒未收到该用户心跳包),就判定该用户已经断线,并将其从会话记录中清除出去。
四、心跳包示例代码
1、客户端代码代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
/*
void *bitheart(void *arg)
{
int ret = 0;
int fd = *((int *)arg);
while(1)
{
sleep(3);
ret = send(fd,"heartbit",8,0);
if(ret < 0)
{
perror("send");
pthread_exit(NULL);
}
}
}*/
int main()
{
int fd = 0;
int ret = 0;
struct sockaddr_in addr;
pthread_t pid = 0;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 6868;
addr.sin_addr.s_addr = inet_addr("192.168.0.146");
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
perror("socket");
exit(-1);
}
ret = connect(fd,(struct sockaddr *)&addr,sizeof(struct sockaddr));
if(ret != 0)
{
perror("connect");
close(fd);
exit(-1);
}
/*
ret = pthread_create(&pid,NULL,bitheart,&fd);
if(ret != 0)
{
perror("pthread_create");
exit(-1);
}
*/
char dest[1024] = {0};
char buf[1024] = {0};
while(1)
{
printf("please input a string:\n");
scanf("%s",buf);
ret = send(fd,buf,strlen(buf),0);
if(ret <= 0)
{
perror("send");
exit(-1);
}
printf("dest is %s\n",dest);
memset(dest,0,sizeof(dest));
}
close(fd);
return 0;
}
2、服务器端代码如下:
Clientlist.h Clientlist.c
#ifndef CLIENTLIST_H
#define CLIENTLIST_H
#include <pthread.h>
struct clientfds
{
char ipaddr[16];
int fd;
int count;
pthread_t pid;
struct clientfds *next;
};
struct clientfds *create_clientfds(void);
struct clientfds *insert(struct clientfds *head,struct clientfds data);
int delete_by_fd(struct clientfds *head,int fd);
void free_clientfds(struct clientfds *head);
#endif
Clientlist.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "clientList.h"
struct clientfds *create_clientfds(void)
{
struct clientfds *head = NULL;
head = (struct clientfds *)malloc(sizeof(struct clientfds));
if(head == NULL)
{
perror("malloc error");
return NULL;
}
return head;
}
struct clientfds *insert(struct clientfds *head,struct clientfds data)
{
struct clientfds *q = NULL;
struct clientfds *p = head;
while(p->next != NULL)
{
p = p->next;
}
q = (struct clientfds *)malloc(sizeof(struct clientfds));
if(q == NULL)
{
perror("malloc error");
return NULL;
}
memset(q,0,sizeof(struct clientfds));
*q = data;
p->next = q;
q->next = NULL;
return q;
}
int delete_by_fd(struct clientfds *head,int fd)
{
struct clientfds *p = NULL;
struct clientfds *q = NULL;
q = head;
p = head->next;
while(p != NULL)
{
if(p->fd == fd)
{
q->next = p->next;
free(p);
return 1;
}
p = p->next;
q = q->next;
}
return 0;
}
void free_clientfds(struct clientfds *head)
{
struct clientfds *p = head;
while(p != NULL)
{
free(p);
p = p->next;
}
}
Server.c
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "clientList.h"
void *fun(void *arg)
{
struct clientfds *p = (struct clientfds *)arg;
int ret = 0;
char buf[1024] = {0};
while(1)
{
memset(buf,0,1024);
ret = recv(p->fd,buf,1023,0);
if(ret <= 0)
{
perror("recv");
pthread_exit(NULL);
}
if(strcmp(buf,"heartbit") == 0)
{
p->count = 0;
printf("%s 发送心跳包\n",p->ipaddr);
}else
{
printf("buf is %s\n",buf);
p->count = 0;
}
}
pthread_exit(0);
}
void *heartfun(void *arg)
{
struct clientfds *head = (struct clientfds *)arg;
struct clientfds *p = NULL;
while(1)
{
sleep(3);
p = head->next;
while(p != NULL)
{
p->count = p->count + 1;
printf("%d:%d\n",p->fd,p->count);
if(p->count == 5)
{
printf("%s is leaving\n",p->ipaddr);
close(p->fd);
pthread_cancel(p->pid);
delete_by_fd(head,p->fd);
}
p = p->next;
}
}
}
int create_socket(void)
{
int fd = 0;
int sockfd = 0;
int ret = 0;
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 6868;
addr.sin_addr.s_addr = inet_addr("192.168.0.146");
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
perror("socket");
exit(-1);
}
ret = bind(fd,(struct sockaddr *)&addr,sizeof(struct sockaddr));
if(ret < 0)
{
perror("bind");
exit(-1);
}
ret = listen(fd,5);
if(ret < 0)
{
perror("listen");
exit(-1);
}
return fd;
}
int main()
{
int fd = 0;
int len = 0;
struct sockaddr_in cliaddr;
int sockfd = 0;
struct clientfds *head = NULL;
struct clientfds *p = NULL;
struct clientfds data;
memset(&data,0,sizeof(data));
memset(&cliaddr,0,sizeof(cliaddr));
len = sizeof(struct sockaddr);
memset(&data,0,sizeof(data));
fd = create_socket();
head = create_clientfds();
pthread_t pid = 0;
pthread_t pid_heart = 0;
int ret = 0;
ret = pthread_create(&pid_heart,NULL,heartfun,head);
if(ret != 0)
{
perror("pthread_create error");
exit(-1);
}
while(1)
{
sockfd = accept(fd,(struct sockaddr*)&cliaddr,&len);
if(sockfd < 0)
{
perror("accept");
exit(-1);
}
data.fd = sockfd;
strcpy(data.ipaddr,inet_ntoa(cliaddr.sin_addr));
data.count = 0;
p = insert(head,data);
ret = pthread_create(&(p->pid),NULL,fun,p);
if(ret != 0)
{
perror("pthread_create");
exit(-1);
}
}
close(fd);
return 0;
}
3、参考代码gitee地址
https://gitee.com/huamanlou32/heartbit.git
2025年12月04日 03点12分
1
1、长连接和短连接概念
2、tcp通信--心跳包概述
3、心跳包编程方法
4、心跳包示例代码
一、长连接和短连接概念
长连接(Persistent Connection)
长连接指的是客户端和服务器之间的TCP连接在一段时间内保持打开状态,可以被多次重复利用而不是每次请求都需要建立新的连接。长连接通常用于减少连接建立和断开的开销,提高通信效率,在长连接中,客户端和服务器之间的TCP连接会一直保持打开状态,直到其中一方发送FIN来关闭连接。这样的连接在某些应用场景下很有用,例如Web服务器上的HTTP长连接,数据库服务器上的持久连接等。
短连接(Non-Persistent Connection)
短连接指的是客户端和服务器之间的TCP连接在每次请求之后都会立即关闭,不会被复用。每次请求都需要重新建立一个新的连接。在短连接中,客户端和服务器之间的TCP连接在完成一次请求-响应循环后立即关闭,这种模式适用于些临时性的通信需求,如一次性的HTTP请求等。
TCP处理长连接和短连接的方式:
连接管理: 对于长连接,TCP会保持连接状态,直到一方显式关闭连接;而对于短连接,TCP会1在每次请求-响应完成后立即关闭连接。
资源消耗: 长连接会占用服务器端的资源(如内存),因为连接需要保持打开状态;而短连接在每次请求之后都会释放资源,不会长时间占用服务器端资源。
性能影响: 长连接可以减少连接建立和断开的开销,降低通信延迟,提高性能;而短连接需要频繁地建立和断开连接,可能会增加一些额外的开销和延迟。
总的来说,长连接适用于需要频繁通信的场景,可以减少连接建立和断开的开销;而短连接适用于-次性的通信需求,可以及时释放资源,避免长时间占用服务器端资源。选择长连接还是短连接取决于具体的应用场景和性能需求。
二、tcp通信---心跳包概述
在 TCP 协议只中,数据被分成多个数据包进行传输,每个数据包都有序号和确认应答机制,以保证数据的完整性和正确性。当通信双方完成数据传输后,可以通过四次挥手断开连接。如果应用程序需要保持长连接,通常需要在客户端和服务器端都设置一个超时时间,如果在超时时间内没有收到数据,则会发送心跳包来保持连接状态。
在实际应用中,例如基于 HTTP 协议的长轮询和 Websocket 协议等,都是在 TCP 协议之上实现的长连接技术。这些长连接技术可以大大减少网络连接的开销,提高数据传输的效率,适用于实时数据传输和在线游戏等对数据传输响应速度要求较高的场景。
如果客户端不发送数据请求,服务器端通常会在接收到客户端的连接请求后进入等待状态,使用recv或者recvfrom函数来接收数据。如果在指定时间内没有接收到数据,服务器可以关闭这个连接。
TCP 的 保活机制(KeepAliveQ机制)。这个机制的原理是这样的:
定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个「探测报文」,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。
心跳包
很多应用层协议只都有HeartBeat机制,通常是客户端每隔一小段时间向服务器发送一个数据包,通知服务器自己仍然在线,并传输一些可能必要的数据。使用心跳包的典型协议是IM,比如QQ/MSN/飞信等协议。
心跳包只之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。
三、心跳包编程
在Socket心跳机制中,心跳包可以由服务器发送给客户端,也可以由客户端发送给服务器,不过比较起来,前者开销可能较大。本文实现的是由客户端给服务器发送心跳包,服务器不必返回应答包,而是通过判断客户在线会话记录中的计数标志值来实现心跳异常的检测,以此决定客户端是否已经断开连接以及删除其在线会话记录。
基本思路:
①客户端定时给服务器发送心跳包(案例中定时时间为3秒);
②服务器创建一个心跳检测的线程,线程中每隔3秒对用户在线会话记录中的计数器进行加1操作(初始值为0);
③服务器每次收到客户端的心跳包后,都将其在线会话记录中的计数器清零;
④当心跳检测线程中检测到某用户计数器已经累加到数值为5时(说明已经有15秒未收到该用户心跳包),就判定该用户已经断线,并将其从会话记录中清除出去。
四、心跳包示例代码
1、客户端代码代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
/*
void *bitheart(void *arg)
{
int ret = 0;
int fd = *((int *)arg);
while(1)
{
sleep(3);
ret = send(fd,"heartbit",8,0);
if(ret < 0)
{
perror("send");
pthread_exit(NULL);
}
}
}*/
int main()
{
int fd = 0;
int ret = 0;
struct sockaddr_in addr;
pthread_t pid = 0;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 6868;
addr.sin_addr.s_addr = inet_addr("192.168.0.146");
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
perror("socket");
exit(-1);
}
ret = connect(fd,(struct sockaddr *)&addr,sizeof(struct sockaddr));
if(ret != 0)
{
perror("connect");
close(fd);
exit(-1);
}
/*
ret = pthread_create(&pid,NULL,bitheart,&fd);
if(ret != 0)
{
perror("pthread_create");
exit(-1);
}
*/
char dest[1024] = {0};
char buf[1024] = {0};
while(1)
{
printf("please input a string:\n");
scanf("%s",buf);
ret = send(fd,buf,strlen(buf),0);
if(ret <= 0)
{
perror("send");
exit(-1);
}
printf("dest is %s\n",dest);
memset(dest,0,sizeof(dest));
}
close(fd);
return 0;
}
2、服务器端代码如下:
Clientlist.h Clientlist.c
#ifndef CLIENTLIST_H
#define CLIENTLIST_H
#include <pthread.h>
struct clientfds
{
char ipaddr[16];
int fd;
int count;
pthread_t pid;
struct clientfds *next;
};
struct clientfds *create_clientfds(void);
struct clientfds *insert(struct clientfds *head,struct clientfds data);
int delete_by_fd(struct clientfds *head,int fd);
void free_clientfds(struct clientfds *head);
#endif
Clientlist.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "clientList.h"
struct clientfds *create_clientfds(void)
{
struct clientfds *head = NULL;
head = (struct clientfds *)malloc(sizeof(struct clientfds));
if(head == NULL)
{
perror("malloc error");
return NULL;
}
return head;
}
struct clientfds *insert(struct clientfds *head,struct clientfds data)
{
struct clientfds *q = NULL;
struct clientfds *p = head;
while(p->next != NULL)
{
p = p->next;
}
q = (struct clientfds *)malloc(sizeof(struct clientfds));
if(q == NULL)
{
perror("malloc error");
return NULL;
}
memset(q,0,sizeof(struct clientfds));
*q = data;
p->next = q;
q->next = NULL;
return q;
}
int delete_by_fd(struct clientfds *head,int fd)
{
struct clientfds *p = NULL;
struct clientfds *q = NULL;
q = head;
p = head->next;
while(p != NULL)
{
if(p->fd == fd)
{
q->next = p->next;
free(p);
return 1;
}
p = p->next;
q = q->next;
}
return 0;
}
void free_clientfds(struct clientfds *head)
{
struct clientfds *p = head;
while(p != NULL)
{
free(p);
p = p->next;
}
}
Server.c
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "clientList.h"
void *fun(void *arg)
{
struct clientfds *p = (struct clientfds *)arg;
int ret = 0;
char buf[1024] = {0};
while(1)
{
memset(buf,0,1024);
ret = recv(p->fd,buf,1023,0);
if(ret <= 0)
{
perror("recv");
pthread_exit(NULL);
}
if(strcmp(buf,"heartbit") == 0)
{
p->count = 0;
printf("%s 发送心跳包\n",p->ipaddr);
}else
{
printf("buf is %s\n",buf);
p->count = 0;
}
}
pthread_exit(0);
}
void *heartfun(void *arg)
{
struct clientfds *head = (struct clientfds *)arg;
struct clientfds *p = NULL;
while(1)
{
sleep(3);
p = head->next;
while(p != NULL)
{
p->count = p->count + 1;
printf("%d:%d\n",p->fd,p->count);
if(p->count == 5)
{
printf("%s is leaving\n",p->ipaddr);
close(p->fd);
pthread_cancel(p->pid);
delete_by_fd(head,p->fd);
}
p = p->next;
}
}
}
int create_socket(void)
{
int fd = 0;
int sockfd = 0;
int ret = 0;
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = 6868;
addr.sin_addr.s_addr = inet_addr("192.168.0.146");
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
perror("socket");
exit(-1);
}
ret = bind(fd,(struct sockaddr *)&addr,sizeof(struct sockaddr));
if(ret < 0)
{
perror("bind");
exit(-1);
}
ret = listen(fd,5);
if(ret < 0)
{
perror("listen");
exit(-1);
}
return fd;
}
int main()
{
int fd = 0;
int len = 0;
struct sockaddr_in cliaddr;
int sockfd = 0;
struct clientfds *head = NULL;
struct clientfds *p = NULL;
struct clientfds data;
memset(&data,0,sizeof(data));
memset(&cliaddr,0,sizeof(cliaddr));
len = sizeof(struct sockaddr);
memset(&data,0,sizeof(data));
fd = create_socket();
head = create_clientfds();
pthread_t pid = 0;
pthread_t pid_heart = 0;
int ret = 0;
ret = pthread_create(&pid_heart,NULL,heartfun,head);
if(ret != 0)
{
perror("pthread_create error");
exit(-1);
}
while(1)
{
sockfd = accept(fd,(struct sockaddr*)&cliaddr,&len);
if(sockfd < 0)
{
perror("accept");
exit(-1);
}
data.fd = sockfd;
strcpy(data.ipaddr,inet_ntoa(cliaddr.sin_addr));
data.count = 0;
p = insert(head,data);
ret = pthread_create(&(p->pid),NULL,fun,p);
if(ret != 0)
{
perror("pthread_create");
exit(-1);
}
}
close(fd);
return 0;
}
3、参考代码gitee地址
https://gitee.com/huamanlou32/heartbit.git