2

I am currently trying to design some C++ classes that encapsulate POSIX sockets and their relevant functions. I want to try and design classes that are simple, elegant, and make the best use of design patterns and inheritance.

Starting from the server and client side sockets we have the following functions POSIX functions for each

// Server Side Socket
socket()
bind() 
listen()
accept() // returns a socket that can send() and recv() and close()
close()

// Client Side Socket
socket()
connect()
send()
recv()
close()

From this I identified 3 sort of overlapping sockets, the client, the server, and the data socket the server returns from the accept() function. My first though was to try and break these functions up into a hierarchy of "interfaces" (pure virtual classes). Something similar to

// IClosable.h (interface for something that can be closed)
class IClosable
{
public:
  virtual ~IClosable();
  virtual void close() = 0;
};
// ISendReceive.h (interface for something that can send and receive)
// (possible that this interface should not inherit from IClosable?)
class ISendReceive : public IClosable
{
public:
  virtual ~ISendReceive();
  virtual void send(std::vector<std::byte> data) = 0;
  virtual std::vector<std::byte> receive(std::size_t num_bytes_to_receive) = 0;
};

// SendReceiveSocket.h (something similar to what is returned by accept())
class SendReceiveSocket : public ISendReceive
{
public:
  void send(std::vector<std::byte> data) override;
  std::vector<std::byte> receive(std::size_t num_bytes_to_receive) override;
  void close() override;
};
// IConnectable.h (interface for something that needs an address like
// bind() or connect()
class IConnectable
{
public:
  virtual ~IConnectable();
  virtual void connect();
};

And these interfaces would continue until I have all the overlapping functionality and I would have my 3 sockets inherit from different interfaces. I think this illustrates the interface segregation principle ("I" in "SOLID"), but this feels a little clunky.

I was curious if my approach so far was considered a best practice or if this is too complicated (I think this is closest to the composite design pattern). Perhaps I should be trying to find a better design pattern maybe utilizing a factory to help me build these sockets or a decorator pattern. Any thoughts would be appreciated.

1
  • 3
    The key question is how close do the users of your code want to be to the underlying socket detail? The granularity of your API/abstraction depends a great deal on how high or low-level you want the abstraction to be; for example, a higher-level abstraction might expose messaging patterns rather than socket concerns, taking care of the asynchronous nature of networking as well as transport/delivery. For an alternative perspective on socket abstractions you could look at the official C++ library for ZeroMQ which may have similar goals to those you describe here. Apr 13, 2023 at 6:27

1 Answer 1

1

I’m not sure that ICloseable is something that makes sense to the callers; perhaps ISocket would be more meaningful to the user. It would still have the close() method.

ISendAndReceive is a great start - I’d add Socket on the end as other things send and receive - email, SMS, frisbee players - as you have done with the implementation.

IConnectable seems to be modelling a Client-side socket connection; maybe name it in these terms.

This just leaves the Server side which is the most complex but you’ve got almost all the pieces already. Just need a IServerSocket (or similar) which has the other methods. Note that accept() should return an ISendAndReceiveSocket (the interface); it’s up to the implementation how that gets created.

C++ can inherit multiple interfaces and implementations. This makes it possible to model your concrete classes from multiple bases, if you wish.

Hope this helps!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.