FB_TCPServer/FB_TCPServer2

概述

类型:

功能块

适用的版本:

V1.0.4.0

继承:

-

执行:

-

任务

TCP 服务器监视并处理指定端口上的传入客户端连接。在接受连接后,可从客户端接收数据,并可将数据发送到一个或所有客户端。

功能描述

库提供两种版本的 FB_TCPServer 功能块。FB_TCPServer2 是增强版本,支持使用 TLS(传输层安全)的连接。

是否支持使用 TLS 建立连接取决于使用 FB_TcpServer2 的控制器。请参阅控制器的相应手册,确认是否支持使用 TLS 开展 TCP 通讯。

两种功能块的工作原理相同。它们在所提供的属性和方法上有一定区别,详见相应章节。

命令的正常顺序是,首先调用 OpenOpenTls 方法,指定 TCP 端口号,并且视情况指定要监视的接口的 IP 地址。然后循环地检索 State 属性值,直至属性值不同于 Opening。如果状态未切换到 Opened,则说明出现了错误。验证 Result 属性值以确定原因。

成功后(状态 = Opened),服务器就可以接受传入的连接。这一点由属性 IsNewConnectionAvailable 指示。应用程序必须定期对其进行验证,如果值是 TRUE,则必须调用 Accept 方法。它返回连接源的 IP 地址和端口。然后可以用编程的方式确定是否保持连接。已连接客户端的数目可通过使用 NumberOfConnectedClients 属性来验证。

要验证客户端是否已将数据发送到现在可以读取的服务器,使用属性 IsDataAvailable。方法 ReceiveFromFirstAvailableClientPeekFromFirstAvailableClient 可用于从第一个客户端读取数据,该客户端的数据在不知道客户端 IP 和端口的情况下也可获得。由于应用程序在调用方法前没有确定从哪个客户端读取数据,因此客户端的 IP 和端口用作方法的输出。除非本文另有说明,否则 FB_TCPServer/FB_TCPServer2 方法运行方式与 FB_TCPClient/FB_TCPClient2 相同。

SendToSpecificClient 方法可在使用 ReceiveFromFirstAvailableClient 方法收到数据后直接回复特定客户端。因此应用程序必须提供连接到 TCP 服务器的客户端的 IP 地址和端口。然后它的工作方式类似于 FB_TCPClient/FB_TCPClient2Send 方法。

要发送数据到已连接的客户端,请使用 SendToAll 方法。在使用 SendToAll 方法时,客户端出错将使向该客户端传输数据终止,并返回已发送字节的数量。然后通过将已发送字节的总数与待发送数据量和客户端数量的乘积进行比较,可以确定字节是否已发送到客户端。

当 TCP 服务器状态是 Listening 时,必须循环调用方法 CheckClients,以检测客户端是否已经关闭连接。另可检索 NumberOfConnectedClients 属性。如果已检测到客户端发起了断开操作,并且无法从该客户端读取更多的数据,则它将关闭,以备建立新的传入连接。否则,保持连接处于可用状态,直到数据已被读取,或者直到已为该连接调用了方法 DisconnectClient。调用方法 DisconnectClient 时,会丢弃从指定客户端接收的尚未处理的数据。

如果方法的处理不成功,Result 属性的值中会指出。每次调用方法后必须验证 Result 的值。结果可使用 ResetResult 方法重置为 Ok

注: 只要属性 Result 的值不等于 Ok,便会阻止所有方法。在这种情况下,会在不影响 Result 属性信息的情形下中止方法调用。

接口

功能块没有输入和输出。其功能通过方法和属性来实现。为了检测是否与所连接的客户端断开连接,必须循环调用方法 CheckClients。另可检索 NumberOfConnectedClients 属性值。上述任一种方法都可以验证客户端是仍处于连接状态还是已经关闭连接。

实现示例

PROGRAM LibDocu_TcpServer2
VAR
    xOpen : BOOL;
    xOpenTls : BOOL;
    xClose : BOOL;
    etResult : TCPUDP.ET_Result;
    etState : TCPUDP.ET_State;
    iState : INT;
    fbTcpServer : TCPUDP.FB_TCPServer2;
    stTlsSettings : TCPUDP.ST_TlsSettingsServer;
    sIp : STRING(15) := '';
    uiPort : UINT := 12345;
END_VAR
CASE iState OF
    0: // idle
        IF xOpen OR xOpenTls THEN
            IF xOpen AND_THEN NOT fbTcpServer.Open(sIp, uiPort) THEN
                iState := 100; // error state
            ELSIF xOpenTls AND_THEN NOT fbTcpServer.OpenTls(sIp, uiPort, stTlsSettings) THEN
                iState := 100; // error state
            END_IF			
            xOpen := xOpenTls := FALSE;
        END_IF
        IF fbTcpServer.State = TCPUDP.ET_State.Listening THEN
            iState := 10;
        END_IF
    10: // listening
        IF fbTcpServer.State = TCPUDP.ET_State.Idle THEN
            iState := 0; 
        ELSIF fbTcpServer.State <> TCPUDP.ET_State.Listening AND fbTcpServer.State <> TCPUDP.ET_State.Closing THEN
            iState := 100; // unexpected state
        ELSIF fbtcpserver.IsNewConnectionAvailable THEN
            fbTcpServer.Accept();
            iState := 20; // state accepting
        ELSE
            IF xClose THEN
                xClose := FALSE;
                fbTcpServer.Close();
            END_IF

            (* your code comes here, e.g. check for data available to read *)

        END_IF
    20: // accepting
        IF fbTcpServer.State <> TCPUDP.ET_State.Accepting AND fbTcpServer.State <> TCPUDP.ET_State.Listening THEN
            iState := 100; // unexpected, go to error state
        ELSIF fbTcpServer.State = TCPUDP.ET_State.Listening THEN
            iState := 10;// incoming connection successful accepted
        END_IF
    100: // error state
        (* your code comes here*)
END_CASE
etResult := fbTcpServer.Result;
etState := fbTcpServer.State;