VastbaseG100

基于openGauss内核开发的企业级关系型数据库。

Menu

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时能够使用(即创建DB时DBCOMPATIBILITY='A'),在其他数据库兼容模式下不能使用该特性。
  • 系统已经安装python3。python3安装方法请参见python3环境配置
  • 系统已经创建plpython3u插件,需设置环境变量PYTHONHOME,包括以下内容(重启数据库生效):

    • PYTHONHOME:python3安装路径。
    • LD_LIBRARY_PATH:python3安装路径下的lib目录。
    • PATH:python3安装路径bin目录。

    使用如下命令创建插件:

    CREATE EXTENSION 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)
                          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 表示永远等待。

注意事项

UTL_TCP 包打开的连接可以保持打开状态,并在共享服务器配置中从一个数据库调用传递到另一个数据库调用。但是,必须明确关闭连接。当存储连接的vastbase记录变量超出vastbase程序的范围时,连接将保持打开状态。未能关闭不需要的连接可能会导致不必要地占用本地和远程系统资源。

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;
/