UTL_TCP
功能描述
借助该内置包中的过程和函数,PL/SQL 应用程序可以使用 TCP/IP 与基于 TCP/IP 的外部服务器进行通信。由于许多 Internet 应用程序协议都基于 TCP/IP,所以这个包对使用 Internet 协议和电子邮件的 PL/SQL 应用程序很有用。
兼容性
Vastbase G100 V2.2版本与Oracle版本相比,暂不支持的函数或过程有:GET_LINE_NCHAR,GET_TEXT_NCHAR,SECURE_CONNECTION。
注意事项
该功能仅在数据库兼容模式为Oracle时能够使用(即初始化数据库时DBCOMPATIBILITY='A')。
UTL_TCP.OPEN_CONNECTION函数使用域名作为传入参数特性仅在Build 14及以后版本支持。
使用本内置包的前提是
系统已经安装python3。python3安装方法请参见python3环境配置。
系统已经创建plpython3u插件,需设置环境变量PYTHONHOME,包括以下内容(重启数据库生效),可参见创建plpython3u插件
UTL_TCP 类型
UTL_TCP包含两个类型:CONNECTION类型和CRLF类型。
CONNECTION 类型
Utl_tcp包与外界通信时需要包含存储通信内容的对象,需要在包中定义一个connection的类型,该类型的对象可以通过包自带的方法生成, connection类型的定义如下:
TYPE connection IS RECORD (
remote_host VARCHAR2(255), -- remote host name
remote_port PLS_INTEGER, -- remote port number
local_host VARCHAR2(255), -- local host name
local_port PLS_INTEGER, -- local port number
charset VARCHAR2(30), -- character set for on-the-wire communication
newline VARCHAR2(2), -- newline character sequence
tx_timeout PLS_INTEGER, -- transfer time-out value (in seconds)
private_sd PLS_INTEGER, -- for internal use
);
参数说明
remote_host
建立连接时远程主机的名称,当没有建立连接时为NULL。
remote_port
连接的远程主机的端口号,当没有建立连接时为NULL。
local_host
用于建立连接的本地主机名,当没有建立连接时为NULL。
local_port
用于建立连接的本地主机的端口号,当没有建立连接时为NULL。
charset
在线字符集。由于数据库中的文本消息可能以不同于网络上预期的字符集(即通信协议指定的字符集,或通信另一端规定的字符集)的字符集进行编码,文本数据库中的消息在网络上发送和接收时被转换为在线字符集或从在线字符集转换。
newline
换行符序列。
tx_timeout
在此连接中放弃读取或写入操作之前,UTL_TCP 包等待的时间(以秒为单位)。
private_sd
仅内部使用。
CRLF类型
字符序列回车换行。它是许多通信标准常用的换行符序列。 不要修改它的值。
这个包变量定义了许多 Internet 协议中常用的换行符序列。 这是 write_line() 的换行符序列的默认值,在打开连接时可以通过参数指定,覆盖默认值。默认的回撤换行的值为CRLF。
CRLF CONSTANT VARCHAR2(2 CHAR);
内置包子程序
子程序 | 描述 |
---|---|
OPEN_CONNECTION | 用以创建到其他服务的tcp/ip连接,并返回一个connection对象。 |
AVAILABLE | 确定可用于从 TCP/IP 连接读取的字节数。 它是可以立即读取而不会阻塞的字节数。 确定数据是否已准备好从连接中读取。 |
READ_RAW | 此函数在打开的连接上从服务接收二进制数据,返回实际接收到的数据字节数。 |
WRITE_RAW | 此函数将二进制消息传输到开放连接上的服务,返回实际传输的数据字节数。 |
READ_TEXT | 此函数在打开的连接上从服务接收文本数据,返回接受到的文本字节数。 |
WRITE_TEXT | 此功能在开放连接上将文本消息传输到服务,并返回写入数据的字节数。 |
READ_LINE | 此函数在打开的连接上接收来自服务的文本行。 一行以换行、回车 或 回车后跟换行结束,返回实际接收到的数据字符数。 |
WRITE_LINE | 此函数将文本行传输到打开连接上的服务。 换行符序列将在传输之前附到消息中。返回实际传输的数据字符数。 |
GET_RAW(),GET_TEXT(),GET_LINE() | 方便的读取函数形式,返回读取的数据而不是读取的数据量。 |
FLUSH | 此过程将输出缓冲区中的所有数据(如果使用缓冲区)立即传输到服务器。 |
CLOSE_CONNECTION | 关闭打开的 TCP/IP 连接。 |
CLOSE_ALL_CONNECTIONS | 关闭所有打开的 TCP/IP 连接。 |
OPEN_CONNECTION
语法格式
UTL_TCP.OPEN_CONNECTION (remote_host IN VARCHAR2,
remote_port IN PLS_INTEGER,
local_host IN VARCHAR2 DEFAULT NULL,
local_port IN PLS_INTEGER DEFAULT NULL,
in_buffer_size IN PLS_INTEGER DEFAULT NULL,
out_buffer_size IN PLS_INTEGER DEFAULT NULL,
charset IN VARCHAR2 DEFAULT NULL,
newline IN VARCHAR2 DEFAULT CRLF,
tx_timeout IN PLS_INTEGER DEFAULT NULL),
wallet_path IN VARCHAR2 DEFAULT NULL,
wallet_password IN VARCHAR2 DEFAULT NULL,
RETURN connection;
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
remote_host | in | 提供服务的主机名。 |
remote_port | in | 要连接服务的监听端口。 |
local_host | in | 本地的主机名。 |
local_port | in | 本地的端口号。 |
in_buffer_size | in | 输入缓冲区的大小。 使用输入缓冲区可以加快从服务器接收数据的执行性能。 缓冲区的适当大小取决于客户端和服务器之间的数据流以及网络状况。 0 值表示不应使用缓冲区。 NULL 值意味着调用者不关心是否使用了缓冲区。 输入缓冲区的最大大小为 32767 字节。 |
out_buffer_size | in | 输出缓冲区的大小。 使用输出缓冲区可以加快向服务器发送数据的执行性能。 适当的缓冲区大小取决于客户端和服务器之间的数据流以及网络状况。 0 值表示不应使用缓冲区。 NULL 值意味着调用者不关心是否使用了缓冲区。 输出缓冲区的最大大小为 32767 字节。 |
charset | in | 在线字符集。 由于数据库中的文本消息可能以不同于网络上预期的字符集(即通信协议指定的字符集,或通信另一端规定的字符集)的字符集进行编码,文本 数据库中的消息将在使用 read_text()、read_line()、write_text() 和 write_line() 在网络上发送和接收时转换为在线字符集或从在线字符集转换。 不需要转换时,将此参数设置为 NULL。 |
newline | in | 换行符序列。 此换行符序列附加到 write_line() API 发送的文本行 |
tx_timeout | in | 在放弃此连接中的读取或写入操作之前,UTL_TCP 包应等待的时间(以秒为单位)。 在读取操作中,如果没有数据可立即读取,则此包放弃。 在写操作中,如果输出缓冲区已满并且没有数据要在网络中发送而不被阻塞,则此包将放弃。 零 (0) 表示根本不等待。 NULL 表示永远等待。 |
wallet_path | in | 包含用于SSL/TLS的wallet的目录路径,格式为:file:directory-path。 |
wallet_password | in | 打开wallet的密码。当wallet启用自动登录时,密码可能会被设置为NULL。 |
注意事项
UTL_TCP 包打开的连接可以保持打开状态,并在共享服务器配置中从一个数据库调用传递到另一个数据库调用。但是,必须明确关闭连接。当存储连接的vastbase记录变量超出vastbase程序的范围时,连接将保持打开状态。未能关闭不需要的连接可能会导致不必要地占用本地和远程系统资源。
OPEN_CONNECTION函数从Build 14开始支持域名作为入参,详情参见示例3。
AVAILABLE
语法格式
UTL_TCP.AVAILABLE (
c IN OUT connection,
timeout IN PLS_INTEGER DEFAULT 0)
RETURN INTEGER;
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
c | in | 用于确定可供读取的数据量的 TCP 连接。 |
timeout | in | 在放弃并报告没有可用数据之前等待的时间(以秒为单位)。 零 (0) 表示根本不等待。 NULL 表示永远等待。 |
注意事项
- 连接必须已经通过调用 open_connection() 打开。
- 此函数返回的可供读取的字节数可能少于实际可用的字节数。 在某些平台上,此函数可能只返回 1,表示某些数据可用。 如果用户担心应用程序的可移植性,请假设此函数在数据可供读取时返回正值,在没有数据可用时返回 0。以下示例说明了以可移植方式使用此函数:
DECLARE
c utl_tcp.connection;
data VARCHAR2(256);
len PLS_INTEGER;
BEGIN
c := utl_tcp.open_connection(...);
LOOP
IF (utl_tcp.available(c) > 0) THEN
len := utl_tcp.read_text(c, data, 256);
ELSE
---do some other things
. . . .
END IF
END LOOP;
END;
READ_RAW
语法格式
UTL_TCP.READ_RAW (c IN OUT connection,
data IN OUT RAW,
data_len OUT INTEGER,
len IN PLS_INTEGER DEFAULT 1,
peek IN BOOLEAN DEFAULT FALSE)
RETURN INTEGER;
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
c | in | 从中接收数据的 TCP 连接 |
data | in | 收到的数据。 |
data_len | in | 实际接收到的数据字节数。 |
len | in | 要接收的数据字节数。 |
peek | in | 通常,用户希望读取数据并将其从输入队列中删除,即消费它。 在某些情况下,用户可能只想向前看数据,即偷看它,而不将其从输入队列中删除,以便在下一次调用中仍然可以读取(甚至偷看)。 要将数据保留在输入队列中,请将此标志设置为 TRUE 并在打开连接之前设置输入缓冲区。 用户可以查看的数据量(即读取但保留在输入队列中)必须小于输入缓冲区的大小。 |
注意事项
连接必须已经通过调用 open_connection() 打开。 在读取指定数量的字符或到达输入结尾之前,此函数不会返回。
如果在打开连接时设置了传输超时,则该函数等待每个数据包准备好读取,直到发生超时。 如果发生,该函数停止读取并返回所有读取成功的数据。 如果没有成功读取数据,则会引发 transfer_timeout 异常。 可以处理异常,稍后可以重试读取操作。
WRITE_RAW
语法格式
UTL_TCP.WRITE_RAW (c IN OUT connection,
data IN RAW,
len IN PLS_INTEGER DEFAULT NULL)
RETURN PLS_INTEGER;
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
c | in | 要发送数据数据的 TCP 连接 |
data | in | 发送的数据。 |
len | in | 要发送的数据字节数。 |
注意事项
连接必须已经通过调用 open_connection() 打开。
READ_TEXT
语法格式
UTL_TCP.READ_TEXT (c IN OUT connection,
data IN OUT VARCHAR2,
data_len OUT INTEGER,
len IN PLS_INTEGER DEFAULT 1,
peek IN BOOLEAN DEFAULT FALSE) RETURN
INTEGER;
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
c | in | 从中接收数据的 TCP 连接 |
data | in | 收到的数据。 |
data_len | in | 实际接收到的数据字节数。 |
len | in | 要接收的数据字节数。 |
peek | in | 通常,用户希望读取数据并将其从输入队列中移除,即消费它。 在某些情况下,用户可能只想提前查看数据而不将其从输入队列中删除,以便在下一次调用时仍可读取(甚至查看)。 要将数据保留在输入队列中,请将此标志设置为 TRUE,并且必须在打开连接时设置输入缓冲区。 用户可以查看的数据量(即读取但保留在输入队列中)必须小于输入缓冲区的大小。 |
注意事项
- 连接必须已经通过调用 open_connection() 打开。
- 在读取指定数量的字符或到达输入结尾之前,此函数不会返回。文本消息将从打开连接时指定的在线字符集转换为数据库字符集,然后再返回给调用者。
WRITE_TEXT
语法格式
UTL_TCP.WRITE_TEXT (c IN OUT connection,
data IN VARCHAR2,
len IN PLS_INTEGER DEFAULT NULL)
RETURN INTEGER;
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
c | in | 需要发送数据的 TCP 连接 |
data | in | 要发送的数据 |
len | in | 要发送的数据字节数。 |
注意事项
连接必须已经通过调用 open_connection() 打开。
READ_LINE
语法格式
UTL_TCP.READ_LINE (c IN OUT connection,
data IN OUT VARCHAR2,
data_len OUT INTEGER,
remove_crlf IN BOOLEAN DEFAULT FALSE,
peek IN BOOLEAN DEFAULT FALSE)
RETURN INTEGER;
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
c | in | 从中接收数据的 TCP 连接 |
data | in | 收到的数据。 |
data_len | in | 实际接收到的数据字节数。 |
remove_crlf | in | 如果为 TRUE,则从接收到的消息中删除尾随 CR/LF 字符。 |
peek | in | 通常,用户希望读取数据并将其从输入队列中删除,即消费它。 在某些情况下,用户可能只想向前看数据,即偷看它,而不将其从输入队列中删除,以便在下一次调用中仍然可以读取(甚至偷看)。 要将数据保留在输入队列中,请将此标志设置为 TRUE 并在打开连接之前设置输入缓冲区。 用户可以查看的数据量(即读取但保留在输入队列中)必须小于输入缓冲区的大小。 |
注意事项
- 连接必须已经通过调用 open_connection() 打开。
- 在到达行尾或到达输入末尾之前,此函数不会返回。文本消息将从打开连接时指定的在线字符集转换为数据库字符集,然后再返回给调用者。
WRITE_LINE
语法格式
UTL_TCP.WRITE_LINE (c IN OUT connection,
data IN VARCHAR2 DEFAULT NULL)
RETURN PLS_INTEGER;
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
c | in | 需要发送数据的 TCP 连接 |
data | in | 要发送的数据 |
注意事项
连接必须已经通过调用 open_connection() 打开。
GET_RAW(),GET_TEXT(),GET_LINE()
语法格式
UTL_TCP.GET_RAW (c IN OUT connection,
len IN INTEGER DEFAULT 1,
peek IN BOOLEAN DEFAULT FALSE) RETURN RAW;
UTL_TCP.GET_TEXT (c IN OUT connection,
len IN INTEGER DEFAULT 1,
peek IN BOOLEAN DEFAULT FALSE) RETURN VARCHAR2;
UTL_TCP.GET_LINE (c IN OUT connection,
remove_crlf IN BOOLEAN DEFAULT false,
peek IN BOOLEAN DEFAULT FALSE) RETURN
VARCHAR2;
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
c | in | 从中接收数据的 TCP 连接 |
len | in | 收到的数据。 |
peek | in | 通常,用户希望读取数据并将其从输入队列中删除,即消费它。 在某些情况下,用户可能只想向前看数据,即偷看它,而不将其从输入队列中删除,以便在下一次调用中仍然可以读取(甚至偷看)。 要将数据保留在输入队列中,请将此标志设置为 TRUE 并在打开连接之前设置输入缓冲区。 用户可以查看的数据量(即读取但保留在输入队列中)必须小于输入缓冲区的大小。 |
remove_crlf | in | 如果为 TRUE,则从接收到的消息中删除尾随 CR/LF 字符。 |
注意事项
连接必须已经通过调用 open_connection() 打开。
FLUSH
语法格式
UTL_TCP.FLUSH (c IN OUT connection);
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
c | in | 要将数据发送到的 TCP 连接。 |
注意事项
连接必须已经通过调用 open_connection() 打开。
CLOSE_CONNECTION
语法格式
UTL_TCP.close_CLOSE_CONNECTION (c IN OUT connection);
参数说明
参数名称 | 参数类型 | 参数描述 |
---|---|---|
c | in | 要关闭的 TCP 连接。 |
注意事项
- 连接必须已经通过调用 open_connection() 打开。
- c 的 remote_host、remote_port、local_host、local_port 和 charset 字段将在连接关闭后重置。
- 必须明确关闭打开的连接。 当存储连接的记录变量超出程序的范围时,打开的连接将保持打开状态。 未能关闭不需要的连接可能会导致不必要地占用本地和远程系统资源。
CLOSE_ALL_CONNECTIONS
语法格式
UTL_TCP.CLOSE_ALL_CONNECTIONS;
示例
示例1:与应用进程的通信
如下方式为通过utl_tcp主动与python进程通信,方式如下:
1、窗口1,用python3执行如下代码(准备python通信进程,ip根据实际填写,端口填写为未占用端口)。
import socket
server=socket.socket() #创建一个用于监听连接的Socket对象(服务器端)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('127.0.0.1',8888)) #设置服务端的ip和端口号
server.listen(0) #开始监听
print('ready')
conn, addr = server.accept() #接受服务端发出的消息和地址信息
while True:
c_info = conn.recv(1024) #将接受的消息存入到c_info变量中
if not c_info:
break
else:
print(c_info)
conn.send(b'Test1!\r\nTest2,\nTest3.\nTest4?\nTest5\'\nTest6"\nTest7:') #向客户端发出消息1
print('send msg1')
msg2 = '测试1\r测试2\n'
msg2 = msg2.encode('utf-8')
conn.send(msg2) #向客户端发出消息2
print('send msg2')
conn.send(b'!@#$%^&*().,') #向客户端发出消息2
print('send msg3')
conn.send(b'') #向客户端发出消息2
print('send msg4')
break
conn.close() #关闭连接
2、窗口2,在数据库中执行如下代码。
DECLARE
v_conn UTL_TCP.connection;
w_text PLS_INTEGER :=0;
r_text PLS_INTEGER :=0;
r_text_data varchar2(100) :='';
g_text varchar2(100);
BEGIN
--关闭所有连接
utl_tcp.close_all_connections;
--建立第一次连接
v_conn := UTL_TCP.open_connection(
remote_host => '127.0.0.1',
remote_port => '8888',
local_host => '127.0.0.1',
local_port => '5432',
in_buffer_size=> 32767,
out_buffer_size => 32767,
charset => 'utf-8',
newline => UTL_TCP.CRLF,
tx_timeout => '3');
--发送text消息
w_text := UTL_TCP.write_text(v_conn,'nice to meet you!',17);
--立即输出缓冲区的数据
utl_tcp.flush(v_conn);
dbms_output.put_line('w_text :=' || w_text );
--等待服务端消息发送至客户端
dbms_lock.sleep(1);
dbms_output.put_line('v_conn_flag :=' || utl_tcp.available(v_conn));
--接收消息
IF UTL_TCP.available(v_conn) > 0 THEN
--read_text指定len参数32767,peek参数为true
utl_tcp.read_text(v_conn,r_text_data,r_text,32767,true);
dbms_output.put_line('r_text_data:==' || r_text_data);
dbms_output.put_line('r_text :==' || r_text );
--len参数为0
utl_tcp.read_text(v_conn,r_text_data,r_text,0,true);
dbms_output.put_line('r_text_data:==' || r_text_data);
dbms_output.put_line('r_text :==' || r_text );
--省略默认参数
utl_tcp.read_text(v_conn,r_text_data,r_text);
dbms_output.put_line('r_text_data:==' || r_text_data);
dbms_output.put_line('r_text :==' || r_text );
END IF;
utl_tcp.close_connection(v_conn);
end;
/
3、此时窗口1中接收到窗口2由数据库发送给python进程的消息,python端结果为:
4、数据库返回结果为:
示例2:请求网页
下面的代码示例说明了如何使用 TCP/IP 包通过 HTTP 检索网页。 它连接到在端口 80(HTTP 的标准端口)上侦听的 Web 服务器并请求根文档。
DECLARE
c utl_tcp.connection; -- TCP/IP connection to the Web server
ret_val pls_integer;
BEGIN
c := utl_tcp.open_connection(remote_host => 'www.acme.com',
remote_port => 80,
charset => 'US7ASCII'); -- open connection
ret_val := utl_tcp.write_line(c, 'GET / HTTP/1.0'); -- send HTTP request
ret_val := utl_tcp.write_line(c);
BEGIN
LOOP
dbms_output.put_line(utl_tcp.get_line(c, TRUE)); -- read result
END LOOP;
END;
utl_tcp.close_connection(c);
END;
/
示例3: 通过UTL_TCP.OPEN_CONNECTION函数使用域名作为传入参数
1、修改host文件。
127.0.0.1 orderer0.example.com orderer1.example.com orderer2.example.com orderer3.example.com orderer4.example.com
2、重启网络服务。
service network restart
3、配置plpython环境。用户可参见python3环境配置。
4、窗口1,准备python通信进程(ip根据实际填写,端口填写为未占用端口)。
import socket
server=socket.socket() #创建一个用于监听连接的Socket对象(服务器端)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('127.0.0.1',8233)) #设置服务端的ip和端口号
server.listen(0) #开始监听
while True:
print('ready')
conn, addr = server.accept() #接受服务端发出的消息和地址信息
while True:
c_info = conn.recv(1024) #将接受的消息存入到c_info变量中
if not c_info:
break
else:
print(c_info)
break
5、窗口2,在数据库中执行如下语句创建plpython3u插件。
create extension plpython3u;
6、窗口2,在数据库中执行如下语句,允许将dbms_output.put_line的输出信息输出至vsql的命令界面的屏幕上。
set serveroutput on;
7、窗口2,在数据库中执行如下匿名块。
DECLARE
v_conn UTL_TCP.connection;
w_text varchar(100);
BEGIN
--关闭所有连接
utl_tcp.close_all_connections;
v_conn := UTL_TCP.open_connection(
remote_host => 'orderer0.example.com',
remote_port => '8233',
in_buffer_size=> 32767,
out_buffer_size => 32767,
charset => 'utf-8',
newline => UTL_TCP.CRLF,
tx_timeout => '3');
--发送text消息
w_text := UTL_TCP.write_text(v_conn,'connection1');
--立即输出缓冲区的数据
utl_tcp.flush(v_conn);
--关闭连接
utl_tcp.close_connection(v_conn);
END;
/
8、此时窗口1中接收到窗口2由数据库发送给python进程的消息,python端结果为: