blob: f4026972d4f5c77cff91fd28732b074897f1bde5 [file] [log] [blame]
/*
*
* Copyright (c) 2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file implements a weave-echo-requester, for the
* Weave Echo Profile.
*
* The Weave Echo Profile implements two simple methods, in the
* style of ICMP ECHO REQUEST and ECHO REPLY, in which a sent
* payload is turned around by the responder and echoed back to
* the originator.
*
*/
#include "weave-app-common.h"
#include <Weave/Profiles/echo/WeaveEcho.h>
#include <Weave/Support/ErrorStr.h>
// Max number of times, the client will try to connect to the server.
#define MAX_CONNECT_ATTEMPTS (5)
// Max value for the number of EchoRequests sent.
#define MAX_ECHO_COUNT (3)
using namespace nl::Inet;
using namespace nl::Weave;
using namespace nl::Weave::Profiles;
// Callback handler when a Weave EchoResponse is received.
static void HandleEchoResponseReceived(uint64_t nodeId, IPAddress nodeAddr, PacketBuffer *payload);
// Callback handler when a connection attempt has been completed
static void HandleConnectionComplete(WeaveConnection *con, WEAVE_ERROR conErr);
// Callback handler when the TCP connection is closed by the peer.
static void HandleConnectionClosed(WeaveConnection *con, WEAVE_ERROR conErr);
// The number of connection attempts performed by this Weave EchoClient.
static int32_t gConnectAttempts = 0;
// The Weave Echo interval time in microseconds.
static int32_t gEchoInterval = 1000000;
// The last time a Weave Echo was attempted to be sent.
static uint64_t gLastEchoTime = 0;
// True, if the EchoClient is waiting for an EchoResponse
// after sending an EchoRequest, false otherwise.
static bool gWaitingForEchoResp = false;
// Count of the number of EchoRequests sent.
static uint64_t gEchoCount = 0;
// Count of the number of EchoResponses received.
static uint64_t gEchoRespCount = 0;
// The WeaveEchoClient object.
static WeaveEchoClient gEchoClient;
// A pointer to a Weave TCP connection object.
static WeaveConnection *gCon = NULL;
// True, if client is still connecting to the server, false otherwise.
static bool gClientConInProgress = false;
// True, once client connection to server is established.
static bool gClientConEstablished = false;
static void StartClientConnection();
static void CloseClientConnection();
static void SendEchoRequest();
static bool EchoIntervalExpired();
static void FormulateEchoRequestBuffer(PacketBuffer*& payloadBuf);
bool EchoIntervalExpired(void)
{
if (Now() >= gLastEchoTime + gEchoInterval)
{
return true;
}
else
{
return false;
}
}
void FormulateEchoRequestBuffer(PacketBuffer*& payloadBuf)
{
payloadBuf = PacketBuffer::New();
if (payloadBuf == NULL)
{
printf("Unable to allocate PacketBuffer\n");
return;
}
// Add some application payload data in the buffer.
char *p = (char *) payloadBuf->Start();
int32_t len = sprintf(p, "Echo Message %" PRIu64 "\n", gEchoCount);
// Set the datalength in the buffer appropriately.
payloadBuf->SetDataLength((uint16_t) len);
}
void SendEchoRequest(void)
{
WEAVE_ERROR err;
PacketBuffer *payloadBuf = NULL;
gLastEchoTime = Now();
FormulateEchoRequestBuffer(payloadBuf);
if (payloadBuf == NULL)
{
return;
}
if (gClientConEstablished)
{
err = gEchoClient.SendEchoRequest(gCon, payloadBuf);
// Set the local buffer to NULL after passing it down to
// the lower layers who are now responsible for freeing
// the buffer.
payloadBuf = NULL;
if (err == WEAVE_NO_ERROR)
{
gWaitingForEchoResp = true;
gEchoCount++;
}
else
{
printf("WeaveEchoClient.SendEchoRequest() failed, err: %s\n", nl::ErrorStr(err));
}
}
}
void HandleEchoResponseReceived(uint64_t nodeId, IPAddress nodeAddr, PacketBuffer *payload)
{
uint32_t respTime = Now();
uint32_t transitTime = respTime - gLastEchoTime;
gWaitingForEchoResp = false;
gEchoRespCount++;
char msgNodeAddrStr[WEAVE_MAX_MESSAGE_SOURCE_STR_LENGTH];
WeaveNodeAddrToStr(msgNodeAddrStr, sizeof(msgNodeAddrStr) - 1, nodeId, &nodeAddr, gDestPort, gCon);
printf("Echo Response from node %s : %" PRIu64 "/%" PRIu64 "(%.2f%%) len=%u time=%.3fms\n", msgNodeAddrStr,
gEchoRespCount, gEchoCount, ((double) gEchoRespCount) * 100 / gEchoCount, payload->DataLength(),
((double) transitTime) / 1000);
}
void StartClientConnection()
{
WEAVE_ERROR err;
// Previous connection attempt underway.
if (gClientConInProgress)
return;
gClientConEstablished = false;
gCon = MessageLayer.NewConnection();
if (gCon == NULL)
{
printf("MessageLayer.NewConnection failed, err: %s\n", nl::ErrorStr(WEAVE_ERROR_NO_MEMORY));
gLastEchoTime = Now();
return;
}
gCon->OnConnectionComplete = HandleConnectionComplete;
gCon->OnConnectionClosed = HandleConnectionClosed;
// Attempt to connect to the peer.
err = gCon->Connect(gDestNodeId, gDestv6Addr, gDestPort);
if (err != WEAVE_NO_ERROR)
{
printf("WeaveConnection.Connect failed, err: %s\n", nl::ErrorStr(err));
gLastEchoTime = Now();
CloseClientConnection();
gConnectAttempts++;
return;
}
gClientConInProgress = true;
}
void CloseClientConnection(void)
{
if (gCon)
{
gCon->Close();
gCon = NULL;
printf("Connection closed\n");
}
gClientConEstablished = false;
gClientConInProgress = false;
}
void HandleConnectionComplete(WeaveConnection *con, WEAVE_ERROR conErr)
{
char msgNodeAddrStr[WEAVE_MAX_MESSAGE_SOURCE_STR_LENGTH];
WeaveNodeAddrToStr(msgNodeAddrStr, sizeof(msgNodeAddrStr) - 1, con->PeerNodeId, &con->PeerAddr, gDestPort, con);
if (conErr != WEAVE_NO_ERROR)
{
printf("Connection FAILED to node %s, err: %s\n", msgNodeAddrStr, nl::ErrorStr(conErr));
gLastEchoTime = Now();
CloseClientConnection();
gConnectAttempts++;
return;
}
printf("Connection established to node %s\n", msgNodeAddrStr);
gCon = con;
con->OnConnectionClosed = HandleConnectionClosed;
gClientConEstablished = true;
gClientConInProgress = false;
}
void HandleConnectionClosed(WeaveConnection *con, WEAVE_ERROR conErr)
{
char msgNodeAddrStr[WEAVE_MAX_MESSAGE_SOURCE_STR_LENGTH];
WeaveNodeAddrToStr(msgNodeAddrStr, sizeof(msgNodeAddrStr) - 1, con->PeerNodeId, &con->PeerAddr, gDestPort, con);
if (conErr == WEAVE_NO_ERROR)
printf("Connection closed to node %s\n", msgNodeAddrStr);
else
printf("Connection ABORTED to node %s, err: %s\n", msgNodeAddrStr, nl::ErrorStr(conErr));
gWaitingForEchoResp = false;
if (con == gCon)
{
CloseClientConnection();
}
}
int main(int argc, char *argv[])
{
WEAVE_ERROR err;
// Assign local IPv6 address.
IPAddress::FromString("fd00:0:1:1::1", gLocalv6Addr);
// Initialize the Weave stack as the client.
InitializeWeave(false);
// Initialize the EchoClient application.
err = gEchoClient.Init(&ExchangeMgr);
if (err != WEAVE_NO_ERROR)
{
printf("WeaveEchoClient.Init failed: %s\n", nl::ErrorStr(err));
exit(EXIT_FAILURE);
}
// Arrange to get a callback whenever an Echo Response is received.
gEchoClient.OnEchoResponseReceived = HandleEchoResponseReceived;
// Set the destination fields before initiating the Weave connection.
IPAddress::FromString("fd00:0:1:1::2", gDestv6Addr);
// Derive the destination node id from the IPv6 address.
gDestNodeId = IPv6InterfaceIdToWeaveNodeId(gDestv6Addr.InterfaceId());
// Set the destination port to be the Weave port.
gDestPort = WEAVE_PORT;
// Wait until the Weave connection is established.
while (!gClientConEstablished)
{
// Start the Weave connection to the weave echo responder.
StartClientConnection();
DriveIO();
if (gConnectAttempts > MAX_CONNECT_ATTEMPTS)
{
exit(EXIT_FAILURE);
}
}
// Connection has been established. Now send the EchoRequests.
for (int i = 0; i < MAX_ECHO_COUNT; i++)
{
// Send an EchoRequest message.
SendEchoRequest();
// Wait for response until the Echo interval.
while (!EchoIntervalExpired())
{
DriveIO();
fflush(stdout);
}
// Check if expected response was received.
if (gWaitingForEchoResp)
{
printf("No response received\n");
gWaitingForEchoResp = false;
}
}
// Done with all EchoRequests and EchoResponses.
CloseClientConnection();
// Shutdown the EchoClient.
gEchoClient.Shutdown();
// Shutdown the Weave stack.
ShutdownWeave();
return EXIT_SUCCESS;
}