| Author: | Roger Gammans |
|---|---|
| Date: | May 2007 |
The current version of the code give the rdp channel a privileged position in the commincations hierachy. While this might seem reasonable at first sight, it means that artifacts on the network between the client (this program) and the server can affect all the channel in various ways including leading commonly unwanted blocking of the program.
However another reasonable view is the main task of this program is a protocol translator/multiplexor between RDP and the vairous local fd's it connects to such as GUI (X-server) , sound and printer . If you take this view it seems that the main function of the program is tho move data efficently between these source and so the main loop should be concerned with the reading & writing of data to as many of these sources as possible. Currently the main loop almost achives this aim but is driven by the view that it only needs to concern itself with reading- and then primirily from the rdp socket.
Each layer of the protocol stack (rdp,secure,mcs,iso & tcp ) have a standard set of functions.
In a few cases these names vary slightly , for instance mcs has mcs_send_to_channel(..) has it primary send fn although it does also implement a mcs_send(..) , and rdp has rdp_init_data(..) rather than rdp_init(...)
I attempt to decribe them here:
PDU init(int len)
- Prepare a PDU struct with space of at least len ready for sending.
void send(PDU p)
- Submit p ready to send to the sevrer.
This function may 'block'
void recv(..)
- Receive a data packet from the lower layer.
The exact arguments to this method depend on the layer concerned.
This function may 'block'
connect(..)
- Establish a connection a sever, thru the next lower layer.
This function may 'block'
disconnect(..)
- Disconnect fromt he lower layer
reset_state(..)
- Initialise the layer ready for use.
The will mostly be the same, although our layer names will change:
x_register( layer_dependent , callback ) x_recv(pkt) - the callback above. x_send(pkt) x_init(pkt) x_reset_state(.)
So we will use the existing STREAM structure for our packet maintaining simple compatibility with the existing code. None of these functions may block.
For maximum portablitliy we need to be able chose between approiate pump loops (eg 1 per lower level select/fd/handles differences and user interface loop requirements.
So the basic OS eg, Unix/Win32 should provide a basic loop implementation which any uiport can use. I say any because I don't want to preclude winelib (win32 on unix) as a port .
This requires a basic loop implementation and functions to have globally visile names. We also requirethe uiports to have access to the Listeners/ Send queue details if they need to implement an alternative to the standard select() to test for message in the UI queue.
The new API is split into two new modules. The Event module which is intended to face replacement message loop providers, and the pump module which whose client are the various parts of the rdesktop program which need to send data to various external system objects (eg, files,sockets devices etc).
Iterators:
HANDLE get_first_listner()
- Return the first handle registered listeners.
HANDLE get_next_listner(HANDLE)
- Return the next HANDLE after HANDLE in the listner list, returns RD_PUMP_INVALID_HANDLE if at
end of list.
HANDLE get_first_active_q()
- Return the first handle with data waiting to be sent.
HANDLE get_next_active_q(HANDLE)
- Return the next handle with outstanding data.
Behavoir of the iterators if insertion,or deletions between calls is guaranteed to never return the same handle twice. (Eg, you may get a deleted handle, or not het an inserted handle. And you may not get returned other handles). So this list iteration should complete in finite (and reasonable) time.
The only exception is if you are continually inserted new handles, so the list becomes unreasonbale large.
Actions:
do_send(HANDLE) - process this handle . Call in response to a ready to send event on HANDLE do_recv(HANDLE) - recv data from this data. Cal in response to a dat available event on HANDLE do_all(HANDLE) - To be called if you dont know whether it is a read or write event.
It is expect that this layer varies the most betweem ports, for instance the a windows ports would probalry use handles not Fds, and use MsgWaitForMultipleObjects(...)
Functions:
void pump_register(HANDLE, amount, ( * recv)(pkt * )) - On recieving amount from HANDLE call recv with pkt of size amount octets. void pump_register_nobuffer(HANDLE, (* recv)()) - on select show HANDLE readable call recv() pkt * pump_init(size) - Get pkt for send of at least size size. pump_send(HANDLE,pkt* p) - Submit p for sending on HANDLE. pump_write(HANDLE , char* p, int len) - Wrap write/send(2) calls pump_send(..) pump_reset_state() - Initialise this for running. pump_main_loop() - handle sending/reccieving data while avaible or block here.
This keep a list of all the files/streams being listened to and the callback functions which handle processing of this data.
Lookup Hash similiar.
Compact representation, or links between elements.
Insertion/Deletion speed not critical .
Two apprroaches, one array style,or hash table implentation for lookup and links between populted elements. or, realloc'd array with domesort of index.
Given that we won't have many handles I'll use a hash table of 32 entries.
Walking the hash table won't be to slow, and lookup will be ok in speed to, as if the hash function is reasoanble we should no get to many hash collisions .
The table is small enough that it won't be too sparse making walking slow.
-> Linked list meets this requirements ok.