使用Python simplefix+Socket维持FIX会话:解决返回None错误
如何维持FIX会话连接
我用Python的simplefix与socket模块开发FIX客户端,发送Logon登录报文和行情数据请求后,可正常接收所需行情约5分钟,之后持续返回'none'错误。如何维持FIX会话连接?
发送代码
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) s.sendall(logon()) print(parse(s.recv(4096), 50)) s.sendall(market_data()) while True: print(s.recv(4096))
服务端响应
8=FIX.4.4|9=106|35=A|34=1|49=CSERVER|50=QUOTE|52=20230128-21:45:28.927|56=demo.roboforex.6183532|57=any|98=0|108=30|141=Y|10=228 b'8=FIX.4.4\x019=135\x0135=W\x0134=2\x0149=CSERVER\x0150=QUOTE\x0152=20230128-21:45:28.988\x0156=demo.roboforex.6183532\x0157=any\x0155=1\x01268=2\x01269=0\x01270=1.08677\x01269=1\x01270=1.08678\x0110=075\x01' b'8=FIX.4.4\x019=88\x0135=0\x0134=3\x0149=CSERVER\x0150=QUOTE\x0152=20230128-21:45:59.917\x0156=demo.roboforex.6183532\x0157=any\x0110=107\x01' b'8=FIX.4.4\x019=97\x0135=1\x0134=4\x0149=CSERVER\x0150=QUOTE\x0152=20230128-21:46:14.917\x0156=demo.roboforex.6183532\x0157=any\x01112=TEST\x0110=119\x01' none none none none none none none none none none none none none none none none none none none none none none
解决方案
1. 严格处理心跳机制
从服务端返回的Logon报文(35=A)可见108=30,这表示会话心跳间隔为30秒。你需要:
- 每隔30秒主动发送心跳报文(35=0)
- 监听服务端发来的心跳请求,及时回复心跳
- 超过心跳间隔1.5倍时间未收到对方报文时,触发连接检查逻辑
2. 维护消息序号连续性
每个FIX报文的34字段是递增的消息序号,必须:
- 本地维护发送序号,每次发报文后序号+1
- 接收服务端报文时验证序号连续性,出现断层则排查连接状态
3. 正确处理Socket连接断开
当recv()返回空字节串或None时,说明服务端已关闭连接,此时需要:
- 关闭当前socket
- 重新发起连接并发送Logon报文重建会话
- 恢复行情数据请求流程
4. 优化报文解析逻辑
当前直接用recv(4096)可能导致报文粘包或截断,建议用simplefix的解析器逐段处理:
import simplefix import socket import time def main(): HOST = "你的服务器地址" PORT = 你的端口号 fix_parser = simplefix.FixParser() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) # 发送Logon报文(确保logon函数设置了108=30和141=Y) logon_msg = logon() s.sendall(logon_msg) # 解析Logon响应 data = s.recv(4096) fix_parser.append_buffer(data) while True: msg = fix_parser.get_message() if msg: print(parse(msg, 50)) break # 发送行情请求 s.sendall(market_data()) send_seq = 2 # 已发送Logon(1)和行情请求(2) last_heartbeat_time = time.time() heartbeat_interval = 30 # 与Logon报文中的108保持一致 while True: # 处理接收数据 data = s.recv(4096) if not data: print("连接断开,尝试重连...") s.close() # 重连流程 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) s.sendall(logon()) # 等待重连后的Logon响应 data = s.recv(4096) fix_parser.append_buffer(data) while True: msg = fix_parser.get_message() if msg: print(parse(msg, 50)) send_seq = 1 last_heartbeat_time = time.time() break continue fix_parser.append_buffer(data) while True: msg = fix_parser.get_message() if not msg: break print(msg) # 响应服务端的心跳请求(35=0) if msg.get(35) == b'0': heartbeat_msg = simplefix.FixMessage() heartbeat_msg.append_pair(8, b'FIX.4.4') heartbeat_msg.append_pair(35, b'0') heartbeat_msg.append_pair(34, str(send_seq).encode()) heartbeat_msg.append_pair(49, b'demo.roboforex.6183532') heartbeat_msg.append_pair(56, b'CSERVER') heartbeat_msg.append_pair(52, time.strftime("%Y%m%d-%H:%M:%S.%f")[:-3].encode()) s.sendall(heartbeat_msg.encode()) send_seq += 1 last_heartbeat_time = time.time() # 响应服务端的测试请求(35=1) if msg.get(35) == b'1': heartbeat_msg = simplefix.FixMessage() heartbeat_msg.append_pair(8, b'FIX.4.4') heartbeat_msg.append_pair(35, b'0') heartbeat_msg.append_pair(34, str(send_seq).encode()) heartbeat_msg.append_pair(49, b'demo.roboforex.6183532') heartbeat_msg.append_pair(56, b'CSERVER') heartbeat_msg.append_pair(52, time.strftime("%Y%m%d-%H:%M:%S.%f")[:-3].encode()) # 携带测试请求的112字段 test_req_id = msg.get(112) if test_req_id: heartbeat_msg.append_pair(112, test_req_id) s.sendall(heartbeat_msg.encode()) send_seq += 1 last_heartbeat_time = time.time() # 检查是否需要主动发送心跳 if time.time() - last_heartbeat_time > heartbeat_interval: heartbeat_msg = simplefix.FixMessage() heartbeat_msg.append_pair(8, b'FIX.4.4') heartbeat_msg.append_pair(35, b'0') heartbeat_msg.append_pair(34, str(send_seq).encode()) heartbeat_msg.append_pair(49, b'demo.roboforex.6183532') heartbeat_msg.append_pair(56, b'CSERVER') heartbeat_msg.append_pair(52, time.strftime("%Y%m%d-%H:%M:%S.%f")[:-3].encode()) s.sendall(heartbeat_msg.encode()) send_seq += 1 last_heartbeat_time = time.time() time.sleep(1) # 避免循环过度占用资源 if __name__ == "__main__": main()
5. 响应测试请求
服务端返回的报文中出现了35=1(Test Request),必须携带对应的112字段回复心跳,否则服务端会判定连接异常并断开。
内容的提问来源于stack exchange,提问作者werwer werwer
相关产品推荐
相关产品推荐

