An Echo server as described in RFC862. Written as a learning exercise for using Donatien's network stack. Hopefully of some use to others to get started with socket programming.

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
darran
Date:
Sat Jun 12 19:05:52 2010 +0000
Commit message:

Changed in this revision

EchoServer.cpp Show annotated file Show diff for this revision Revisions of this file
EchoServer.h Show annotated file Show diff for this revision Revisions of this file
EthernetNetIf.lib Show annotated file Show diff for this revision Revisions of this file
TCPEchoHandler.cpp Show annotated file Show diff for this revision Revisions of this file
TCPEchoHandler.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EchoServer.cpp	Sat Jun 12 19:05:52 2010 +0000
@@ -0,0 +1,60 @@
+#include "EchoServer.h"
+
+EchoServer::EchoServer() {
+    // Create the sockets and set the callbacks
+    tcpSock = new TCPSocket;
+    tcpSock->setOnEvent(this, &EchoServer::onNetTcpSocketEvent);
+    udpSock = new UDPSocket;
+    udpSock->setOnEvent(this, &EchoServer::onNetUdpSocketEvent);
+}
+
+EchoServer::~EchoServer() {
+    // Delete the sockets on destruction
+    delete tcpSock;
+    delete udpSock;
+}
+
+void EchoServer::bind(int tcpPort, int udpPort) {
+    // bind and listen on TCP
+    tcpSock->bind(Host(IP_ADDR_ANY, tcpPort));
+    tcpSock->listen();
+    // bind UDP
+    udpSock->bind(Host(IP_ADDR_ANY, udpPort));
+}
+
+void EchoServer::onNetTcpSocketEvent(TCPSocketEvent e) {
+    // We're only interested in the ACCEPT event where we need to accept
+    // the incoming connection
+    if ( e == TCPSOCKET_ACCEPT ) {
+        TCPSocket* tcpClientSocket;
+        Host client;
+        if ( tcpSock->accept(&client, &tcpClientSocket) ) {
+            printf("onNetTcpSocketEvent : Could not accept connection.\r\n");
+            return; //Error in accept, discard connection
+        }
+        // We can find out from where the connection is coming by looking at the
+        // Host parameter of the accept() method
+        IpAddr clientIp = client.getIp();
+        printf("Incoming TCP connection from %d.%d.%d.%d\r\n", clientIp[0], clientIp[1], clientIp[2], clientIp[3]);
+        // Create TCPEchoHandler and pass client socket
+        TCPEchoHandler* tcpHandler = new TCPEchoHandler(tcpClientSocket); //TCPSocket ownership is passed to handler
+        // The handler object will destroy itself when done, or will be destroyed on Server destruction
+    }
+}
+
+void EchoServer::onNetUdpSocketEvent(UDPSocketEvent e) {
+    // We're only interested in the READABLE event (it's the only one)
+    if ( e == UDPSOCKET_READABLE ) {
+        // No need for a handler for UDP - set up a buffer and check client
+        char buff[128];
+        Host client;
+        IpAddr clientIp = client.getIp();
+        printf("Incoming UDP connection from %d.%d.%d.%d\r\n", clientIp[0], clientIp[1], clientIp[2], clientIp[3]);
+        // Keep reading while there's data to be read
+        while ( int len = udpSock->recvfrom(buff, 128, &client) ) {
+            if ( len > 0 )
+                // If there's data, send it straight back out
+                udpSock->sendto(buff, len, &client);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EchoServer.h	Sat Jun 12 19:05:52 2010 +0000
@@ -0,0 +1,52 @@
+#ifndef ECHO_SERVER_H
+#define ECHO_SERVER_H
+
+#include "mbed.h"
+#include "TCPSocket.h"
+#include "UDPSocket.h"
+
+#include "TCPEchoHandler.h"
+
+/*
+    Class: EchoServer
+    Binds itself to port 7 on TCP and UDP and listens for
+    incoming connections
+*/
+class EchoServer {
+public:
+    // Constructor: EchoServer
+    // Creates the TCP and UDP sockets and wires up
+    // the event callback methods
+    EchoServer();
+    // Destructor: ~EchoServer
+    // Deletes the TCP and UDP sockets
+    ~EchoServer();
+    /*
+        Function: bind
+        Binds the sockets to the specified ports
+        Parameters:
+            tcpPort - The TCP port to bind to (default 7 as per RFC862)
+            udpPort - The UDP port to bind to (default 7 as per RFC862)
+     */
+    void bind(int tcpPort=7, int udpPort=7);
+private:
+    // Variable: tcpSock
+    // The TCP server socket
+    TCPSocket* tcpSock;
+    // Variable: udpSock
+    // The UDP socket
+    UDPSocket* udpSock;
+    
+    // Function: onNetTcpSocketEvent
+    // The callback function called by the network stack whenever an
+    // event occurs on the TCP socket
+    // Parameter: e - The event that has occurred
+    void onNetTcpSocketEvent(TCPSocketEvent e);
+    // Function: onNetUdpSocketEvent
+    // The callback function called by the network stack whenever an
+    // event occurs on the UDP socket
+    // Parameter: e - The event that has occurred
+    void onNetUdpSocketEvent(UDPSocketEvent e);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/EthernetNetIf.lib	Sat Jun 12 19:05:52 2010 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/donatien/code/EthernetNetIf/#bc7df6da7589
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TCPEchoHandler.cpp	Sat Jun 12 19:05:52 2010 +0000
@@ -0,0 +1,69 @@
+#include "TCPEchoHandler.h"
+
+// When the constructor's called, initialise the member variables
+TCPEchoHandler::TCPEchoHandler(TCPSocket* tcpClientSocket)
+        : NetService()
+        , clientSocket(tcpClientSocket)
+        , closed(0)
+        , timeoutWatchdog() {
+    // Wire up the event handler on the client TCP socket
+    clientSocket->setOnEvent(this, &TCPEchoHandler::onTCPSocketEvent);
+}
+
+TCPEchoHandler::~TCPEchoHandler() {
+    // Close the socket on destruction
+    close();
+}
+
+void TCPEchoHandler::onTCPSocketEvent(TCPSocketEvent e) {
+    switch (e) {
+        // If the socket is readable, do stuff
+        case TCPSOCKET_READABLE:
+            // Disable the timeout watchdog timer
+            timeoutWatchdog.detach();
+            // Read in any available data into the buffer
+            char buff[128];
+            while ( int len = clientSocket->recv(buff, 128) ) {
+                // And send straight back out again
+                clientSocket->send(buff, len);
+            }
+            // Reset timeout countdown
+            setTimeout(ECHO_TIMEOUT);
+            break;
+        case TCPSOCKET_CONTIMEOUT:
+        case TCPSOCKET_CONRST:
+        case TCPSOCKET_CONABRT:
+        case TCPSOCKET_ERROR:
+        case TCPSOCKET_DISCONNECTED:
+            // Close the socket on any terminal TCP event
+            close();
+            break;
+    }
+
+}
+
+void TCPEchoHandler::close() {
+    // Prevent recursive calling or calling on an object being destructed by someone else
+    if ( closed )
+        return;
+    closed = 1;
+    timeoutWatchdog.detach();
+    if ( clientSocket ) {
+        clientSocket->resetOnEvent();
+        clientSocket->close();
+        delete clientSocket; //This fn might have been called by this socket (through an event), so DO NOT DESTROY IT HERE
+    }
+    // Flags this service as closed - will be destructed and deleted on
+    // the next call of NetService::poll() by Net::poll()
+    NetService::close();
+}
+
+void TCPEchoHandler::setTimeout(unsigned int timeout) {
+    // Attach our timeout handler to the timeout watchdog timer to close the socket if no activity
+    timeoutWatchdog.attach_us<TCPEchoHandler>(this, &TCPEchoHandler::onTimeout, ECHO_TIMEOUT * 1000);
+}
+
+void TCPEchoHandler::onTimeout() {
+    // Nothing fancy, just close the socket and mark this class for destruction
+    close();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TCPEchoHandler.h	Sat Jun 12 19:05:52 2010 +0000
@@ -0,0 +1,52 @@
+#ifndef TCP_ECHO_HANDLER_H
+#define TCP_ECHO_HANDLER_H
+
+#include "mbed.h"
+#include "TCPSocket.h"
+
+// Constant: ECHO_TIMOUT
+// The timeout period for inactivity in milliseconds
+#define ECHO_TIMEOUT 5000
+
+/*
+    Class: TCPEchoHandler
+    A class instantiated to handle the incoming TCP client connection
+    Extends NetService to hook into the TCP/IP stack's polling
+    and destruction service
+*/
+class TCPEchoHandler : public NetService {
+public:
+    // Constructor: TCPEchoHandler
+    // Setup and handle the incoming connection
+    TCPEchoHandler(TCPSocket*);
+    virtual ~TCPEchoHandler();
+private:
+    // Variable: clientSocket
+    // The incoming TCP socket from the client
+    TCPSocket* clientSocket;
+    // Variable: closed
+    // A marker to say whether this socket is already closed
+    int closed;
+    // Variable: timeoutWatchdog
+    // A timer to countdown from during inactivity and close
+    // dormant connections
+    Timeout timeoutWatchdog;
+
+    // Function: onNetTcpSocketEvent
+    // The callback function called by the network stack whenever an
+    // event occurs on the TCP socket
+    // Parameter: e - The event that has occurred
+    void onTCPSocketEvent(TCPSocketEvent e);
+    // Function: close
+    // Closes the client socket and marks this class as done with
+    // for the TCP/IP stack to destroy
+    virtual void close();
+    // Function: setTimeout
+    // Parameter: timeout - The length of time to wait for more activity in milliseconds
+    void setTimeout(unsigned int timeout);
+    // Function: onTimeout
+    // The handler called by the timeout watchdog to close the connection when timed out
+    void onTimeout();
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sat Jun 12 19:05:52 2010 +0000
@@ -0,0 +1,41 @@
+/*
+ * Echo server
+ * Listens on TCP and UDP ports 7 for any incoming connections
+ * Re-transmits any incoming bytes
+ */
+
+#include "mbed.h"
+#include "EthernetNetIf.h"
+
+#include "EchoServer.h"
+
+// Our Ethernet interface
+EthernetNetIf eth;
+// Our Echo server
+EchoServer server;
+
+/*
+    Function: main
+    
+    Sets up the Ethernet interface using DHCP, reports the assigned
+    IP address via serial, binds the Echo server to port 7 on
+    TCP and UDP and then sits in a loop calling Net::poll() to
+    keep the network stack doing its thing
+*/
+int main() {
+    printf("\r\nSetting up...\r\n");
+    EthernetErr ethErr = eth.setup();
+    if (ethErr) {
+        printf("Error %d in setup.\n", ethErr);
+        return -1;
+    }
+    IpAddr ip = eth.getIp();
+    printf("mbed IP Address is %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3]);
+
+    server.bind();
+
+    printf("Entering while loop Net::poll()ing\r\n");
+    while (1) {
+        Net::poll();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Sat Jun 12 19:05:52 2010 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/029aa53d7323