Two Clients and One Server
- page test_twoclients
- Summary
This example demonstrates two clients connecting to a single server. Each client sends messages to, and receives messages from, the server. The code is broken up into a function for the client and a function for the server. The client and server code first sets up the inbound and outbound message layouts then enters into a loop that repeatedly sends and receives messages using the defined layouts.
- Launching the client and server
A dummy RCBPtn object is created for construction of the Redev instance; the partition is not used for creating message layout arrays in the client and server code below. The ADIOS2 parameters for the BP4 engine (the default when
isSST
isfalse
) are then set and passed into the functions for the client and server.//dummy partition vector data const auto dim = 1; auto ranks = isRdv ? redev::LOs({0}) : redev::LOs(1); auto cuts = isRdv ? redev::Reals({0}) : redev::Reals(1); auto ptn = redev::RCBPtn(dim,ranks,cuts); redev::Redev rdv(MPI_COMM_WORLD,ptn,static_cast<redev::ProcessType>(isRdv)); adios2::Params params{ {"Streaming", "On"}, {"OpenTimeoutSecs", "6"}}; if(!isRdv) { client(rdv,clientId,params,isSST); } else { server(rdv,params,isSST); } std::cout << "done\n";
- Client Setup
- Client setup begins with creation of the redev::BidirectionalComm communication object via the call to redev::CreateAdiosClient. Note, the string for each created redev::BidirectionalComm must match in the Server and Client calls.
- Server Create Clients
- In the Client code only a single redev::BidirectionalComm is needed. As there are two Clients, the Server must call redev::CreateAdiosClient twice; once for “client0” and again for “client1”.
- First Inbound Messages
- As was done in the Client code when receiving a message from the Server, the message layout is retrieved and checked that it defines a single entry coming from process zero of each Client. This sequence is repeated for both the message coming from each Client using the respective redev::BidirectionalComm
- First Outbound Messages
- Now that the first ‘Forward’ messages (Client-to-Server) have been received the layout of the ‘Reverse’ messages (Server-to-Client) can be constructed using the data returned from the call to redev::AdiosComm::GetInMessageLayout. But, given that this example is only sending and receiving a single redev::LO between Client and Server, the layout of the Reverse message is hardcoded. See the wdmapp_coupling documentation for an example that constructs the Reverse message layout for data associated with an unstructured mesh.
- Client Loop
- With the outbound message layout setup for each Client-to-Server Send a loop over communication rounds is entered that reuses that layout. The round first packs and sends a message to the Server (using the
- Server Loop
- The Server loop over communication rounds begins by receiving messages from the Clients (via the
Remark
The message layouts in this example are trivial; only a single integer is being sent/receieved and the client and server only have a single process. For a more complete example discussing message layout see test_sendrecv.
Given that the data being transferred is a single integer and each Client and Server is only running on a single process the data layout (dest
and offsets
) for the outbound and inbound messages are trivial.
The first outbound message has a single entry in the dest
vector, 0
, to specify that all messages are being sent to Server process zero. Likewise, the offsets
array is set to {0,1}
to denote that messages for the destination process start at position zero in the messages array and there is only a single entry (offsets[1]-offsets[0]=1-0=1
). These vectors are passed into redev::AdiosComm::SetOutMessageLayout for the c2s
(Client-to-Server) member of the commPair
struct. As long as the layout remains the same, no additional calls to redev::AdiosComm::SetOutMessageLayout are needed.
As with the outbound message, the inbound message layout defines a single entry coming from Server process zero.. redev::AdiosComm::SetOutMessageLayout is called to retrieve the layout of the message that was just read with the call to redev::AdiosComm::Recv. Note, that these methods are called on the s2c
(Server-to-Client) member of the commPair
struct.
std::stringstream clientName;
clientName << "client" << clientId;
auto commPair = rdv.CreateAdiosClient<redev::LO>(clientName.str(),params,static_cast<redev::TransportType>(isSST));
//setup outbound message
std::cout << "sending to server\n";
redev::LOs dest = redev::LOs{0};
redev::LOs offsets = redev::LOs{0,1};
commPair.SetOutMessageLayout(dest, offsets);
//first outbound send
redev::LOs msgs = redev::LOs(1,42+clientId);
commPair.Send(msgs.data());
//first inbound message from server
std::cout << "recieving from server\n";
auto msgFromServer = commPair.Recv();
auto inMsg = commPair.GetInMessageLayout();
REDEV_ALWAYS_ASSERT(inMsg.offset == redev::GOs({0,1}));
REDEV_ALWAYS_ASSERT(inMsg.srcRanks == redev::GOs({0}));
REDEV_ALWAYS_ASSERT(inMsg.start == 0);
REDEV_ALWAYS_ASSERT(inMsg.count == 1);
REDEV_ALWAYS_ASSERT(msgFromServer[0] == 1337+clientId);
auto client0 = rdv.CreateAdiosClient<redev::LO>("client0",params,static_cast<redev::TransportType>(isSST));
auto client1 = rdv.CreateAdiosClient<redev::LO>("client1",params,static_cast<redev::TransportType>(isSST));
c2s
(Client-to-Server) struct member.
std::cout << "recieving from client0\n";
auto msgs0 = client0.Recv();
{
auto inMsg = client0.GetInMessageLayout();
REDEV_ALWAYS_ASSERT(inMsg.offset == redev::GOs({0,1}));
REDEV_ALWAYS_ASSERT(inMsg.srcRanks == redev::GOs({0}));
REDEV_ALWAYS_ASSERT(inMsg.start == 0);
REDEV_ALWAYS_ASSERT(inMsg.count == 1);
REDEV_ALWAYS_ASSERT(msgs0[0] == 42);
}
std::cout << "recieving from client1\n";
auto msgs1 = client1.Recv();
{
auto inMsg = client1.GetInMessageLayout();
REDEV_ALWAYS_ASSERT(inMsg.offset == redev::GOs({0,1}));
REDEV_ALWAYS_ASSERT(inMsg.srcRanks == redev::GOs({0}));
REDEV_ALWAYS_ASSERT(inMsg.start == 0);
REDEV_ALWAYS_ASSERT(inMsg.count == 1);
REDEV_ALWAYS_ASSERT(msgs1[0] == 43);
}
As with the outbound messages from the Client to the Server, the call to redev::AdiosComm::SetOutMessageLayout only needs to be made once for each Server-to-Client (s2c
within the client0
and client1
commPair
objects).
std::cout << "sending to client0\n";
redev::LOs dest = redev::LOs{0};
redev::LOs offsets = redev::LOs{0,1};
client0.SetOutMessageLayout(dest, offsets);
redev::LOs msgs = redev::LOs(1,1337);
client0.Send(msgs.data());
std::cout << "sending to client1\n";
client1.SetOutMessageLayout(dest, offsets);
msgs = redev::LOs(1,1338);
client1.Send(msgs.data());
c2s
member of the commPair
struct) then receives a message from the Server (using the s2c
member).
for(int iter=0; iter<3; iter++) {
std::cout << "iter " << iter << "\n";
//outbound message to server
redev::LOs outMsg = redev::LOs(1,42+clientId);
commPair.Send(outMsg.data());
//inbound message from server
auto msg = commPair.Recv();
REDEV_ALWAYS_ASSERT(msg[0] == 1337+clientId);
}
client[0|1].c2s
objects) then sends messages back to the Clients (via client[0|1].s2c
).
for(int iter=0; iter<3; iter++) {
std::cout << "iter " << iter << "\n";
//inbound messages from clients
auto inMsg0 = client0.Recv();
REDEV_ALWAYS_ASSERT(inMsg0[0] == 42);
auto inMsg1 = client1.Recv();
REDEV_ALWAYS_ASSERT(inMsg1[0] == 43);
//outbound messages to clients
redev::LOs outMsg0 = redev::LOs(1,1337);
client0.Send(outMsg0.data());
redev::LOs outMsg1 = redev::LOs(1,1338);
client1.Send(outMsg1.data());
}