Finalizing D2
Andrei Alexandrescu
SeeWebsiteForEmail at erdani.org
Sat May 23 17:26:16 PDT 2009
Denis Koroskin wrote:
> On Sat, 23 May 2009 22:20:14 +0400, Andrei Alexandrescu
> <SeeWebsiteForEmail at erdani.org> wrote:
>
>>>> * std.socket, std.socketstream: We need a real networking library.
>>> what would it do on top of what that does?
>>
>> I haven't studied it, but Walter said he doesn't like it and I trust
>> him. Anyhow, we'd need to create at least full range integration and
>> support for protocols such as http, ftp, ssh, and imap. Today's
>> languages load a webpage in one line, and that's great so we need to
>> do that. It's even better to be able to process the webpage while it's
>> loading (concurrency!), so we want to do that as well.
>>
>>
>> Andrei
>
> We wrote a networking library with a unique modern flexible design.
>
> It was initially written in C++, but I'm slowly porting it to D2 (it's
> already usable and I wrote a few applications with it by now).
>
> If anyone is interested, I may contribute it to Phobos. Its design
> overview (very short one) is attached for those who are interested.
>
Sounds great! The doc comes off as binary, so I'm pasting it below for
others' convenience.
Andrei
There are two main concepts in "net" (which is a name of the library):
- Link
Link is an establish connection between two computers. It is a very
simple interface that has two important methods - void
send(const(void)[] data) and void disconnect().
- Driver
Driver is something that creates Links and transfer data between them.
Each driver has a set orthogonal properties (some guaranties that they
provide):
- Reliable Boundary
Indicates that packet boundary is guaranteed by the driver.
This means, if packet with size N is sent and recieved on the other
side,
it is recieved as one packet with size N, neither splitted into
several portions nor
merged with other packets. Stream protocols like TCP often doesn't
support reliable
packet boundaries, datagrams like UDP often does support this feature.
- Reliable Content
Indicates that packet consistency is guaranteed by the driver.
This means, sent data is recieved without corruption of content.
All corrupted data
will be filtered out by the driver that supports this feature.
- Reliable Order
Indicates that packet order is guaranteed by the driver.
This means, packets will never change their order is driver supports
this feature.
- Reliable Delivery
Indicates that packet delivery is guaranteed by the driver.
This means, while connection is still alive, any data sent is
recieved on the other side (maybe after some time, but will be).
etc.
These are called driver capabilities. If a driver doesn't have some
property which is important for your application (for example, content
reliability, or packet order), you can create a proxy-driver that will
externally add missing feature. This is one of the main ideas behind
Drivers: they should be easily "decorable" (compoundable). Other example
is, if your driver doesn't compress data automatically, you may easily
wrap it with some driver that supports data compression.
Networking library provides a set of cross-platform proxies that provide
any of the required features. Here is an incomplete list of implemented
Driver Proxies (in addition to Proxies that fulfil requirements above -
consistency, order, etc)
- FastCompression Driver
Compresses traffic before sending it over network
- Local Driver
A kind of "loop back" driver. "Sends" data within address space of the
single application (no data copying ever occurs)
- Signature Driver
"Signs" every outgoing packet and filters out packets with wrong
signature
- Statistics Driver
Gathers statistics on transferred data (number of lost packets,
out-of-order packets, damaged packets, bytes sent/received, etc)
- Timeout driver
Automatically disconnects when a specified timeout is reached
Future work:
- Encryption Driver
Why was it important for us? We develop games for embedded devices
(think of game consoles, pocket pcs, phones etc). Some of them have very
primitive hardware and software. For example, some of them don't
implement BSD Sockets, have no TCP or UDP driver (*very* common case)
etc. This is why our networking library doesn't rely on any of these
features, although they are used when available. All that is needed is a
simple ability to transfer data in *any* way. Everything is else
configurable externally by our library. For example, our library
provides cross-platform implementation of TCP over UDP.
You decide what features you create driver with depending on your needs.
For example, when developing turn-based strategy, it is not very
important to have ultra-low traffic, and ease of development is of more
importance. In this case you may request all of the features and
simplify your code dramatically.
Sometimes you need to connect over some specific protocol, such as TCP
or UDP (for example, access a web-page over HTTP). In this case, you
request some concrete driver implementation.
Drivers are created using factory methods like the following:
Driver createDriver(uint requiredDriverCaps, ...);
Driver wrapDriver(Driver hostDriver, uint requiredDriverCaps, ...);
Driver createTcpServerDriver(bool async, ushort listeningPort, ...);
Driver createTcpClientDriver(bool async, ushort defaultDestinationPort,
...);
Driver createUdpDriver(bool async, ushort listeningPort, ushort
defaultDestinationPort, ...);
etc
Packet Processing
Whenever a driver receives new packet, it unwinds it (some proxy drivers
may add additional data to packets - checksum, packet index, etc - or
completely modify it - encryption, compression, etc) and passes to the
corresponding link. If a programmer wants to handle packets that come
from links, he subscribes to them:
// Using a listener
link.addIncomingPacketProcessor(this);
// Using a delegate
link.addIncomingPacketProcessor(&someMethod);
These callbacks may be invoked either in main thread (synchronous,
during implicit driver.processIncomingPackets() call) or in other thread
(asynchronous) - behavior is specified during Driver intiialization.
Creating a new link is as simple as:
auto link = driver.createLinkTo(host, port);
A new valid link is always returned even though the connection may not
be establish immediately (non-blocking operation). You can start using
it (sending data etc) without waiting until connection fully establishs.
Notification callback will be invoked if connection fails.
Some drivers may emit new Links. Whenever new connection is received, a
link is created and passed to Listeneres. You subscribe to Driver events
the same way you do it with Links - driver.addIncomingLinksProcessor(...);
All the operations are inherently asynchronous (non-blocking). For
example, there is no method link.receive() that waits until a link
receives any packet (although it's often very handy, but mostly for
prototyping). Operations like this implemented using helpers.
This is a core functionality, the one that "drives" the development.
Everything else (HTTP/FTP/SSH connections, etc) needs to be built on top
of the core functionality in a cross-platform manner.
Other cool feature that is implemented using our library is remote
procedure call (which is very helpful, not only for debugging). In
short, you may remotely invoke almost any method on any object with any
arguments and get result back. D compile-time reflection capabilities
will significantly simplify porting this.
More information about the Digitalmars-d
mailing list