C++ IP API  9.5.5
TCP & UDP for virtual user scripts
C++ API for TCP and UDP protocols

Introduction

Manage IP connections and endpoints and send and receive data using the IP family of protocols from an eggPlant test script. Two protocols are supported: UDP and TCP.

Facilita::IpVirtualUser (inherits from VirtualUser) and Facilita::IpScript (inherits from VirtualUserScript) provide base classes for developing IP test scripts. Facilita::UdpLink and Facilita::TcpConnection instances provide peer to peer communications using the underlying network protocol. Links and Connections are created by factory objects within the Facilita::IpVirtualUser instances that are created as part of a test. There are distinct factory objects for UDP and TCP per IpVirtualUser instance, call the script's udp() or tcp() methods to access the relevant factory. Within test scripts (that inherit from Facilita::IpScript) the factory objects are used to create Links and Connections.

TCP

To send data to and receive data from a remote TCP peer at IP address 192.168.0.1 on IP port 60000:

public: void script()
{
...
// Create an IpEndPoint to represent the IP address and port of a remote server.
IpEndPoint myRemotePeer = IpEndPoint("192.168.0.1", 60000);
...
// Create a TCP connection where the local IP address and port are not specified. This means the Operating System will subsequently assign them.
// tcp() returns a reference to the TCP factory held in the IpVirtualUser.
TcpClientConnection ep1 = tcp().createClient(myRemotePeer);
...
// An alternative approach:
// Create a TCP connection where the local IP address and port are specified
TcpClientConnection ep2 = tcp().createClient(IpEndPoint("127.0.0.1", 60000 + getIndex()), myRemotePeer);
...
// send "hello" on ep1
ep1.send("hello");
// read bytes up to and including "good morning" on ep1
// The ReceiveResponse object indicates any failure and contains the data read
ReceiveResponse response1 = ep1.receive("good morning");
}

TCP is a streaming protocol where the current position in the read data stream is maintained. As bytes are consumed methods the read position advances. No backtracking is allowed. Bytes received from the network but not yet read from the API are buffered. The Facilita::TcpConnection methods that consume data fall into three groups:

  • receive - read and return data.
  • discard* - read and discard data.
  • clearBuffer - all buffered data.

The property Buffersize specifies the maximum data that can be read in a single receive call. The discard* methods are not affected by Buffersize.

TCP connection classes come in two flavours Facilita::TcpConnection and a sub-class Facilita::TcpClientConnection. These classes have almost the same signature, Facilita::TcpClientConnection has the additional method connect. A TcpClientConnection is created in the above example. To create a server:

public: void script()
{
// change the default timeout to wait for a connection in this VU, INFINITE is OK
tcp().setListenTimeout(60*milliseconds);
// wait for a new peer to connect to port 60000 on this machine
TcpConnection ep1 = tcp().waitForConnection(IpEndPoint("127.0.0.1", 60000));
...
}

Making multiple simultaneous waitForConnection calls from the same engine process is supported. This enables a number of Virtual Users executing the same server style script to emulate a multi-threaded service. A single server thread per engine process listens for connection requests. This thread passes completed connections to the server-side Virtual User threads. Attempts by multiple engine processes (executing on the same machine) to make simultaneous Facilita::TcpConnectionFactory::waitForConnection() calls for the same address will result in "address in use" errors.

UDP

To create a directed UDP link and then exchange data:

public: void script()
{
...
// Create an IpEndPoint to represent a remote server with an IP address of 192.168.0.1 and a port of 60000
IpEndPoint myRemotePeer = IpEndPoint("192.168.0.1", 60000);
...
// Create a UDP link where the local IP address and port are not specified meaning the Operating System will assign them.
// udp() returns a reference to the factory held in the IpVirtualUser.
UdpLink ep1 = udp().createDirectedLink(myRemotePeer);
...
// Here is an alternative.
// This time create a link and specify the local address
UdpLink ep2 = udp().createDirectedLink(IpEndPoint("127.0.0.1", 60000 + getIndex()), myRemotePeer);
...
// send "hello" on ep1
ep1.send("hello");
// read bytes up to and including "good morning" from ep1
// The ReceiveResponse object indicates any failure and contains the data read
ReceiveResponse response1 = ep1.receive();
if (response1.verifyNotContains("good morning"))
{
...
}
...
}

The UDP classes support a maximum datagram size up to the limit of the underlying network, the current limit is given by the property Facilita::UdpLink::maxUdpSize. The UDP peer to peer links over which datagrams are received and sent are either directed or non-directed. Directed links reference the same peers throughout their life and are rather like TCP connections. Non-directed links represent a local endpoint that can handle incoming calls from variable remote peers.

UDP clients are created by calls on the IpVirtualUser UDP factory.

// create a directed UDP client
UdpLink ep1 = udp().createDirectedLink(myRemotePeer);
// create a non-directed UDP client
UdpLink ep1 = udp().createLink(myRemotePeer);

To avoid loss of data, each link has it's own thread dedicated to reading data from the associated remote peer.

The difference between a UDP "client" and "server" is that a server waits for the first message whilst the client initiates the conversation. A pool of Virtual Users running a server script can implement a multi-threaded service accessed via UDP. Here is how a link is created in a server script:

public: void script()
{
// change the default timeout to wait for a link in this script, INFINITE is OK
udp().setLinkTimeout(60*milliseconds);
// wait for a new peer sending data to port 60000 on this machine
UdpLink ep1 = udp().waitForLink(IpEndPoint("127.0.0.1", 60000));
// read the first datagram from a remote peer
ReceiveResponse response1 = ep1.receive();
...
}

Server scripts wait for a link to be created and script instances, like the example above, are in a race to handle the next incoming message. The script instances for a server endpoint share a reader thread. This thread handles incoming messages and depending on the source address of the incoming message passes them to either:

  • a queue of new IpdEndpoints - to be processed by scripts calling waitForLink.
  • a queue to read by a script holding the appropriate IpEndPoint.