[sctp-dev] Basic Questions
David Ryan
oobles at gmail.com
Tue Jun 23 18:27:28 PDT 2009
Hi Chris,
I'm getting closer to having a working design. I've started my first draft
of the demo code. A few more comments below..
I was thinking that in the situation where you have multiple threads each
>> waiting on a stream, it would be nice to block the thread and then when
>> data
>> was available you could notify the thread and it could receive the data
>> directly into its buffer (this is in the non TLS/SSL situation). In effect
>> this replicates the SctpChannel send/receive API but for individual
>> streams. This would obviously reduce buffer copies in the protocol stack.
>> I'd still need to deal with the situation where a thread wasn't available
>> to
>> receive the data; most likely using a queue.
>>
> I'm not sure I fully understand your design, but the typical use of
> selectors is where you have a single thread block on the selector, when it
> fires (a channel is readable/writable), iterate over the
> selected keys and hand off the ready channel for each key to another thread
> to do the actual work. In which cause you may want a pool of threads and
> buffers that can be reused.
>
I'll try and explain the design I was thinking of a little better. The aim
of the design is to keep buffer copies to a minimum. As you can't get
additional information from the Selector API, I've introduced a peek()
method call in the example below. The design is that at the application
level there would be a number of threads which can each use as a single
bi-directional message stream. Imagine something like:
myAppFunction(int stream)
{
ByteBuffer buffer = getBuffer();
SctpStreams stream = getStream();
MsgInfo msgInfo = stream.receive( buffer, stream ); // Blocking call.
... do something with data ...
}
The SctpStreams.receive call would look something like the following.
Obviously there's some elements missing to make it more readable:
MessageInfo receive( ByteBuffer buffer, int stream )
{
StreamData data = getStreamData( stream );
synchronized (data.queue)
{
// If data is available on the queue, pick it up.
if ( !data.queue.isEmpty() )
{
QueueData queueData = data.queue.remove();
// copy data from queue.
buffer.put( queueData.buffer );
returnBufferToPool( queueData.buffer );
queueData.buffer = null;
return queueData.msgInfo;
}
else
{
// No data on the queue, so wait.
data.isWaiting = true;
data.queue.wait(); // wait for data to be available for this
stream.
MessageInfo msgInfo = data.channel.receive( buffer, _handlerData,
_handler )
data.isWaiting = false;
data.queue.notify(); // tell the main reading thread we got the
data.
return msgInfo;
}
}
The final part of the puzzle is the reading thread.
class ReadingThread
implements Runnable
{
public void run()
{
while ( _channel.isOpen() )
{
MessageInfo msgInfo = _channel.peek();
StreamData streamData = getStreamData( msgInfo.streamNumber()
);
synchronized ( streamData.queue )
{
if ( streamData.isWaiting )
{
streamData.queue.notify(); // tell waiting thread
to read data directly from input.
streamData.queue.wait(); // wait until thread
finishes reading.
}
else
{
// no thread waiting. Will need to allocate buffer
and put on queue.
ByteBuffer buffer = getBufferFromPool();
MessageInfo msgInfo = _channel.receive( buffer,
_handlerData, _handler );
QueueData queueData = new QueueData( buffer, msgInfo
);
streamData.queue.put( queueData );
}
}
}
}
}
For the above to work there needs to be some way of knowing what data is
about to be received before you actually make the call to receive. Using
the peek call the thread can find out if there is a waiting buffer (and/or
thread) to put the data. I've got no idea if this sort of information is
available on the underlying implementation to allow a peek method, but I
think it would be useful.
The fall back case is actually the method that will need to be used with the
current API for this design. The data needs to be read into a buffer
(probably from a buffer pool) and then copied again into the buffer waiting
for the data.
I'm currently doing something like the above for the SSL/TLS design. As SSL
requires that the encrypted data is read and then un-encrypted into another
buffer, the design above should work reasonably well. I don't need to peek
and can just read all data into a single buffer and if a buffer/thread is
waiting I can decode directly into it.
>
>
> The second is that this will require separate SSLEngine objects for each
>>>
>>>> bi-directional stream.
>>>>
>>>> Only if you want separate SSL sessions per stream. You could have a
>>> single
>>> SSL session, that is one SSLEngine, per association/channel. Therefore,
>>> all
>>> streams would use the same SSLEngine. You would need to ensure that each
>>> message is read completely before a message on another stream is
>>> received,
>>> this is the default behavior on Solaris.
>>>
>>>
>> My understanding was that the SSLEngine is a kind of state machine. If
>> you
>> had two streams on one association/channel trying to perform handshaking
>> at
>> the same time, the order of messages could get confused. That was the
>> main
>> reason I was thinking that I'd require different SSLEngines on each
>> stream.
>> Is my understanding on this wrong?
>>
> SSLEngine is a state machine, but what I was suggesting is that you have a
> single SSL session for the channel. Handshaking takes place after the
> connection is made and then data is exchanged on any stream. As far as the
> SSLEngine is concerned it is simply decrypting data is does not concern
> itself with there the data comes from. You would need to keep the
> association between data and stream at the application level.
>
> The difference between this design and what you suggested is that there is
> only one SSL session per channel where you were suggesting one SSL session
> per stream. Either should work, but I don't see that you need a session for
> each stream.
>
I was really just following RFC3436 as it says that "the TLS handshake
protocol is used on each bi-directional stream separately." I agree though
that either will work. For now I'll probably do a full handshake on each
stream and if I can work out the abbreviated handshake method later it
should be easier to implement.
David.
>
> [snip]
>
> -Chris.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mail.openjdk.java.net/pipermail/sctp-dev/attachments/20090624/9a19fbae/attachment.html
More information about the sctp-dev
mailing list