会。UTL_TCP本身不丢包,但应用层未flush、接收端缓冲区溢出、发送超限截断、空闲断连或协议解析错误等,会导致数据“看似丢失”。
utl_tcp 不会丢包,但你的应用层逻辑、网络配置或接收端处理不当,会让它“看起来”丢了。
为什么 UTL_TCP.send\_text 看似丢包
Oracle 的 UTL_TCP 是对底层 TCP socket 的 PL/SQL 封装,它本身不实现重传、分片或拥塞控制——这些都由操作系统内核的 TCP 协议栈完成。所谓“丢包”,99% 情况下不是 UTL_TCP.send_text 返回成功却没发出去,而是:
- 调用
UTL_TCP.send_text后立即调用UTL_TCP.close_connection,导致内核发送缓冲区里还没来得及推走的数据被强制丢弃(TCP FIN 时未等缓冲区清空) - 接收端(如 Python/Java 服务)未及时
recv,socket 接收缓冲区溢出,后续包被内核静默丢弃(netstat -s | grep "packet receive errors"可见UdpInErrors或TcpExtListenOverflows) - 发送数据量大且未分块,单次
send_text超过 OS socket 发送缓冲区(net.core.wmem_max),函数虽返回字节数,但实际只拷入缓冲区部分,剩余截断(UTL_TCP不报错也不重试) - 防火墙/NAT 设备对长连接空闲超时(如 5 分钟),中间断连,而 Oracle 侧仍认为连接有效,下次
send_text报ORA-29260: network error: TNS:connection closed或直接无响应
如何安全调用 UTL_TCP.send_text
关键不是“怎么发”,而是“怎么确认发完 + 让对方收稳”。必须配合显式 flush 和合理等待:
- 每次
UTL_TCP.send_text后,**立刻跟一句UTL_TCP.flush(c)**(c是 connection 变量),否则数据可能卡在 PL/SQL 层缓冲或内核发送队列中 - 若发送多段数据,避免高频小包:合并为单次
send_text(≤ 8192 字节较稳妥),或手动 sleep 10–50ms 再发下一条(防止突发打满对方接收缓冲区) - 不要依赖
UTL_TCP.write_line自动换行作为协议分界——它只是加\n,对方若按行读取但网络粘包,就会漏数据;应自定义长度头或固定分隔符,并在接收端严格解析 - 发送完毕后,**不要马上
close_connection**;先UTL_TCP.write_text(c, 'END')发结束标记,再flush,最后close_connection
接收端必须适配 UTL_TCP 的行为
Oracle 用 UTL_TCP 发出的数据,是标准 TCP 流,但默认没有应用层帧界定。接收端若简单 recv(1024),极易读到半包或跨包内容:
- Python 示例中,不能写
s.recv(4096).decode()就完事;应循环读直到收到完整消息(比如约定首 4 字节为长度,先读够 4 字节再读指定长度) - Java 使用
DataInputStream.readUTF()前,必须确保 Oracle 端用UTL_TCP.write_raw+ 手动写 2 字节长度 + UTF-8 字节,否则会抛UTFDataFormatException - Linux 下用
nc -l 8080测试时,会发现部分消息“消失”——因为nc默认行缓冲,且不处理半包;换成socat TCP4-LISTEN:8080,reuseaddr -更可靠 - 检查接收端 socket 选项:
SO_RCVBUF是否足够(sysctl net.core.rmem_max)、是否启用了TCP_NODELAY(禁 Nagle,减少延迟,但小包增多)
真正要查丢包,绕不开系统层指标
别只盯着 PL/SQL 日志。当怀疑丢包时,优先在数据库服务器和接收服务器上运行:
-
ss -i sport = :your_port查看该连接的retrans(重传次数)、rto(重传超时)、qsize(发送队列积压) -
netstat -s | grep -A 5 -B 5 "retransmit\|drop\|overflow"找TcpRetransSegs、TcpExtTCPBacklogDrop、TcpExtListenDrops - 用
tcpdump -i any port your_port -w utl_tcp.pcap抓包,Wireshark 中过滤tcp.analysis.retransmission || tcp.analysis.lost_segment
如果 ss 显示 retrans > 0 或 tcpdump 真有重传包,说明是网络路径问题(中间设备丢包、链路拥塞),和 UTL_TCP 无关;如果一切正常但接收端仍少数据,那一定是应用层协议没对齐——比如 Oracle 发了 10 行,接收端只调了一次 recv 就关闭了连接。


















