NetBurner 3.3.4
PDF Version
TCP Server

A TCP server is basically an application that creates a listening socket, and then listens on the socket for incoming connections. When an incoming connection request is received, tit must be accepted before communication can begin. A web server is an example of a TCP server. A web server listens on port number 80 for incoming connections. Once a connection is established, the web browser sends a GET request to the web server, which then sends the requested information and terminates the connection.

To connect to a TCP server you must specify a port number. A port number is a 16-bit value. Since you must know the port number before connecting, many port numbers have been defined for common protocols, and are called well-known port numbers. Some of these values are shown below:

FTP 21
Telnet 23
SMTP 25
DNS 53
TFTP 69
HTTP 80
POP3 110
NTP 123

NetBurner TCP Server Basics

When creating a TCP server with the NetBurner you will use the following functions:

  • listen(): Open a listening socket on a specific port number
  • accept(): Accept a connection request on a listening port
  • I/O functions to send and receive data such as read(), write(), fdprintf(), writestring(), etc.
  • close(): Close a network socket

Simple TCP Server Example

This example is located in \nburn\examples\TCP. It will listen on port 23 for incoming connections, send a sign on message to the client when a connection is made, and display all received data to the debug serial port. A telnet program on a host PC can be used to connect to the server.

#include <predef.h>
#include <stdio.h>
#include <tcp.h>
#include <nbrtos.h>
#include <iosys.h>
#include <init.h>
#include <fdprintf.h>
const char *AppName = "Simple TCP Server Example";
#define TCP_LISTEN_PORT 23 // Telnet port number
#define RX_BUFSIZE (4096)
char RXBuffer[RX_BUFSIZE];
// Allocate task stack for TCP listen task
uint32_t TcpServerTaskStack[USER_TASK_STK_SIZE];
/*-------------------------------------------------------------------
* TCP Server Task
*------------------------------------------------------------------*/
void TcpServerTask(void * pd)
{
int listenPort = (int) pd;
// Set up the listening TCP socket
int fdListen = listen(INADDR_ANY, listenPort, 5);
if (fdListen > 0)
{
IPADDR clientAddress;
uint16_t clientPort;
while(1)
{
/* The accept() function will block until a TCP client requests a connection. Once a client
* connection is accepting, the file descriptor fdAccept is used to read/write to it.
*/
iprintf( "Waiting for connection on port %d...\n", listenPort );
int32_t fdAccept = accept(fdListen, &clientAddress, &clientPort, 0);
iprintf("Connected to: %I\r\n", GetSocketRemoteAddr(fdAccept));
writestring(fdAccept, "Welcome to the NetBurner TCP Server\r\n");
fdprintf(fdAccept, "You are connected to IP Address %I:%d\r\n", GetSocketRemoteAddr(fdAccept), GetSocketRemotePort(fdAccept) );
while (fdAccept > 0)
{
/* Loop while connection is valid. The read() function will return 0 or a negative number if the
* client closes the connection, so we test the return value in the loop. Note: you can also use
* ReadWithTimout() in place of read to enable the connection to terminate after a period of inactivity.
*/
int n = 0;
do
{
n = read( fdAccept, RXBuffer, RX_BUFSIZE );
RXBuffer[n] = '\0';
iprintf( "Read %d bytes: %s\n", n, RXBuffer );
} while ( n > 0 );
iprintf("Closing client connection: %I\r\n", GetSocketRemoteAddr(fdAccept) );
close(fdAccept);
fdAccept = 0;
}
} // while(1)
} // while listen
}
/*-------------------------------------------------------------------
User Main
------------------------------------------------------------------*/
void UserMain(void * pd)
{
init();
// Create TCP Server task
OSTaskCreatewName( TcpServerTask,
(void *)TCP_LISTEN_PORT,
&TcpServerTaskStack[USER_TASK_STK_SIZE] ,
TcpServerTaskStack,
MAIN_PRIO - 1, // higher priority than UserMain
"TCP Server" );
while (1)
{
OSTimeDly( TICKS_PER_SECOND * 5 );
}
}
Used to hold and manipulate IPv4 and IPv6 addresses in dual stack mode.
Definition: ipv6_addr.h:40
int read(int fd, char *buf, int nbytes)
Read data from a file descriptor (fd). This function will block forever until at least one byte is av...
Definition: fileio.cpp:271
int close(int fd)
Close the specified file descriptor and free the associated resources.
Definition: fileio.cpp:101
int writestring(int fd, const char *str)
Write a null terminated ascii string to the stream associated with a file descriptor (fd)....
Definition: iosys.cpp:399
uint8_t OSTaskCreatewName(void(*task)(void *dptr), void *data, void *pstktop, void *pstkbot, uint8_t prio, const char *name)
This function creates a new task.
void OSTimeDly(uint32_t to_count)
Delay the task until the specified value of the system timer ticks. The number of system ticks per se...
Definition: nbrtos.h:1409
uint16_t GetSocketRemotePort(int fd)
Returns the port number of the remote host associated with the connection.
Definition: tcp.cpp:1975
int accept(int listening_socket, IPADDR *address, uint16_t *port, uint16_t timeout)
Accept an incoming connection from a listening socket.
Definition: tcp.cpp:1014
IPADDR GetSocketRemoteAddr(int fd)
Returns the IP address of the remote host associated with the specified file descriptor.
Definition: tcp.h:472
int listen(const IPADDR &addr, uint16_t port, uint8_t maxpend=5)
Listen for incoming connections on the specified network interface IP address.
Definition: tcp.h:226
void init()
System initialization. Normally called at the beginning of all applications.
Definition: init.cpp:29
NetBurner System Initialization Header File.
NetBurner I/O System Library API.
NetBurner Real-Time Operating System API.
NetBurner TCP API.



This is an extremely simple example designed to illustrate how the accept() and listen() calls can be used. It only listens to a single port number, and processes a single connection at a time. Any information sent from the Client will be displayed in the MTTTY window. The application does not have the capability to terminate the incoming connection.

There are #define options for the TCP listen port number and the incoming TCP buffer storage array size. RXBuffer[] is then declared and will hold the received data. The listen() function call sets up a socket to listen for an incoming connection from any IP address on port number 23, the telnet port number.

If the listen() succeeds in creating a listening socket, we then enter a second while loop. The application will block at the accept() function call until an incoming connection request is received, such as when the telnet program on a PC attempts to connect. When the connection is established, the accept() function returns and the sign-on message is sent to the telnet application.

We now enter the do loop: while(n > 0). The read() function will block until data is received or an error occurs such as the client terminating the connection. When data is sent from the telnet application, the read() function will return with the data in the RXBuffer[] array. The application will stay in this while loop until the connection is terminated by the telnet client (or you reset the NetBurner device). If the connection is broken by the telnet client, the application will then loop back to the accept() function call and wait for another incoming connection.

Advanced TCP Server for Multiple Connections

In the previous example the TCP server processed only a single incoming connection. The select() function has the ability to pend on multiple file descriptors, which can used for TCP or serial connections. The example below demonstrates how the TCP Server can be written using select().

/*---------------------------------------------------------------------------
This example creates a TCP server that listens on the specified TCP port
number and can handle multiple TCP connections simultaneously (10 in this
example). The select() function is a great way method to pend and process
multiple connections.
An easy way to test the example is to use multiple Telnet sessions to create
simultaneous connections to the NetBurner device. Status messages are sent
out stdio to the debug serial port, and to the client TCP connections.
*-----------------------------------------------------------------------------*/
#include <predef.h>
#include <constants.h>
#include <utils.h>
#include <system.h>
#include <iosys.h>
#include <stdio.h>
#include <ctype.h>
#include <tcp.h>
#include <init.h>
#include <ipshow.h>
const char* AppName = "TCP Multiple Sockets Example";
#define listenPort (23) // TCP port number to listen on
#define maxConnections (10) // Max number of file descriptors/connections
#define readBufferSize (1024) // Connection read buffer size
int32_t fdArray[maxConnections]; // Array of TCP file descriptors
/*-----------------------------------------------------------------------------
* User Main
*------------------------------------------------------------------------------*/
void UserMain( void* pd )
{
init();
WaitForActiveNetwork(TICKS_PER_SECOND * 10);
showIpAddresses();
// Listen for incoming TCP connections. You only need to call listen() one time.
// Any incoming IP address is allowed, queue up to 5 connection requests at one time
int32_t fdListen = listen( INADDR_ANY, listenPort, 5 );
iprintf( "Listening for incoming connections on port %d\r\n", listenPort );
while ( 1 )
{
// Declare file descriptor sets for select()
fd_set readFds;
fd_set errorFds;
// Init the fd sets
FD_ZERO( &readFds );
FD_ZERO( &errorFds );
// Configure the fd sets so select() knows what to process. In this case any fd data to be read, or an error
for ( int32_t i = 0; i < maxConnections; i++ )
{
if ( fdArray[i] ) // The fd in the array will be > 0 if open and valid, so reset the file descriptor sets
{
FD_SET( fdArray[i], &readFds );
FD_SET( fdArray[i], &errorFds );
}
}
// select() should also process the listen fd
FD_SET( fdListen, &readFds );
FD_SET( fdListen, &errorFds );
// select() will block until any fd has data to be read, or has an error. When select() returns,
// readFds and/or errorFds variables will have been modified to reflect the events.
select( FD_SETSIZE, &readFds, ( fd_set * )0, &errorFds, 0 );
// If the listen fd has a connection request, accept it.
if ( FD_ISSET( fdListen, &readFds ) )
{
IPADDR clientIp;
uint16_t clientPort;
int fdAccept = accept( fdListen, &clientIp, &clientPort, 0 );
// If accept() returned, find an open fd array slot
if ( fdAccept > 0 )
{
for ( int32_t i = 0; i < maxConnections; i++ )
{
if ( fdArray[i] == 0 )
{
fdArray[i] = fdAccept;
writestring( fdAccept, "Welcome to the NetBurner Multi-Socket TCP Server! 'Q' to quit." );
iprintf( "Added connection on fd[%d] = %d, Client IP: %I:%d\r\n",
i, fdAccept, GetSocketRemoteAddr(fdAccept), GetSocketRemotePort(fdAccept) );
fdAccept = 0;
break;
}
}
}
// If no array positions are open, close the connection
if ( fdAccept )
{
writestring( fdAccept, "I am sorry, but the server is full\r\n" );
iprintf("Server Full\r\n");
close( fdAccept );
}
}
// If the listen fd has an error, close it and reopen
if ( FD_ISSET( fdListen, &errorFds ) )
{
close( fdListen );
fdListen = listen( INADDR_ANY, listenPort, 5 );
}
// Process each fd array element and check it against readFds and errorFds.
for ( int32_t i = 0; i < maxConnections; i++ )
{
if ( fdArray[i] )
{
// Check for data to be read
if ( FD_ISSET( fdArray[i], &readFds ) )
{
char buffer[readBufferSize];
int rv = read( fdArray[i], buffer, readBufferSize );
if ( rv > 0 )
{
buffer[rv] = 0;
if ( buffer[0] == 'Q' )
{
iprintf( "Closing connection fd[%d]\r\n", i );
writestring( fdArray[i], "Bye\r\n" );
close( fdArray[i] );
fdArray[i] = 0;
}
else
{
iprintf( "Read \"%s\" from fd[%d]\r\n", buffer, i );
sniprintf( buffer, readBufferSize, "Server read %d byte(s)\r\n", rv );
writestring( fdArray[i], buffer );
}
}
else
{
iprintf( "Read Error on fd[%d]\r\n", fdArray[i] );
FD_SET( fdArray[i], &errorFds );
}
} // data available to read
// Check for errors
if ( FD_ISSET( fdArray[i], &errorFds ) )
{
iprintf( "Error on fd[%d], closing connection\r\n", i );
close( fdArray[i] );
fdArray[i] = 0;
}
} // if fd is valid
} // process each connection in the array
} // while (1)
} // UserMain
NetBurner System Constants.
int FD_ISSET(int fd, fd_set *pfds)
A fd_set (file descriptor set) holds a set of file descriptors (fds). This function checks whether or...
Definition: iosys.cpp:256
void FD_SET(int fd, fd_set *pfds)
A fd_set (file descriptor set) holds a set of file descriptors (fds). This function sets or adds a sp...
Definition: iosys.cpp:246
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, unsigned long timeout)
Wait for events to occur on one or more I/O resources associated with a set of file descriptors (fds)...
Definition: iosys.cpp:373
void FD_ZERO(fd_set *pfds)
Clear (set to 0) a fd_set (file descriptor set) so no file descriptors (fds) are selected.
Definition: iosys.cpp:225
bool WaitForActiveNetwork(uint32_t ticks_to_wait=120 *TICKS_PER_SECOND, int interface=-1)
Wait for an active network connection on at least one interface.
Definition: dhcpc.cpp:1550
NetBurner System Functions.