Dear all,
I have a number of interesting things on the queue, which need some polishing before publishing but here is one that might deserve your attention. I hope I can get some useful feedback from at least some of you. You do not need prior knowledge in order to understand the documentation. Please let me know if I’m wrong.
I’d be especially happy to have some of the new guys commenting! Imagine your task was to extend or implement some missing functionality of Connection. Please answer the following questions:
1. Is the information provided in the documentation enough to understand BOTH the problem and the solution?
2. Do you think that describing the problem helped your understanding what the class is supposed to do?
3. Just by reading this documentation, can you think of some scenarios, which could be unit tested? Give a few examples.
4. Is the documentation too much?
5. Do you feel more confident about doing some work on Connection after reading the docs?
6. Do you have any ideas how the documentation can be improved?
Ok, enough questions! Below are the docs!
Regards,
Peter
R1. Typically each application layer protocol (TCP/IP suite) is bound to using either TCP or UDP transport. For the sake of extensibility we at Calitko would like to be able to easily change transports and for example run HTTP on top of a custom UDP-based transport protocol (e.g. make NAT to NAT transfers possible, or to run Gnutella over UDP instead of TCP). Therefore we need Connection to provide an abstract interface and basic implementation for most of the Connection responsibilities. S1. This need is addressed by using the Bridge Pattern, which allows us to vary the abstraction and its implementation independently. Connection represents the abstraction and a ConnectionImp derived classes could represent the TCP, UDP or custom implementations. Connection::Connection() requires a ConnectionImp object in the ctor. R2. The classical socket interface requires users to implement a mechanism for buffering incoming or outgoing data until a complete information unit (e.g. packet) has been received or sent. User code can be largely simplified by guaranteeing that a certain number of bytes can be read or written over the Connection. The clients can thus buffer only higher level representation of their data (as C++ objects) and write/read each one as raw bytes in a single call to Connection. Buffering raw data would not be required anymore. S2. To address this need, Connection will manage internal buffers of known and configurable size, which will be used in case the implementation cannot write or has read more data. Take a look at the functions canRead(), peek(), read(), setReadBufferSize(), readBufferSize(), bytesToRead(), canWrite(), write(), setWriteBufferSize(), writeBufferSize() and bytesToWrite(). R3. Since concrete protocols may be either stream oriented or datagram oriented, the data read/write interface of Connection should provide for unambiguous interpretation how the raw data should be arranged in datagrams. For example, piggybacking status on ping or pong possible should be possible to be explicitly specified by the user. That would make no difference if the actual transport is stream oriented. S3. All read datagrams will be buffered completely and can be read as a stream. Of course no guarantees for the reception order is provided, so the user should make sure that only complete entities (packets/buffers) are sent in a single datagram. This way it will be guaranteed that also complete entities can be read out. When writing data to the connection, the user must give Connection a clue to where an entity or group of entities start and end. The write() function has a parameter a flush defaulting to true. All data written in a single call are guaranteed to be sent in a single datagram. If a flush is set to false, then the data written will be buffered and will not be sent to the implementation until a call with a flush set to false is made when all buffered data will be transferred in one piece to the implementation. The user must make sure that this accumulated buffer is not bigger than the maximal datagram allowed by the implementation and Connection::writeBufferSize(). This means that a packet can be written in pieces and that multiple packets can be sent in a single datagram. R4. Most network operations have some time constraints and associated timeout intervals (connect/disconnect/read/write). To simplify implementation of concrete protocols, the base class Connection should take care of these timeouts. S4. Timeouts for connectToNode() and disconnectToNode() can be set using setConnectTimeout() and setDisconnectTimeout() or get the current values using connectTimeout() and disconnectTimeout(). There are also inactivity timeouts for read and write operations. If no data has been received in the last readTimeout() miliseconds, the connection will be closed. The read timeout can be set using setReadTimeout(). Similarly, if no data could have been written to the implementation within the last writeTimeout() milliseconds, the connection will be closed. Use setWriteTimeout() to configure the timeout.
