[Ns-developers] [ns3] socket API

Tom Henderson tomh at tomh.org
Tue May 13 09:44:28 PDT 2008


Here is an update on the socket API work.  This work (pending review) and Mathieu's tags work need to be checked in and cleaned up before we can work towards finalizing the beta release with stable C++ API.  

Mathieu's original post is at:
http://mailman.isi.edu/pipermail/ns-developers/2008-April/003976.html

To summarize:

1) need to implement a "pull" API in ns3::Socket.

I did this mostly along the lines Mathieu suggested, except for:
> * If the size of
> * the next packet to return is bigger than maxSize, return 0 and
> * set ENOMEM.

it seemed to me that ENOMEM is not the right errno response here, so that is an open issue.

I also implemented the overloaded Recv (void) method (and Send()) along the lines of Gustavo's suggestion on the list.

As for the implementation status of different socket types, finite recv should be supported on Udp and Packet socket types now (untested) but Tcp needs some further integration.

2) Finite send buffer

The API is mostly what Mathieu had suggested, except for this piece which has not changed.

> * return -1 if packet does not fit in tx buffer (current behavior is 
> * to send the part of the packet which fits in the tx buffer).

Right now, it still takes a partial send() but I think Mathieu is correct to make this return -1 (will fix it later).

Also, Mathieu remarked:
> If we implement this, we should remove the SendTo method. Problem: if we
> do this, we cannot specify the "to" address for
> int Socket::SendTo (uint8_t *buffer, uint32_t size);
> because there is no Ptr<Packet> to attach the "to" address to.
>
> Solution: get rid of the Socket::Send* methods which take raw buffers
> and implement them exclusively at the "synchronous" layer.

I have not removed these raw buffer calls, although they could be replaced with Packet-like variants such as:

replace
int Socket::Send (buffer, size); 
with
int Socket::Send (Create <Packet> (buffer, size));

and 

uint32_t size = Recv (buffer, len);
with something like:
uint32_t size = Recv (len)->CopyTo (buffer, len);

I would support whatever consensus would emerge on whether to keep or remove these variants, but my sense is that opinions still differ.

As for the implementation status, finite send is supported on Tcp but not on Udp or Packet socket types (that is, you can set a finite buffer at the API but it has no effect on behavior).  For Tcp, a question has arisen as to how to encode the semantics of "infinite send" buffer.  Raj and I have discussed the relative merits of using the special value "0" to denote "infinite" send buffer.  I tend to prefer "maxint" to "0" to encode the concept of infinite buffer, because in practice we have to support some finite value else our data types will overflow.

In either case, I think we can make the default value for socket buffers "infinite" or "very large" and let users configure it downward if they want to model that aspect.

3) How should socket options and ioctls be supported ?

I implemented this along the lines of Mathieu's suggestion (define  SocketOptions class and aggregate the object to a socket) although I did not go so far as to implement (yet) the UdpSocketOptions or TcpSocketOptions class.  Users who hold a reference to a Ptr<Socket> can call GetObject() to get whichever one they want.  Then, calls to Set..() or Get..() these values indirect through the Socket base class to a setter or getter implemented in the socket implementation class.

Only two options are supported for now (just proof of concept), equivalent to SO_SNDBUF and SO_RCVBUF at the socket level.

The question arose as to where to put the defaults for these options, since class SocketFactory is abstract base class.  I implemented a class SocketDefaults and made these attribute-based, and aggregated a SocketDefaults object to the Node (in InternetStack::AddInternetStack()).

Maybe there is a better implementation strategy that avoids this class, but the above seems workable at least.

- Socket::Create (Ptr<Node> node, TypeId tid)

I added a helper method to avoid having users to create sockets through the socket factory.  Where one once typed:

  Ptr<SocketFactory> socketFactory = node->GetObject<SocketFactory> (tid);
  m_socket = socketFactory->CreateSocket ();

now one can type:
  m_socket = Socket::CreateSocket (node, tid);

However, we need to keep these socket factories around IMO to host the default values (attributes) for the various socket types.

Finally, I haven't developed further the idea of a socket helper class since the API seemed very similar to the class Socket API other than perhaps moving the raw buffer variants there; it seems to me that if the API is different in only that aspect, that the potential for confusion of having this additional class might outweigh the benefits.

If there are no objections in principle to what is proposed in ns-3-dev-socket, I would like to start to marshal in these changes (perhaps tomorrow) so that it doesn't remain as a blocker.  If there are still further changes/discussions needed on portions of it, perhaps we can still start to marshal in the portions that are settled.

- Tom







More information about the Ns-developers mailing list