Python 建立TCP连接,并接收Jmeter发送过来的xml报文
前言
TCP连接对于一个客户端程序来说,建立一个socket需要两个步骤。
首先,您需要建立一个实际的socket对象。其次,您需要把它连接到远程服务器上。
在建立socket对象的时候,您需要告诉系统两件事:通信类型和协议家族。通信类型指明用什么协议来传输数据。协议的例子包括IPv4(当前的Internet标准),IPv6(将来的Internet标准),IPX/SPX(NetWare)和AFP(Apple文件共享)。到目前为止最通用的是IPv4。
协议家族则定义数据如何被传输,通信类型基本上都是:AF_INET(和IPv4对应)。协议家族表示TCP通信的SOCK_STREAM和表示UDP通信的SOCK_DGRAM
一、Socket
套接字模块是一个非常简单的基于对象的接口,它提供对低层BSD套接字样式网络的访问。使用该模块可以实现客户机和服务器套接字。要在python 中建立具有TCP和流套接字的简单服务器,需要使用socket模块。利用该模块包含的函数和类定义,可生成通过网络通信的程序
二、使用步骤
1.安装并引入库
pip install socket
import socket
2.封装建立socket连接的方法
def socket_connet(ip,port):
sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 实例化一个基于tcp的socket对象
sk.bind((ip, port)) # 使用ip port建立连接
sk.listen(5) # 设置监听
return sk
3.封装接收xml报文的方法
def get_xml_message(client,log):
# 先接收20字符
recv_front_20 = client.recv(20)
while len(recv_front_20) < 20:
recv_front_20 = recv_front_20 + client.recv(20)
log.info("前20字符: " + str(recv_front_20))
# 正则匹配出报文长度
recv_front_20_num = re.match(r'^(.*)<', recv_front_20.decode('utf-8'))
message_len = recv_front_20_num.group(1)
message_len = re.findall('\d+', message_len)[0]
message_len = message_len[-6:]
log.debug("报文长度: " + str(message_len))
# 报文中除了长度还可能有项目标识 例如:CBS 001034
string_len = re.match(r'(.+(?=<))', recv_front_20.decode('utf-8'))
log.debug('string_len: ' + str(string_len))
frame_len = string_len.group(1)
log.debug("实际报文长度: " + frame_len)
total_data = recv_front_20
total_len = 20
if (message_len.isdigit()):
# 最终的报文长度=报文实际长度+长度所占字节的长度
iFrameLen = int(message_len) + len(frame_len)
log.info("报文总长度: " + str(message_len) + " + " + str(len(frame_len)) + " = " + str(iFrameLen))
while total_len < iFrameLen:
# 读报文
frameData = client.recv(1024)
log.debug("-----先读取1024字节: " + str(frameData))
# 拼接
total_data = total_data + frameData
log.debug("-----前20内容+获取1024内容:" + str(total_data))
# 获取长度
total_len = len(total_data)
log.debug("-----获取后的总长度:" + str(total_len))
else:
log.error("正则抓取到的报文长度不是纯数字,请排查!")
frame_data = total_data[len(frame_len):]
# 设置编码格式
frame_data = frame_data.decode('utf-8', 'ignore')
# 判断报文是否为空
if frame_data == '':
log.error('报文不能为空 !')
exit()
log.info("收取到的报文:\n {}".format(frame_data))
# xml转bytes 方便etree处理
xml = bytes(bytearray(frame_data, encoding='utf-8'))
# 格式化转换xml报文 为以后xpath提供方便
xml_data = etree.XML(xml)
log.debug('xml_data: ' + str(xml_data))
return xml_data
4 启动程序
if __name__ == '__main__':
# 初始化日志
log = get_log('test.log','info')
ip='127.0.0.1'
port=9996
sk = socket_connet(ip,port)
# 持续接收
while True:
client = sk.accept()
if client: # 监听到有接收,则调用接收xml的方法
xml_data = get_xml_message(client,log) # get_log 是封装的日志方法
封装通用的日志模块
def get_log( filename, level,when='MIDNIGHT', backupCount=3, maxBytes=10000000, fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
level = __level_dict.get(level.lower(), None)
logger = logging.getLogger(filename) # 设置日志名称
format_str = logging.Formatter(fmt) # 设置日志格式
logger.setLevel(level) # 设置日志级别
console_handler = logging.StreamHandler() # 控制台输出
console_handler.setFormatter(format_str) # 控制台输出的格式
logger.addHandler(console_handler) # 控制台输出
file_handler = handlers.RotatingFileHandler(filename=filename, maxBytes=maxBytes, backupCount=backupCount, encoding='utf-8') # 文件输出
file_handler.setFormatter(format_str) # 文件输出格式
logger.addHandler(file_handler) # 日志输出
return logger