转:关于SO_LINGER的使用说明

本文详细介绍了SO_LINGER套接字选项及其对TCP连接关闭行为的影响。通过解释linger结构的两个字段l_onoff和l_linger的作用,展示了三种不同的关闭方式,并讨论了它们对数据处理的不同影响。

SO_LINGER

   此选项指定函数close对面向连接的协议如何操作(如TCP)。缺省close操作是立即返回,如果有数据残留在套接口缓冲区中则系统将试着将这些数据发送给对方。

SO_LINGER选项用来改变此缺省设置。使用如下结构:

struct linger {

     int l_onoff; /* 0 = off, nozero = on */

     int l_linger; /* linger time */

};

有下列三种情况:

l_onoff为0,则该选项关闭,l_linger的值被忽略,等于缺省情况,close立即返回;

l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折连接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,而不是通常的四分组终止序列,这避免了TIME_WAIT状态;

l_onoff 为非0,l_linger为非0,当套接口关闭时内核将拖延一段时间(由l_linger决定)。如果套接口缓冲区中仍残留数据,进程将处于睡眠状态,直 到(a)所有数据发送完且被对方确认,之后进行正常的终止序列(描述字访问计数为0)或(b)延迟时间到。此种情况下,应用程序检查close的返回值是非常重要的,如果在数据发送完并被确认前时间到,close将返回EWOULDBLOCK错误且套接口发送缓冲区中的任何数据都丢失close的成功返回仅告诉我们发送的数据(和FIN)已由对方TCP确认,它并不能告诉我们对方应用进程是否已读了数据。如果套接口设为非阻塞的,它将不等待close完 成。

l_linger的单位依赖于实现,4.4BSD假设其单位是时钟滴答(百分之一秒),但Posix.1g规定单位为秒。

转载于:https://www.cnblogs.com/huking/archive/2009/12/24/1631746.html

import socket import struct import time # 定义要发送的数据的长度(32KB - 2个字节的起始值) data_length = 4095-3 # 将0x03和0x36分别打包为两个字节 byte1 = struct.pack('>B', 0x2E) # 打包0x03为大端字节序 byte2 = struct.pack('>B', 0xF0) # 打包0x36为大端字节序 byte3 = struct.pack('>B', 0x11) # 打包0x03为大端字节序 # 合并这两个字节 start_bytes = byte1 + byte2 + byte3 # 创建一个全为0的字节串,长度为data_length zero_bytes = b'\x00' * data_length # 将起始字节和填充的字节合并 full_data = start_bytes + zero_bytes # 配置ECU的IP地址和端口 ecu_ip = '192.168.69.8' ecu_port = 13400 bind_ip = '192.168.69.71' def setup_socket(): """创建并配置TCP socket""" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(30) # 设置接收超时 # 关键配置:防止发送FIN/RST sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 0, 0)) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) return sock # 构造DoIP路由激活请求 def create_doip_routing_activation_request(source_address, activation_type, extra_data): """ 构造DoIP路由激活请求报文 :param source_address: 客户端逻辑地址 (2字节) :param activation_type: 激活类型 (1字节) :param extra_data: 额外数据 (4字节) :return: DoIP路由激活请求报文 """ # DoIP头部 protocol_version = 0x03 # DoIP协议版本 inverse_protocol_version = 0xFC # 协议版本的反码 payload_type = 0x0005 # 路由激活请求的类型 payload_length = 0x0000000B # Payload长度为11字节 # Payload部分 source_address_bytes = struct.pack('>H', source_address) # 客户端逻辑地址 activation_type_byte = struct.pack('>B', activation_type) # 激活类型 reserved_bytes = b'\x00\x00\x00\x00' # 保留字段 extra_data_bytes = extra_data # 额外数据 # 构造完整的DoIP消息 doip_message = ( struct.pack('>B', protocol_version) + struct.pack('>B', inverse_protocol_version) + struct.pack('>H', payload_type) + struct.pack('>I', payload_length) + source_address_bytes + activation_type_byte + reserved_bytes + extra_data_bytes ) return doip_message # 构造DoIP诊断请求 def create_doip_diagnostic_request(source_address, target_address, diagnostic_data): """ 构造DoIP诊断请求报文 :param source_address: 客户端逻辑地址 (2字节) :param target_address: 目标ECU逻辑地址 (2字节) :param diagnostic_data: 诊断数据 (UDS请求) :return: DoIP诊断请求报文 """ # DoIP头部 protocol_version = 0x03 # DoIP协议版本 inverse_protocol_version = 0xFC # 协议版本的反码 payload_type = 0x8001 # 诊断消息的类型 payload_length = 4 + len(diagnostic_data) # Payload长度 = 4字节地址 + 诊断数据长度 # Payload部分 source_address_bytes = struct.pack('>H', source_address) # 客户端逻辑地址 target_address_bytes = struct.pack('>H', target_address) # 目标ECU逻辑地址 diagnostic_data_bytes = diagnostic_data # 诊断数据 # 构造完整的DoIP消息 doip_message = ( struct.pack('>B', protocol_version) + struct.pack('>B', inverse_protocol_version) + struct.pack('>H', payload_type) + struct.pack('>I', payload_length) + source_address_bytes + target_address_bytes + diagnostic_data_bytes ) return doip_message # 发送DoIP路由激活请求 source_address = 0x0F01 # 客户端逻辑地址 activation_type = 0x00 # 默认激活类型 extra_data = b'\xFF\xFF\xFF\xFF' # 自定义的4字节额外数据 doip_activation_request = create_doip_routing_activation_request(source_address, activation_type, extra_data) def monitor_alive_check(sock): """监听Alive Check消息""" print("开始监听Alive Check消息...") try: while True: try: # 设置非阻塞接收以避免长时间卡住 sock.settimeout(1.0) data = sock.recv(1024) if data: print(f"接收到数据: {data.hex()}") # 检查是否是Alive Check (Payload Type 0x0008) if len(data) >= 8 and data[2:4] == b'\x00\x08': print("检测到Alive Check消息") last_alive = time.time() else: print("连接已关闭") break except socket.timeout: # 正常超时,继续监听 continue except Exception as e: print(f"接收错误: {str(e)}") break except KeyboardInterrupt: print("用户中断监听") finally: # 不关闭socket,直接退出 print("保持连接打开状态退出") def main(): sock = setup_socket() try: sock.bind((bind_ip, 0)) # 随机端口 sock.connect((ecu_ip, ecu_port)) print(f"已连接到 {ecu_ip}:{ecu_port}") # 发送路由激活请求 sock.send(doip_activation_request) print(f"Sent DoIP Routing Activation Request: {doip_activation_request.hex()}") # 接收路由激活响应 activation_response = sock.recv(1024) print(f"Received DoIP Routing Activation Response: {activation_response.hex()}") # 发送DoIP诊断请求 target_address = 0x32A # 目标ECU逻辑地址 diagnostic_data = b'\x22\xF0\x11' # 示例诊断请求 (UDS请求: 0x10 0x01) doip_diagnostic_request = create_doip_diagnostic_request(source_address, target_address, full_data) #doip_diagnostic_request = create_doip_diagnostic_request(source_address, target_address, diagnostic_data) # 发送诊断请求 sock.send(doip_diagnostic_request) print(f"Sent DoIP Diagnostic Request: {doip_diagnostic_request.hex()}") # 接收诊断响应 diagnostic_response = sock.recv(1024) print(f"Received DoIP Diagnostic Response: {diagnostic_response.hex()}") # 开始监听Alive Check monitor_alive_check(sock) except Exception as e: print(f"发生错误: {str(e)}") finally: # 不调用close(),保持连接打开 print("程序退出,socket保持打开状态") # 如果需要完全避免任何关闭行为,可以取消下面这行注释 # os._exit(0) if __name__ == "__main__": main() 解释代码
最新发布
07-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值