Echo.c

Echo.c example details

To verify this program, first 'ping' the DSP:

  >ping 192.168.168.200

If the settings and network connections are correct, the pings will be replied.

The TCP Echo server will re-send (echo) anything it receives. To verify this, start Netcat (nc) with the following command

>nc 192.168.168.200 5031
           |         |
           |         +-- port 5031 = User Echo Port
           +------------ the DSP's IP address

Any characters you type in the console window will be returned. Netcat doesn't send the characters immediately, it will send the entire string following a terminating CR. Please note that the maximum length of the character string is limited by the size of the buffer used for TCP echo (MAX_DATA = 1460).

See also
TCP Transmission Control Protocol
/***************************************************************************//**
@file Echo.c
@brief TCP echo server on port 5031.
@verbatim
_ _ _
__| | ___(_) ____ _ __ | |_
/ _` | / __| |/ _` | '_ \| __|
| (_| | _ \__ \ | (_| | | | | |_
\__,_|(_) ___/_|\__, |_| |_|\__|
Signalprocessing |___/ Technology
@endverbatim
@author D.SignT GmbH & Co. KG, Claus Hermbusche
@date 2019-06-03
@anchor ECHOEX
@details
This program works as a TCP echo server with quad buffering (default).
A define MAX_TCP_BUFFERS controls the number of buffers.
Uncomment define USE_STATIC_MEMORY to use static instead of dynamic memory
for buffering.
To achieve a better TCP performance, MAX_TCP_BUFFER_SIZE can be increased
by multiple of TCP_MAX_PACKET_SIZE. Also linking the tcp_buffer to internal
memory (if available) may increase the performance significantly.
To verify this program, first 'ping' the DSP:
>ping 192.168.168.200
or
>ping mydemo
If the settings and network connections are correct, the pings
will be replied.
The TCP Echo server will re-send (echo) anything it receives.
To verify this, start NetCat (nc) with the following command
>nc 192.168.168.200 5031
| |
| +-- port 5031 = User Echo Port
+------------ the DSP's IP address
or
>nc mydemo 5031
| |
| +------ port 5031 = User Echo Port
+------------ the DSP hostname
Any characters you type in the console window will be returned.
NetCat doesn't send the characters immediately, it will send
the entire string following a terminating CR. Please note that
the maximum length of the character string is limited by the
size of the buffer used for TCP echo (MAX_TCP_BUFFER_SIZE).
@cond Software License Agreement
Copyright (C) 2001-2019 D.SignT GmbH & Co. KG - http://www.dsignt.de
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
Neither the name of D.SignT GmbH & Co. KG nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
Disclaimer
THIS SOFTWARE IS PROVIDED BY D.SIGNT GMBH & CO. KG "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL D.SIGNT GMBH & CO. KG BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@endcond
*******************************************************************************/
/*******************************************************************************
include stdtypes.h to avoid data type mismatch
*******************************************************************************/
/*******************************************************************************
include Runtime Source
*******************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <inttypes.h>
#include <time.h>
#include <string.h>
/*******************************************************************************
network support functions
*******************************************************************************/
#include <Libs/NETlib/net.h> /* D.Module network support */
/*******************************************************************************
board specific functions
*******************************************************************************/
#include <BoardSupport/inc/BoardSpecific.h> /* board support functions */
/*******************************************************************************
common support functions
*******************************************************************************/
#include <Common/Common.h> /* on exit function */
#include <Common/uartio.h> /* UART support */
#include <Common/timer.h> /* timer setting */
#include <Common/CPrintf.h> /* CPrintf defines */
/*******************************************************************************
network configuration
*******************************************************************************/
#include <BoardSupport/config/netconfig.c> /* network configuration */
/*******************************************************************************
Uncomment USE_STATIC_MEMORY to use static memory instead of dynamic.
This definition is required before "nbuffer.h" include.
Don't forget to change heap size and linker command file to fit these buffers
into memory.
*******************************************************************************/
#define USE_STATIC_MEMORY TRUE
#define MAX_NBUFFER_SIZE (1500)
#include <Common/nbuffer.h> /* network buffer support */
#ifdef __cplusplus
extern "C" {
#endif /* !__cplusplus */
/*******************************************************************************
local prototypes
*******************************************************************************/
#pragma CODE_SECTION(tcp_echo_cb , ".commontext")
int32_t tcp_echo_cb (SOCKET *so, void *data, uint32_t len, uint32_t ec);
#pragma CODE_SECTION(process_data , ".commontext")
void process_data (SOCKET *so);
#ifdef __cplusplus
} // extern "C"
#endif
/*******************************************************************************
* *
* DEFINES *
* *
*******************************************************************************/
/*******************************************************************************
define the number of buffers. At least two buffers are necessary to perform
ping-pong buffering (one buffer - ping - is used to receive data while the
other one - pong - is used to process data).
*******************************************************************************/
#define MAX_TCP_BUFFERS 4
/*******************************************************************************
* *
* GLOBALS *
* *
*******************************************************************************/
/*******************************************************************************
program name
*******************************************************************************/
char *program_name = "Echo";
extern far char *domain_name;
/*******************************************************************************
create data buffer
NBuffer_t is defined in nbuffer.h
*******************************************************************************/
#ifdef USE_STATIC_MEMORY
#pragma DATA_SECTION(tcp_buffer , ".data_slow");
#endif
NBuffer_t tcp_buffer[MAX_TCP_BUFFERS]; /* 4 buffers for TCP echo */
/*******************************************************************************
TCP socket
*******************************************************************************/
SOCKET *tcp_echo_so = INVALID_SOCKET; /* TCP socket */
/*******************************************************************************
create and pre-initialize control structure
NBCtl_t is defined in nbuffer.h
*******************************************************************************/
{
tcp_buffer, /* buffers */
sizeof(NBuffer_t), /* size of NBuffer_t */
0, /* used */
0, /* out of buffer memory */
BUFFER_EMPTY, /* receive index */
BUFFER_EMPTY, /* process index */
MAX_TCP_BUFFERS, /* max buffers */
0, /* use static memory */
#else
1, /* use dynamic memory */
#endif
MAX_NBUFFER_SIZE /* NBuffer size limit */
};
/*******************************************************************************
* *
* FUNCTIONS *
* *
*******************************************************************************/
/*******************************************************************************
@brief This function is called each time new TCP data is received or the
TCP event status has changed (MSB set in error code)
@param so - socket
@param data - pointer to received data
@param len - received data length
@param ec - error code
@return SOCKET_CB_OK - if message processed
@return SOCKET_CB_CLOSED - if socket was closed
@details Function tcp_echo_cb is immediately called from net_isq() in case
of new data waiting in receive buffer or in case of socket events
which require user intervention. Since parameter data, len and ec
are not used in this example, UNREFERENCED_PARAMETER is used to
suppress compiler unused warnings.
Function net_recv_event_handler() is used to detect new data or
to handle socket events.
*******************************************************************************/
int32_t tcp_echo_cb (SOCKET *so, void *data, uint32_t len, uint32_t ec)
{
/***************************************************************************
locals
***************************************************************************/
NBCtl_t *bctl = (NBCtl_t *)so-> user_pointer; /* get buffer control */
int32_t data_length; // net_recv_event_handler() return parameter
/***************************************************************************
suppress unused parameter warning
***************************************************************************/
UNREFERENCED_PARAMETER(data); // replaced by bctl-> Buf[].Data
UNREFERENCED_PARAMETER(ec); // handled in net_recv_event_handler()
UNREFERENCED_PARAMETER(len); // len is ignored, data_len is used instead
/***************************************************************************
Check events
If NULL is passed as pLog parameter, no message is printed to output.
Use net_recv_event_handler() to determine the data length waiting in the
receive buffer
***************************************************************************/
data_length = net_recv_event_handler (so, CPrintf); /* with messages */
// data_length = net_recv_event_handler (so, NULL); /* without messages */
/***************************************************************************
net_recv_event_handler() returns the amount of data waiting in the buffer
***************************************************************************/
if (data_length)
{
// CPrintf ("received %"PRId32" bytes\r\n", data_length);
if ( bctl == NULL )
{
/*******************************************************************
no control structure available, buffer processing not possible
*******************************************************************/
return (SOCKET_CB_OK);
} // if
/***********************************************************************
new data is available in buffer that has been previously assigned by
socket_define_callback() or set_recv_buffer() function.
The receive index bctl-> ri should always point to the active buffer
and is maintained in NBufferAcquireBuffer()
***********************************************************************/
/***********************************************************************
save packet size, useful for later processing
***********************************************************************/
bctl-> Buf[bctl-> ri].Size = data_length;
/***********************************************************************
set process index to this buffer, if not already set.
this starts the processing task in main.
if bctl-> pi is not BUFFER_EMPTY, processing is already in progress
***********************************************************************/
if ( bctl-> pi == BUFFER_EMPTY )
{
bctl-> pi = bctl-> ri;
} // if
/***********************************************************************
malloc space for new data
***********************************************************************/
if ( NBufferAcquireBuffer (so) == NULL)
{
/*******************************************************************
allocation failed due to all buffers in use.
this may happen when the receive task works faster than the signal
processing task. Send a zero window packet to the remote side to
stop the TCP and set OutOfBuffer flag to handle this condition
later
*******************************************************************/
bctl-> OutOfBuffer = TRUE;
} // if
}
/***************************************************************************
Return true, if socket was not closed before
***************************************************************************/
return (SOCKET_CB_OK);
}
/*******************************************************************************
@brief Process TCP data, send back echo
@param so - socket
@return -
@details This function monitors buffer allocation problems and tries to
allocate a new buffer if OutOfBuffer was signalled.
Furthermore the TCP input queue is checked, the buffer is processed
and the echo is sent back. After sending back the data, the buffer
is freed.
*******************************************************************************/
void process_data (SOCKET *so)
{
/***************************************************************************
locals
***************************************************************************/
NBCtl_t *bctl = (NBCtl_t *)so-> user_pointer; /* get buffer control */
char *buffer;
uint32_t size;
if ( bctl == NULL )
{
/***********************************************************************
no control structure available
***********************************************************************/
return;
} // if
/***************************************************************************
if allocation failed due to all buffers in use, OutOfBuffer flag was
set in tcp_echo_cb(). try to allocate new buffer and send window update on
success
***************************************************************************/
if ( bctl-> OutOfBuffer )
{
if ( NBufferAcquireBuffer (so) != NULL )
{
/*******************************************************************
allocation successful,
reset OutOfBuffer flag,
send window update to start the TCP
*******************************************************************/
bctl-> OutOfBuffer = FALSE;
} // if
} // if
/***************************************************************************
check received TCP messages
process index bctl-> pi is set by the first received packet in tcp_echo_cb()
and signals data waiting for processing
***************************************************************************/
if ( bctl-> pi != BUFFER_EMPTY )
{
/***********************************************************************
process buffer if necessary
the next data is bctl-> Buf[bctl-> pi].Data
***********************************************************************/
buffer = (char*)bctl-> Buf[bctl-> pi].Data;
size = (uint32_t)bctl-> Buf[bctl-> pi].Size;
/***********************************************************************
send data back (TCP echo), wait for completion
***********************************************************************/
net_send_safe (so, buffer , size, TCP_WAIT);
/***********************************************************************
since wait flag was set in net_send_safe() call before, it's safe
to free this buffer here
***********************************************************************/
if ( NBufferFreeBuffer (so) )
{
/*******************************************************************
error occurred, no buffer control structure available
assign buffer control structure to socket-> user_pointer
during initialization
*******************************************************************/
prg_exit ("out of memory error");
} // if
}
}
/*******************************************************************************
@brief Main application
@param -
@return never
*******************************************************************************/
#pragma CODE_SECTION(main , ".commontext");
int main ( void )
{
/***************************************************************************
locals
***************************************************************************/
int main_loop = 1; /* main loop switch, set to 0 to exit */
timeval stamp1, stamp2, delta; /* used to determine startup time */
char conversion_buffer[16];
/***************************************************************************
initialize application (e.g. timer clocks, PLL settings, EMIF etc.)
(ref. \Common\Common.c)
***************************************************************************/
/***************************************************************************
select output device for CPrintf (ref. \Common\cprintf.c)
possible settings:
CPRINTF_UART_OUTPUT -> output to UART
CPRINTF_CCS_OUTPUT -> output to CCS
CPRINTF_UART_OUTPUT | CPRINTF_CCS_OUTPUT -> output to UART and CCS
***************************************************************************/
/***************************************************************************
print a start up message
***************************************************************************/
/**************************************************************************/
// CPrintfProgress (" Heap check ");
// at least 0x2000 bytes required for this app
/**************************************************************************/
// CPrintfProgressSuccess();
/**************************************************************************/
CPrintfProgress (" Setup system time ");
// 1 milli seconds resolution
/**************************************************************************/
CPrintf (" *** timer %d mapped to CPU int %d ***\r\n",
/**************************************************************************/
CPrintfProgress (" Enable interrupts ");
/**************************************************************************/
/**************************************************************************/
CPrintfProgress (" Start system timer ");
/**************************************************************************/
CPrintf (" *** timer %d running at %"PRId32" Hz ***\r\n", SystemTimerDev, RES_SECONDS/GetSystemTimerRes());
/***************************************************************************
measure network initialization time
***************************************************************************/
stamp1 = GetTimeStamp();
/**************************************************************************/
CPrintfProgress (" Initialize network ");
/**************************************************************************/
InitializeNetwork ( 64); // 64 bytes for ping
/**************************************************************************
open sockets
**************************************************************************/
5031,
{
prg_exit ("socket_open() error"); /* possibly insufficient heap */
} // if
/***************************************************************************
attach buffer control structure to socket
(required for NBufferAcquireBuffer() later)
***************************************************************************/
tcp_echo_so-> user_pointer = (void *)&bcontrol;
/***************************************************************************
initially malloc space for data
NBufferAcquireBuffer() allocates new buffer space and modifies the
bcontrol structure data buffer pointer and receive index
***************************************************************************/
{
prg_exit ("out of memory error");
} // if
/***************************************************************************
install callback function
NBufferAcquireBuffer() has modified receive index ri to point to the next
buffer
***************************************************************************/
/***************************************************************************
set keep alive time 10 seconds (three unanswered packets within 10 seconds)
***************************************************************************/
stamp2 = GetTimeStamp();
tv_interval (&delta, &stamp1, &stamp2);
CPuts (" network startup time [sec]: ");
"%"PRId32".%03"PRId32"\r\n"
delta.tv_sec,
delta.tv_usec/1000);
/***************************************************************************
main program loop: set main_loop to 0 to exit loop
***************************************************************************/
CPuts ("\r\n Entering main loop ...\r\n");
while ( main_loop )
{
/***********************************************************************
process net_isq()
***********************************************************************/
net_isq (); // process ISQ
/***********************************************************************
monitor link status
***********************************************************************/
/***********************************************************************
try to detect IP assignment
if DHCP is used, the assigned IP address may change
***********************************************************************/
{
/*******************************************************************
print what to do next
*******************************************************************/
CPrintf (" Establish a TCP connection on port %d:\r\n", tcp_echo_so-> src_port);
if (domain_name) CPrintf (">nc %s.%s %d\r\n or\r\n",
tcp_echo_so-> src_port);
CPrintf (">nc %s %d\r\n",
inet_ntoa(get_ip_address(0), conversion_buffer),
tcp_echo_so-> src_port);
CPuts (" Any characters you type into the nc console will be echoed back\r\n when you press enter until the connection is closed.\r\n");
}
/***********************************************************************
process data
***********************************************************************/
/***********************************************************************
show that the program is running, perform symbol animation
***********************************************************************/
}
/***************************************************************************
exit program, shut down peripherals
***************************************************************************/
return (0);
}