The UPnP+™ Software Development Kit
General information to the SDK, its structure and requirements.
The SDK has two parts, handling Control Points or UPnP Devices. They are largely independent of each other. You can compile both parts separately so you have resulting libraries that only handle Control Points or UPnP Devices. That make sense for installations that are only a Control Point or only a UPnP Device and is of particular interest for embedded devices. Of course you can also have both parts for hybrid applications.
We have three different internal libraries:
To manage a smooth transition from old pUPnP "C" code to the object oriented C++ class library there are different compile modes specified.
code | with library | build | mode |
---|---|---|---|
old | pupnp | yes | verify (if old programs still run as expected) |
old | compa | yes | compatibility (used to start transition) |
new | pupnp | no | not supported |
old modified | compa | yes | portability to UPnPsdk (step by step ongoing transition) |
new | UPnPsdk | yes | new project |
The Software Development Kit contains some main components that are associated with a specific step of the UPnP™ Device Architecture 2.0.
These components are:
order | component | part of |
---|---|---|
1 | miniserver | Step 0: Addressing |
2 | webserver | Step 1: Discovery |
3 | SSDP | Step 1: Discovery |
4 | SOAP | Step 3: Control |
5 | GENA | Step 4: Eventing |
The order shows the dependency. Each component depends on its predecessor. For example if selecting SSDP on configuring to compile, this selects also automatically the webserver (only that) and the webserver selects miniserver. If you select GENA for compiling you have a full featured configuration with all main components. There is no need to select previous moduls. Also the abstraction will increase with the order. For example no component except miniserver will handle network sockets.
There are several functions and methods that are specified to noexcept. This may not always be true with debugging or TRACE enabled because there we have extended use of std::string objects for output to std::cerr. Both may throw exceptions in rare cases like exceeding max. length of a std::string or error on memory allocation or modified output flags of the std::cerr output stream by the program using the SDK. The result of such an unexpected exception is the immediate termination of the program as specified for noexcept. But I assume that debugging or TRACE is only a temporary setting not used for production code and is under special observation by the developer so that he can evaluate these exceptions.
There is an issue when a network connection unexpectedly dies. The port then has normaly to TIME_WAIT
util it can use its old netaddress again. This may not be acceptable for certain server applications. So there is a socket option SO_REUSEADDR
to immediately reuse a given netaddress, but with the risk of network errors and security issues. For more details have a look at Bind: Address Already in Use.
I don't set the option to immediately reuse an address of a local connected socket after the connection closed unexpectedly. Instead I respect the TIME_WAIT
. For an immediate reconnection I use a new address. A new netaddress is already given with a changed port so I just use the same ip address with another port. I always reset socket option SO_REUSEADDR
with constructing a socket object on all platforms.
A TIME_WAIT is not necessary if the remote control point closes the connection. UPnPsdk uses the protocol to signal the remote control point to shutdown the connection if it support it.
TIME_WAIT is state of TCP connection, not the port. Every TCP connection identifies by tuple (local-address, local-port, remote-address, remote-port). So if the client connect to server using new (dynamic) local port then new TCP connection is created and TIME_WAIT isn't issue.
This is unclear on Microsoft Windows. THERE WE HAVE AN IMPORTANT SECURITY ISSUE!
In the past Windows had a very bad network security on low level socket handling as documented at Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE. Microsoft has fixed this since Windows Server 2003. The network stack now behaves like it does by default on other platforms. Strangely, they call it advanced security, which is standard on other major operating systems. But this isn't done by default. The developer has to take this into account by using the socket option SO_EXCLUSIVEADDRUSE. I always set this option with constructing a socket object on a WIN32 platform.
For embedded devices 32 bit architectures are still supported. Only for this architectures Large-file support is needed to handle files greater 2 GiB. We need this because we have to handle video files that may exceed this limit. For targets with 64 bit architecture LFS is not relevant.
The problem is the access to files. The natural limit is given by the address structure. With 32 bit you can access max. 2 GiB, with 64 bit architectures you can access usually 256 terabytes for example with fseek
. To handle large files with 32 bit you have to provide new functions, e.g. fseek64
. Microsoft Windows is going this way and provides fseek
and _fseeki64
. POSIX is going an other way. It provides fseeko()
and ftello()
functions that are identical to fseek()
and ftell()
, respectively, except that the offset argument of fseeko()
and the return value of ftello()
is of type off_t instead of long. On some architectures, both off_t and long are 32-bit types, but defining _FILE_OFFSET_BITS=64
as compile option will turn off_t into a 64-bit type. This way you can compile your code to use LFS or not without modifications.
References:
Visibility Support provides a powerful optimization. I use it as described at the GCC Wiki - Visibility. It only belongs to shared libraries. Here in short the needed steps configured for this software development kit:
UPnPsdk_SHARE
and UPnPsdk_EXPORTS
. You should declare UPnPsdk_SHARE
to be PUBLIC. With nested use of libraries this is only needed for the first library. CMake propagates the compile definition. With other combinations you have to respect CMakes propagation rules for PRIVATE, PUBLIC, INTERFACE and it may be necessary to also declare UPnPsdk_SHARE
on following libraries and executables. In result every shared library and executable using it, must see UPnPsdk_SHARE
.UPnPsdk_EXPORTS
to PUBLIC. This may see an executable target that should not export symbols. UPnPsdk_VIS
or UPnPsdk_API
in struct, class and function declarations you wish to make public visible. You should not specify it in the definition of your source files. On Microsoft Windows Visual Studio it does not compile due to an error. You should never do it on templated or static functions because they are defined to be local. UPnPsdk_LOCAL
look at the GCC Wiki - Visibility. If you have a publicy visibile class UPnPsdk_VIS PublicClass()
then all its member functions are also publicy visible. To keep them private you can prefix them with UPnPsdk_LOCAL
.