之前参与了一个横向项目,对方要求和他们的服务端对接时,我们开发的客户端必须一直保持连接,即维护一个长连接,这样服务端可以随时对我们下发控制命令。

简介

本文主要介绍如何实现TCP的长连接维护,主要通过Python的socket模块来实现,采用的实现方式为心跳保活策略,即定期发送约定好的心跳包以维持连接不断开。

原理简介

短连接指的是开启一个socket连接,收发完数据后,立刻关闭连接。我们通常使用的TCP就是这种连接方式,其示意图和工作流程如下(Client表示客户端,Server表示服务端)。

  1. Client对Server发起连接请求;
  2. Server收到请求,双方建立连接;
  3. Client向Server发送数据;
  4. Server回应Client;
  5. 一次读写完成,此时双方任何一个都可以发起关闭连接操作;
  6. 另一方收到关闭连接后,断开本次连接。

长连接指的是开启一个socket连接,多次收发数据包直到需要的时候再关闭连接。因此,需要定期发送一个不占用数据传输的心跳包来告知服务端自己的状态以维持连接。其示意图和工作流程如下(Client表示客户端,Server表示服务端)。

  1. Client向Server发起连接请求;
  2. Server收到请求,双方建立连接;
  3. Client多次向Server发送数据(包括心跳包);
  4. Server对每个收到的数据进行回应;
  5. 长时间操作之后Client发起关闭请求;
  6. Server断开和Client的连接。

代码实现

我们约定传输的数据采用JSON格式,且报文结尾都紧跟一个换行符以方便服务端进行解析,下面时具体的代码,为这里去掉了一些具体的业务线程,只保留了简单的心跳发送和接受服务端消息的线程,以方便理解。

  • 登录设备,若连接建立则登录成功
  • 每隔固定时间向服务端发送一次心跳以保活连接
  • 每隔固定时间接受一次服务端发送过来的数据,按照约定的间隔符解析出报文以方便业务处理
import os
import socket
import time
import threading
import jsonfrom loguru import loggerclass TCPSocket(object):def __init__(self, size, ip, port):"""@param size: 报文上限大小@param ip: ip地址@param port: 端口"""self.sk = Noneself.size = sizeself.format = "utf8"self.ip_port = (ip, port)self.logger = loggerself.msg_type = ['LOGIN', 'HEART']self.login_dict = {"code": "LOGIN",}# 心跳频率self.heart_interval = 5self.status_interval = 5self.adapt_time = False# 建立socket连接def connect(self):self.sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)try:self.sk.connect(self.ip_port)except Exception as e:self.logger.error("connect to server failed,prepare to reconnect", e)self.reconnect()# 重新连接 5s/次def reconnect(self):self.logger.info("try to reconnect")while True:try:self.sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.sk.connect(self.ip_port)self.login_send()self.logger.info('client start connect to host/port:{}'.format(self.ip_port))breakexcept ConnectionRefusedError:self.logger.error('socket server refused or not started, reconnect to server in 5s .... host/port:{}'.format(self.ip_port))time.sleep(5)except Exception as e:self.logger.error('do connect error:{}'.format(str(e)))time.sleep(5)self.logger.info("reconnect successfully!!!")# 发送登录验证def login_send(self):try:login_msg = self.build_request_json("LOGIN", **self.login_dict)self.sk.send(login_msg.encode(self.format))self.logger.info("[tcp client] send logon message:{}".format(login_msg.replace(os.linesep, "")))except socket.error:self.logger.info('socket error,do reconnect')time.sleep(5)except Exception as e:self.logger.error(e)time.sleep(5)def rec(self):while True:try:message = self.sk.recv(self.size)messages = self.parse_response_json(message)if messages:for msg in messages:if msg['code'] == "LOGIN":# 登录成功self.logger.info("[tcp client] receive logon response: {}".format(msg))elif msg['code'] == "HEART":# 收到心跳反馈self.logger.info("[tcp client] receive heart response: {}".format(msg))else:self.logger.warning("message queue is not supported!!!")else:self.logger.info("no message from server or messages are not valid:{}".format(messages))except socket.error as e:self.logger.error(e)time.sleep(5)self.reconnect()except Exception as e:self.logger.error(e)time.sleep(5)# 间隔固定时间发送心跳def heartbeats(self):while True:try:msg = self.build_request_json("HEART")self.sk.send(msg.encode(self.format))self.logger.info("[tcp client] send heart message:{}".format(msg.replace(os.linesep, "")))except socket.error:self.logger.error('socket error,do reconnect')self.reconnect()except Exception as e:self.logger.error('other error occur', e)time.sleep(5)self.reconnect()time.sleep(self.heart_interval)@staticmethoddef build_request_json(method: str, **args) -> str:""":param method: 该请求的方法类型:return: 构建好的用于和服务端通信的Json数据"""if method == "LOGIN":json_data = {"code": "LOGIN",}elif method == "HEART":json_data = {"code": "HEART",}else:print("this method {} is not supported now!!!".format(method))json_data = Nonereturn json.dumps(json_data) + os.linesep if json_data else Nonedef parse_response_json(self, data: bytes):msgs = []try:data_list = data.decode(self.format).split(os.linesep)data_list = list(filter(lambda x: x.strip().startswith("{"), data_list))for msg in data_list:msg = json.loads(msg)if msg['code'] in self.msg_type:msgs.append(msg)return msgsexcept Exception as e:self.logger.error(e)return Noneif __name__ == '__main__':socket1 = TCPSocket(1024, "127.0.0.1", 5433)socket1.connect()socket1.login_send()t1 = threading.Thread(target=socket1.rec)t2 = threading.Thread(target=socket1.heartbeats)t1.start()t2.start()

服务端的代码正常实现即可,这里就不贴了。

总结

本文简单介绍了如何使用Python实现基于心跳保活的TCP长连接。

Python实现心跳保活TCP长连接相关推荐

  1. Python+socket完美实现TCP长连接保持存活

    推荐教材: <Python程序设计(第2版)>,ISBN:978-7-302-43651-5,董付国,清华大学出版社,第17次印刷,清华大学出版社2019年度畅销图书 图书详情: 配套资源 ...

  2. TCP长连接与NAT超时

    TCP长连接 TCP连接建立后只要不关闭,逻辑上连接一直存在. TCP是有保活定时器的,可以打开保活定时器来维持长连接,设置SO_KEEPALIVE才会开启,时间间隔默认7200s,也就是2h,这个默 ...

  3. TCP握手机制、TCP长连接和短连接、TCP 保活机制 、心跳机制

    参考: https://www.cnblogs.com/Andya/p/7272462.html 1. TCP连接(3次握手建立连接.4次挥手关闭连接) 当网络通信时采用TCP协议时,在真正的读写操作 ...

  4. 聊聊 TCP 长连接和心跳那些事

    1 前言 可能很多 Java 程序员对 TCP 的理解只有一个三次握手,四次挥手的认识,我觉得这样的原因主要在于 TCP 协议本身稍微有点抽象(相比较于应用层的 HTTP 协议):其次,非框架开发者不 ...

  5. tcp长连接和保活时间

    tcp长连接和保活时间 TCP协议中有长连接和短连接之分.短连接在数据包发送完成后就会自己断开,长连接在发包完毕后,会在一定的时间内保持连接,即我们通常所说的Keepalive(存活定时器)功能.   ...

  6. TCP长连接与短连接、心跳机制

    转自: 1. TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是 ...

  7. TCP长连接,心跳机制介绍

    TCP长连接,心跳机制介绍 长连接 为何要长连接 心跳 心跳为何设置在服务器端 心跳维持长连接 TCP keep-alive的三个参数 参数的具体意义 心跳的使用场景 长连接 TCP经过三次握手建立连 ...

  8. python使用socket实现协议TCP长连接框架

    点击上方↑↑↑蓝字[协议分析与还原]关注我们 " 使用python实现协议中常见的TCP长连接框架." 分析多了协议就会发现,很多的应用,特别是游戏类和IM类应用,它们的协议会使用 ...

  9. TCP长连接和短连接

    2019独角兽企业重金招聘Python工程师标准>>> 1. TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操 ...

最新文章

  1. (ql)30W单片精密开关电源 电路图加分析
  2. Android 数据存储 Room
  3. python考级证书-Python 全国考级二级
  4. UA SIE545 优化理论基础1 例题1 常见的凸集
  5. 【安全漏洞】Cisco命令注入漏洞CVE-2021-1414分析
  6. android 下载进度条代码实现,Android 文件下载进度条的实现
  7. 开发工具使用技巧和插件大总结
  8. linux怎么64位,在linux下如何查看CPU是否支持64位
  9. qt web混合编程_VS2017+QT 混合编程-在VS控制台程序配置QT
  10. 含有百分数的简便运算_青岛版三年级上册数学6.1不含括号的混合运算(一)微课知识点精讲+练习...
  11. 隐藏网络计算机,如何在网络中隐藏自己的计算机名称
  12. 003.ASP.NET MVC集中管理Session
  13. 工厂设计模式究竟怎么写更优雅?!
  14. colab配合谷歌云盘使用
  15. html 密码不小于六位怎么设置,192.168.1.1登录入口要六位密码是多少?
  16. 播放超1200w,仅21w粉的B站UP主靠带货也能引爆B站
  17. python数字时钟
  18. 【高数学习笔记】2.一元函数微分学
  19. 算法:合唱队形(最大上升子序列,线性DP)
  20. JavaScript中的倒叙和排序

热门文章

  1. Zookeeper基于Java 访问
  2. shouldParkAfterFailedAcquire
  3. 销毁Bean的基本操作有哪些?
  4. request获得请求行的内容
  5. 方法入门_方法的调用
  6. 数据库-左外连接-右外连接
  7. 创建consumer服务
  8. Spring七中传播行为详解
  9. FastDFS_install_document_detail
  10. 使用Docker运行java项目需要注意的glibc依赖库问题