emNet TCP EchoServer (Sample)
Jump to navigation
Jump to search
| IP_TCP_Echo_Server_Sample.c | |
|---|---|
| Requires modifications | No |
| Download | IP_TCP_Echo_Server_Sample.c |
This Sample starts a TCP echo server and waits for a IP_TCP_Echo_Client_Sample.c to connect and send a payload to echo back.
Code
/*********************************************************************
* (c) SEGGER Microcontroller GmbH *
* The Embedded Experts *
* www.segger.com *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
Purpose : Sample program for embOS & emNet
Starts a TCP echo server on port SERVER_PORT and waits for
the IP_TCP_Echo_Client.c to connect to it and send a payload.
The server tries to read one chunk as big as possible from the
stream and echoes the data back. Using this method the payload
sent can be as small as a single byte.
The following is a sample of the output to the terminal window:
.
.
.
0:017 Server - Waiting for a client to connect on port 1234 .
5:037 Server - New client connected.
.
.
.
Notes : For compatibility with interfaces that need to connect in
any way this sample calls connect and disconnect routines
that might not be needed in all cases.
This sample can be used for Ethernet and dial-up interfaces
and is configured to use the last registered interface as
its main interface.
*/
#include "RTOS.h"
#include "BSP.h"
#include "IP.h"
/*********************************************************************
*
* Configuration
*
**********************************************************************
*/
#define USE_RX_TASK 0 // 0: Packets are read in ISR, 1: Packets are read in a task of its own.
//
// Server sample.
//
#define SERVER_PORT 1234 // Remote port the server listens on.
#define TIMEOUT 5000 // Timeout after which the server kicks an idle client [ms].
#define USE_ZERO_COPY 0 // Whether to use TCP receive zero-copy API or not.
//
// Task priorities.
//
enum {
TASK_PRIO_IP_SERVER = 150
,TASK_PRIO_IP_TASK // Priority should be higher than all IP application tasks.
#if USE_RX_TASK
,TASK_PRIO_IP_RX_TASK // Must be the highest priority of all IP related tasks.
#endif
};
//
// Task stack sizes that might not fit for some interfaces (multiples of sizeof(int)).
//
#ifndef APP_TASK_STACK_OVERHEAD
#define APP_TASK_STACK_OVERHEAD 0
#endif
/*********************************************************************
*
* Static data
*
**********************************************************************
*/
static IP_HOOK_ON_STATE_CHANGE _StateChangeHook;
static int _IFaceId;
//
// Task stacks and Task-Control-Blocks.
//
static OS_STACKPTR int _IPStack[TASK_STACK_SIZE_IP_TASK/sizeof(int)]; // Stack of the IP_Task.
static OS_TASK _IPTCB; // Task-Control-Block of the IP_Task.
#if USE_RX_TASK
static OS_STACKPTR int _IPRxStack[TASK_STACK_SIZE_IP_RX_TASK/sizeof(int)]; // Stack of the IP_RxTask.
static OS_TASK _IPRxTCB; // Task-Control-Block of the IP_RxTask.
#endif
static OS_STACKPTR int _ServerStack[(1024 + APP_TASK_STACK_OVERHEAD)/sizeof(int)]; // Stack of the server task.
static OS_TASK _ServerTCB; // Task-Control-Block of the server task.
//
// Sample specific.
//
#if (USE_ZERO_COPY == 0)
static U32 _abData[2048 / sizeof(U32)]; // Enough to fit a full Ethernet packet of 1514 (+ 4 bytes CRC that we do not get).
// U32 to make sure we have a word alignment.
#else
static OS_EVENT _RxEvent;
#endif
static int _Status;
static int _NumBytesToSend;
/*********************************************************************
*
* Prototypes
*
**********************************************************************
*/
#ifdef __cplusplus
extern "C" { /* Make sure we have C-declarations in C++ programs */
#endif
void MainTask(void);
#ifdef __cplusplus
}
#endif
/*********************************************************************
*
* Local functions
*
**********************************************************************
*/
/*********************************************************************
*
* _OnStateChange()
*
* Function description
* Callback that will be notified once the state of an interface
* changes.
*
* Parameters
* IFaceId : Zero-based interface index.
* AdminState: Is this interface enabled ?
* HWState : Is this interface physically ready ?
*/
static void _OnStateChange(unsigned IFaceId, U8 AdminState, U8 HWState) {
//
// Check if this is a disconnect from the peer or a link down.
// In this case call IP_Disconnect() to get into a known state.
//
if (((AdminState == IP_ADMIN_STATE_DOWN) && (HWState == 1)) || // Typical for dial-up connection e.g. PPP when closed from peer. Link up but app. closed.
((AdminState == IP_ADMIN_STATE_UP) && (HWState == 0))) { // Typical for any Ethernet connection e.g. PPPoE. App. opened but link down.
IP_Disconnect(IFaceId); // Disconnect the interface to a clean state.
}
}
#if (USE_ZERO_COPY != 0)
/*********************************************************************
*
* _cbOnRx()
*
* Function description
* TCP zero-copy receive callback. Signals the main application
* once a packet has been received or the status has been updated.
*
* Parameters
* hSock : Socket handle.
* pPacket: Packet with data received.
* Code : * == 0: O.K.
* * < 0: Error or IP_ERR_SHUTDOWN if socket closed by peer.
*
* Return value
* IP_OK : Application has taken all data out of packet.
* The packet is freed by the stack.
* IP_OK_KEEP_PACKET : Application now own the packet.
* IP_OK_KEEP_IN_SOCKET: The packet is forwarded to the socket to
* be received using recv() .
*/
static int _cbOnRx(long hSock, IP_PACKET* pPacket, int Code) {
int NumBytes;
if (Code < 0) { // Error or close ?
_Status = SOCKET_ERROR;
_NumBytesToSend = SOCKET_ERROR + 1; // The application task compares _Status and _NumBytesToSend .
} else {
//
// Send the response.
//
NumBytes = pPacket->NumBytes;
_NumBytesToSend = NumBytes;
_Status = send(hSock, (const char*)pPacket->pData, NumBytes, 0);
}
//
// Signal task that something has happened.
//
OS_EVENT_Set(&_RxEvent);
return IP_OK;
}
#endif
/*********************************************************************
*
* _ServerTask()
*
* Function description
* Initializes a socket and starts the server. Then waits to receive
* a TCP payload to echo it back to the client.
*/
static void _ServerTask(void) {
struct sockaddr_in InAddr;
U32 Timeout;
int hSockListen;
int hSock;
int Error;
#if (USE_ZERO_COPY != 0)
char IsTimedOut;
OS_EVENT_Create(&_RxEvent);
#endif
Timeout = TIMEOUT;
//
// Open parent socket.
//
hSockListen = socket(AF_INET, SOCK_STREAM, 0);
if (hSockListen <= 0) {
getsockopt(hSockListen, SOL_SOCKET, SO_ERROR, &Error, sizeof(Error));
IP_Warnf_Application("Error opening parent socket: %s", IP_Err2Str(Error));
while (1) {
BSP_ToggleLED(0);
OS_Delay(20);
}
}
InAddr.sin_family = AF_INET;
InAddr.sin_port = htons(SERVER_PORT);
InAddr.sin_addr.s_addr = INADDR_ANY;
(void)bind(hSockListen, (struct sockaddr*)&InAddr, sizeof(InAddr));
(void)listen(hSockListen, 1); // Allow one client connection.
#if (USE_ZERO_COPY != 0)
//
// Set callback for the parent socket.
// In newer emNet versions the callback is inherited to child sockets, making sure that
// even very early data received from peer ends up in the callback instead of the socket buffer.
//
setsockopt(hSockListen, SOL_SOCKET, SO_CALLBACK, (void*)_cbOnRx, 0); // Register Rx callback to process data.
#endif
//
// Retry loop in case of remote-disconnect/timeout.
//
for (;;) {
IP_Logf_Application("Waiting for a client to connect on port %d .", SERVER_PORT);
//
// Accept new client
//
hSock = accept(hSockListen, NULL, NULL);
if (hSock == SOCKET_ERROR) {
continue; // Error but do not care and wait for next client.
}
IP_Logf_Application("New client connected.");
#if (USE_ZERO_COPY != 0)
setsockopt(hSock, SOL_SOCKET, SO_CALLBACK, (void*)_cbOnRx, 0); // Register Rx callback to process data.
#endif
setsockopt(hSock, SOL_SOCKET, SO_RCVTIMEO, &Timeout, sizeof(Timeout)); // Set timeout to wait for an echo request.
//
// Inner receive/send loop.
//
for (;;) {
#if (USE_ZERO_COPY == 0)
//
// Receive data.
//
_Status = recv(hSock, (char*)&_abData[0], sizeof(_abData), 0);
if (_Status <= 0) { // Error, disconnect or timeout ?
IP_Logf_Application("Receive error!");
if (_Status == SOCKET_ERROR) {
getsockopt(hSock, SOL_SOCKET, SO_ERROR, &Error, sizeof(Error));
IP_Logf_Application("Error: %s", IP_Err2Str(Error));
}
break;
}
//
// Send data.
//
_NumBytesToSend = _Status;
_Status = send(hSock, (const char*)&_abData[0], _NumBytesToSend, 0);
if (_Status != _NumBytesToSend) { // Error or disconnect ?
IP_Logf_Application("Send error!");
if (_Status == SOCKET_ERROR) {
getsockopt(hSock, SOL_SOCKET, SO_ERROR, &Error, sizeof(Error));
IP_Logf_Application("Error: %s", IP_Err2Str(Error));
}
break;
}
if (_Status != _NumBytesToSend) { // Error or disconnect ?
IP_Logf_Application("Send error!");
if (_Status == SOCKET_ERROR) {
getsockopt(hSock, SOL_SOCKET, SO_ERROR, &Error, sizeof(Error));
IP_Logf_Application("Error: %s", IP_Err2Str(Error));
}
break;
}
#else
//
// Receive data in callback and send back from callback.
// Wait for signal from callback to evaluate the result.
//
IsTimedOut = OS_EVENT_WaitTimed(&_RxEvent, TIMEOUT); // Wait for the callback to signal a status update or timeout to occur.
if ((IsTimedOut != 0) || (_Status != _NumBytesToSend)) { // Error or disconnect ?
IP_Logf_Application("Receive/send error!");
if (_Status == SOCKET_ERROR) {
getsockopt(hSock, SOL_SOCKET, SO_ERROR, &Error, sizeof(Error));
IP_Logf_Application("Error: %s", IP_Err2Str(Error));
}
break;
}
#endif
}
closesocket(hSock);
IP_Logf_Application("Client disconnected.");
}
}
/*********************************************************************
*
* Global functions
*
**********************************************************************
*/
/*********************************************************************
*
* MainTask()
*
* Function description
* Main task executed by the RTOS to create further resources and
* running the main application.
*/
void MainTask(void) {
IP_ConfigDoNotAddLowLevelChecks();
IP_Init();
IP_AddLogFilter(IP_MTYPE_APPLICATION);
_IFaceId = IP_INFO_GetNumInterfaces() - 1; // Get the last registered interface ID as this is most likely the interface we want to use in this sample.
OS_SetPriority(OS_GetTaskID(), TASK_PRIO_IP_TASK); // For now, this task has highest prio except IP management tasks.
OS_CREATETASK(&_IPTCB , "IP_Task" , IP_Task , TASK_PRIO_IP_TASK , _IPStack); // Start the IP_Task.
#if USE_RX_TASK
OS_CREATETASK(&_IPRxTCB , "IP_RxTask", IP_RxTask , TASK_PRIO_IP_RX_TASK, _IPRxStack); // Start the IP_RxTask, optional.
#endif
OS_CREATETASK(&_ServerTCB, "Server" , _ServerTask, TASK_PRIO_IP_SERVER , _ServerStack); // Start the server task.
IP_AddStateChangeHook(&_StateChangeHook, _OnStateChange); // Register hook to be notified on disconnects.
IP_Connect(_IFaceId); // Connect the interface if necessary.
OS_SetPriority(OS_GetTaskID(), 255); // Now this task has highest prio for real-time application. This is only allowed when this task does not use blocking IP API after this point.
while (IP_IFaceIsReadyEx(_IFaceId) == 0) {
OS_Delay(50);
}
while (1) {
BSP_ToggleLED(1);
OS_Delay(200);
}
}
/*************************** End of file ****************************/