Sockets API

Legacy Networking Libraries

This page currently covers the 'legacy' networking libraries. A completely new library has been written, with significantly improved performance and reliability.

Sockets programming with the networking stack

Architecture Model

Like the rest of the stack, UDP and TCP sockets and DNS Requests are interfaced through a polling/events model. Their instances need to be Net::poll()ed regularly.

A few set of functions are available, which are similar to the Berkeley sockets API.

UDP Socket

The full documentation of the UDP Socket class is available here: http://mbed.org/users/donatien/programs/NetServicesSource/latest/docs/classUDPSocket.html.

Import program

Public Member Functions

  UDPSocket ()
  Creates a new socket.
  ~UDPSocket ()
  Closes and destroys socket.
UDPSocketErr   bind (const Host &me)
  Binds the socket to local host or a multicast address.
int  sendto (const char *buf, int len, Host *pHost)
  Sends data.
int  recvfrom (char *buf, int len, Host *pHost)
  Receives data.
UDPSocketErr   close ()
  Closes socket.
void  setOnEvent (void(*pMethod)( UDPSocketEvent ))
  Setups callback.
template<class T >
void  setOnEvent (T *pItem, void(T::*pMethod)( UDPSocketEvent ))
  Setups callback.
void  resetOnEvent ()
  Disables callback.

UDP being a simple protocol, the API for an UDP socket is pretty small, so it is a good starting point.

UDPSocket sock;

udp.setOnEvent(&onUDPSocketEvent);

First of all, setup the callback that will be fired on every UDP event:

void onUDPSocketEvent(UDPSocketEvent e)

There is only one event in the UDP API which is that there is some incoming data to read.

sock.bind( Host(IpAddr(), UDP_PORT) );

The first thing you have to do after creating your socket is binding it to a specific port. For UDP purists it might no make sense, but it's actually there to filter-out all UDP packets that are not meant for the socket instance.

The IpAddr instance is initialized with no value here since we will listen on all interfaces (i.e. only one on the mbed in 99% of the cases). However if you want to listen to a multicast group, enter its IP address here and the IGMP group will be joined and listened to.

You can now use the functions sendto and recvfrom to transfer data with a remote host. The pHost parameter must be set to the destination host (IP + port) or multicast group (Multicast IP + port) in the sendto function. In the recvfrom function it's set by the API to return from which host or multicast group the incoming data comes from.

int /*if < 0 : UDPSocketErr*/ sendto(const char* buf, int len, Host* pHost);
int /*if < 0 : UDPSocketErr*/ recvfrom(char* buf, int len, Host* pHost);

TCP Socket

The full documentation of the TCP Socket class is available here: http://mbed.org/users/donatien/programs/NetServicesSource/latest/docs/classTCPSocket.html.

Import program

Public Member Functions

  TCPSocket ()
  Creates a new socket.
  ~TCPSocket ()
  Closes if needed and destroys the socket.
TCPSocketErr   bind (const Host &me)
  Binds the socket to (local) host.
TCPSocketErr   listen ()
  Starts listening.
TCPSocketErr   connect (const Host &host)
  Connects socket to host.
TCPSocketErr   accept ( Host *pClient, TCPSocket **ppNewTcpSocket)
  Accepts connection from client and gets connected socket.
int  send (const char *buf, int len)
  Sends data.
int  recv (char *buf, int len)
  Receives data.
TCPSocketErr   close ()
  Closes socket.
void  setOnEvent (void(*pMethod)( TCPSocketEvent ))
  Setups callback.
template<class T >
void  setOnEvent (T *pItem, void(T::*pMethod)( TCPSocketEvent ))
  Setups callback.
void  resetOnEvent ()
  Disables callback.

TCP is a more reliable but more complex protocol than UDP, so it's harder to work with TCP sockets as well.

TCPSocket sock;

sock.setOnEvent(&onTCPSocketEvent);

Unlike UDP, TCP is a connection-oriented protocol.

That means that a TCP socket connects to another one to exchange data and that the client-server relationship makes sense.

There is actually two kinds of TCP sockets:

- The listening TCP socket which listens for incoming connections on a fixed port (that what happens on a server)

- The connnected TCP socket that exchanges data once the connection has been established

So basically, you will need on your client side a connected TCP socket that will negotiate a connection with a listening TCP socket on your server side. When your server receives a connection request it can accept it: in that case a connected TCP socket is created on the server side an the client socket can now communicate with it.

Client Side

Try to connect to the server:

Host server(IpAddr(1,2,3,4), 1234);
TCPSocketErr bindErr = sock.connect(host);

Wait for a connected event:

switch(e)
{
case TCPSOCKET_CONNECTED:

However any error event may occur so you should handle these properly (by closing your socket).

case TCPSOCKET_CONTIMEOUT:
case TCPSOCKET_CONRST:
case TCPSOCKET_CONABRT:
case TCPSOCKET_ERROR:
  //Close socket...

Once you are connected you are able to exchange data with the server. This is done through the send and recv function.

int /*if < 0 : TCPSocketErr*/ send(const char* buf, int len);
int /*if < 0 : TCPSocketErr*/ recv(char* buf, int len);

These functions return the length that was actually written to/read from the connection, so if this length is inferior to the one you tried to put to/get from the socket, you have to wait for a writeable/readable event to occur before transfering more data in that direction.

case TCPSOCKET_WRITEABLE:
  //Can now write some data...
case TCPSOCKET_READABLE:
  //Can now read dome data...

The remote host can close the connection, in that case an event is raised.

case TCPSOCKET_DISCONNECTED:

However some data might still be available to read at that point, so be sure to read it before closing the socket.

At the end of the session, close the socket:

sock.close();

Server Side

TCPSocketErr err;
err = listeningSock.bind(Host(IpAddr(), TCP_LISTENING_PORT));
if(err)
{
  //Deal with that error...
}
err = listeningSock.listen(); //Starts listening
if(err)
  //...

In your events handler, lookup for an accept event:

if ( e == TCPSOCKET_ACCEPT )
{

And in that case accept the connection:

TCPSocket* pConnectedSock;
Host client;
TCPSocketErr err = listeningSock.accept(&client, pConnectedSock);
if(err)
  return; //Could not accept client
pConnectedSock->setOnEvent(&onConnectedTCPSocketEvent); //Setup the new socket events

Afterwards, the link is symmetric and the pConnectedSock will behave the same way as the client's.

DNS Request

The full documentation of the DNS Request class is available here: http://mbed.org/users/donatien/programs/NetServicesSource/latest/docs/classDNSRequest.html.

Import program

Public Member Functions

  DNSRequest ()
  Creates a new request.
  ~DNSRequest ()
  Terminates and closes request.
DNSRequestErr   resolve (const char *hostname)
  Resolves an hostname.
DNSRequestErr   resolve ( Host *pHost)
  Resolves an hostname.
void  setOnReply (void(*pMethod)( DNSReply ))
  Setups callback.
template<class T >
void  setOnReply (T *pItem, void(T::*pMethod)( DNSReply ))
  Setups callback.
DNSRequestErr   getResult ( IpAddr *pIp)
  Gets IP address once it has been resolved.
DNSRequestErr   close ()
  Closes DNS Request before completion.

DNSRequest req;

req.setOnReply(&onDNSRequestReply);

To resolve an hostname, you can either pass the DNSRequest instance a Host object or a string containing the hostname.

DNSRequestErr resolve(const char* hostname);
DNSRequestErr resolve(Host* pHost);

When the corresponding IP address is obtained or on error the callback setup in the setOnReply method is raised.

On success, if a Host object was passed to the instance, it is updated with the resolved IP. In all cases this address can be obtained through the getResult method.

DNSRequestErr getResult(IpAddr* pIp);

Services

The NetService class provides a way of dealing with multiple dynamic connections (for servers) through a dynamic services pool. To use it, just have your class inherit from the NetService class.

The full documentation of the NetServices class is available here: http://mbed.org/users/donatien/programs/NetServicesSource/latest/docs/classNetService.html.

Import program

Public Member Functions

  NetService (bool owned=true)
  Instantiates a new service.
virtual void  poll ()
  This method can be inherited so that it is called on each Net::poll() call.

Protected Member Functions

void  close ()
  This flags the service as to be destructed if owned by the pool.

Your service can be either owned by you or the services pool, which is chosen on construction of the class.

NetService(bool owned = true);

If you choose to keep ownership of it (owned = false), you will have the responsibility of destroying the instance yourself. However, if it is owned by the pool(owned = true), the instance will be destroyed safely on a call to the close method of the object, even if called inside a method from this object or an event context.

virtual void poll();

By inheriting this method, your object will be polled on each Net::poll() call.

void close();

This flags the service as to be destructed.

This class can be particularly useful for both client (in user-owned mode, you can ensure that you class will be regularly polled and deal with timeout events for instance) and server (in pool-owned mode, it will be polled as well and you can create dynamically connection-oriented objects that will destroy themselves at the connection closure).

Examples

A very good starting point is Darren's Echo Server which is well documented:

Import programEchoServer

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.

http://mbed.org/users/darran/notebook/echo-server/

For multicast you can have a look at this program:

Import programUDPSocketExample

UDP Sockets use example

You can have a look at some high-level components as well in the NetServices stack source.

Import programNetServicesSource

NetServices Stack source