UDP connections supported?

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

UDP connections supported?

Radim Kolar
Are UDP sessions
client ip:port -> server ip:port mapped to different Selection keys or
every UDP packet arriving to server port belongs to same selection key?

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: UDP connections supported?

Jeanfrancois Arcand-2


Radim Kolar SF.NET wrote:
> Are UDP sessions
> client ip:port -> server ip:port mapped to different Selection keys or
> every UDP packet arriving to server port belongs to same selection key?

If I understand correctly your question, the SelectionKey is associated
with the connection, not the packet. Hence all packets from the same
connection use the same SelectionKey. It that what you ask?

Thanks!

-- Jeanfrancois

>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

suitability for writing UDP servers

Radim Kolar
> If I understand correctly your question, the SelectionKey is associated
> with the connection, not the packet. Hence all packets from the same
> connection use the same SelectionKey. It that what you ask?
I think Grizzly needs some tuning for better support for writing UDP
servers. His UDP support seems to be limited to writing udp client apps.

Main problem is:
   all UDP packets arriving into same server ip:server port combo are sharing same SelectionKey.
     this cause some problems:
        * at lot of places in code filters/other components are canceling SelectionKeys on io errors (such as UDP output buffer full). If they cancel SelectionKey, server socket is closed and nobody other
can use server until server is restarted.
        * DefaultSelectionKeyHandler closes server socket after 30 secs of
          inactivity
        * UDPWriteFilter Closes server socket after sending data too.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: suitability for writing UDP servers

Jeanfrancois Arcand-2

Hi,

Radim Kolar SF.NET wrote:
>> If I understand correctly your question, the SelectionKey is associated
>> with the connection, not the packet. Hence all packets from the same
>> connection use the same SelectionKey. It that what you ask?
> I think Grizzly needs some tuning for better support for writing UDP
> servers. His UDP support seems to be limited to writing udp client apps.
>
> Main problem is:
>    all UDP packets arriving into same server ip:server port combo are sharing same SelectionKey.
>      this cause some problems:

Can you explain how to handle that with NIO? Grizzly uses SelectionKey,
but the creation is made by the NIO layer of the JDK. If I understand
the issue properly, you would like to have one packet per SelectionKey?

>         * at lot of places in code filters/other components are canceling SelectionKeys on io errors (such as UDP output buffer full). If they cancel SelectionKey, server socket is closed and nobody other
> can use server until server is restarted.

Hum, that would be a bug if that's the case. When the SelectionKey is
cancelled, it means the connection is either closed by the client of
something unexpected happens. Do you have a test case I can use to
reproduce the problem?

>         * DefaultSelectionKeyHandler closes server socket after 30 secs of
>           inactivity

Yes that's a design decision we took. You can disable the feature by doing:

>             DefaultSelectionKeyHandler keyHandler =
>                     new DefaultSelectionKeyHandler(){
>                 public void expire(Iterator<SelectionKey> keys){
>                 } // The connections are never going to be closed
>             };


>         * UDPWriteFilter Closes server socket after sending data too.

It close socket only when an exception happens. Are you seeing something
different? If yes, it means there is a bug :-)

Thanks so far for the feedback!

-- Jeanfrancois

>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: suitability for writing UDP servers

Radim Kolar
> Can you explain how to handle that with NIO? Grizzly uses SelectionKey, but
> the creation is made by the NIO layer of the JDK.

> If I understand the issue properly, you would like to have one packet per SelectionKey?
no, i would like to have one selection key per (client ip:client port:server ip:server port).

statefull firewalls can do something like this and it can be possibly simulated in Java:
Code something like ACCEPT for UDP:

After receiving UDP packet on server socket, check own database of client ip/client port combos. If client ip/port combo is found in database it is probably
safe to ignore packet because it should arrive on second channel too.

If no known client is found, create new UDP channel and bind it to server ip,port (with addr reuse) and then connect() it to client ip/port. you will get
new SelectionKey - unique to client

This is a bit OS dependent because UDP packet from client can be received by both channels or just by newly created channel. Modern OSes should forward received UDP packet just to new channel but it needs some testing.

>>         * at lot of places in code filters/other components are canceling
>> SelectionKeys on io errors (such as UDP output buffer full). If they
>> cancel SelectionKey, server socket is closed and nobody other
>> can use server until server is restarted.
>
> Hum, that would be a bug if that's the case. When the SelectionKey is
> cancelled, it means the connection is either closed by the client of
> something unexpected happens. Do you have a test case I can use to
> reproduce the problem?
Its timing dependent. test case is like this:
 client creates UDP socket connect it to server, sends packet,closes socket.
server reads packet,sends reply (UDP stack returns error because client closed socket), server closes socket
and no other client can connect to it.

>>         * DefaultSelectionKeyHandler closes server socket after 30 secs of
>>         inactivity
>
> Yes that's a design decision we took. You can disable the feature by doing:
you are closing listening socket if no client connects to TCP server in last 30
seconds by default? Because in UDP case, it closes socket but Controller select
loop is still running, so result is that nobody can connect to it anymore.
While it can be good design decision to close client sockets after 30 seconds,
it should not close server socket.

> It close socket only when an exception happens. Are you seeing something
> different? If yes, it means there is a bug :-)
UDPSelectorHandler needs
protected Boolean isClient = false; field
when running in client mode, don't create server socket, when running in
server mode, never close sockets on errors.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: suitability for writing UDP servers

Jeanfrancois Arcand-2


Radim Kolar SF.NET wrote:

>> Can you explain how to handle that with NIO? Grizzly uses SelectionKey, but
>> the creation is made by the NIO layer of the JDK.
>
>> If I understand the issue properly, you would like to have one packet per SelectionKey?
> no, i would like to have one selection key per (client ip:client port:server ip:server port).
>
> statefull firewalls can do something like this and it can be possibly simulated in Java:
> Code something like ACCEPT for UDP:
>
> After receiving UDP packet on server socket, check own database of client ip/client port combos. If client ip/port combo is found in database it is probably
> safe to ignore packet because it should arrive on second channel too.
>
> If no known client is found, create new UDP channel and bind it to server ip,port (with addr reuse) and then connect() it to client ip/port. you will get
> new SelectionKey - unique to client
>
> This is a bit OS dependent because UDP packet from client can be received by both channels or just by newly created channel. Modern OSes should forward received UDP packet just to new channel but it needs some testing.
>
>>>         * at lot of places in code filters/other components are canceling
>>> SelectionKeys on io errors (such as UDP output buffer full). If they
>>> cancel SelectionKey, server socket is closed and nobody other
>>> can use server until server is restarted.
>> Hum, that would be a bug if that's the case. When the SelectionKey is
>> cancelled, it means the connection is either closed by the client of
>> something unexpected happens. Do you have a test case I can use to
>> reproduce the problem?
> Its timing dependent. test case is like this:
>  client creates UDP socket connect it to server, sends packet,closes socket.
> server reads packet,sends reply (UDP stack returns error because client closed socket), server closes socket
> and no other client can connect to it.
>
>>>         * DefaultSelectionKeyHandler closes server socket after 30 secs of
>>>         inactivity
>> Yes that's a design decision we took. You can disable the feature by doing:
> you are closing listening socket if no client connects to TCP server in last 30
> seconds by default?

We close connection that are idle for more than 30 seconds. As an
example, a browser always open a connection to a server and wait for the
server to close it after X times. A good example is by running:

% telnet localhost 8080
GET / HTTP/1.1
Host: localhost

The WebServer will return the page and then telnet will be idel for 30
seconds. After that the connection will be closed (but the server still
listen to port 8080).


  Because in UDP case, it closes socket but Controller select
> loop is still running, so result is that nobody can connect to it anymore.
> While it can be good design decision to close client sockets after 30 seconds,
> it should not close server socket.

I don't think it close the server socket, does it? This is a serious bug
if it does that :-)

>
>> It close socket only when an exception happens. Are you seeing something
>> different? If yes, it means there is a bug :-)
> UDPSelectorHandler needs
> protected Boolean isClient = false; field
> when running in client mode, don't create server socket, when running in
> server mode, never close sockets on errors.

Agreed. This is not the behavior right now? Let me take a look.

Thanks!!!

-- Jeanfrancois


>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: suitability for writing UDP servers

Radim Kolar
>  Because in UDP case, it closes socket but Controller select
>> loop is still running, so result is that nobody can connect to it anymore.
>> While it can be good design decision to close client sockets after 30
>> seconds,
>> it should not close server socket.
> I don't think it close the server socket, does it? This is a serious bug if
> it does that :-)
yes it does. create udp server then send 1 udp packet to it, wait 30 seconds and it will close server socket.  without sending any packets to server socket is not closed.

You should check same case against TCP server too, i didn't checked it. it
will be most likely fine because ppl will already complain about it.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: suitability for writing UDP servers

Oleksiy Stashok
In reply to this post by Radim Kolar
Hello Radim,

Radim Kolar SF.NET wrote:

>> If I understand the issue properly, you would like to have one packet per SelectionKey?
>>    
> no, i would like to have one selection key per (client ip:client port:server ip:server port).
>
> statefull firewalls can do something like this and it can be possibly simulated in Java:
> Code something like ACCEPT for UDP:
>
> After receiving UDP packet on server socket, check own database of client ip/client port combos. If client ip/port combo is found in database it is probably
> safe to ignore packet because it should arrive on second channel too.
>
> If no known client is found, create new UDP channel and bind it to server ip,port (with addr reuse) and then connect() it to client ip/port. you will get
> new SelectionKey - unique to client
>
> This is a bit OS dependent because UDP packet from client can be received by both channels or just by newly created channel. Modern OSes should forward received UDP packet just to new channel but it needs some testing.
>  
It's good idea. Think we need to implement this in Grizzly. At least as
one of the possible modes.

Thanks!

WBR,
Alexey.

>  
>>>         * at lot of places in code filters/other components are canceling
>>> SelectionKeys on io errors (such as UDP output buffer full). If they
>>> cancel SelectionKey, server socket is closed and nobody other
>>> can use server until server is restarted.
>>>      
>> Hum, that would be a bug if that's the case. When the SelectionKey is
>> cancelled, it means the connection is either closed by the client of
>> something unexpected happens. Do you have a test case I can use to
>> reproduce the problem?
>>    
> Its timing dependent. test case is like this:
>  client creates UDP socket connect it to server, sends packet,closes socket.
> server reads packet,sends reply (UDP stack returns error because client closed socket), server closes socket
> and no other client can connect to it.
>
>  
>>>         * DefaultSelectionKeyHandler closes server socket after 30 secs of
>>>         inactivity
>>>      
>> Yes that's a design decision we took. You can disable the feature by doing:
>>    
> you are closing listening socket if no client connects to TCP server in last 30
> seconds by default? Because in UDP case, it closes socket but Controller select
> loop is still running, so result is that nobody can connect to it anymore.
> While it can be good design decision to close client sockets after 30 seconds,
> it should not close server socket.
>
>  
>> It close socket only when an exception happens. Are you seeing something
>> different? If yes, it means there is a bug :-)
>>    
> UDPSelectorHandler needs
> protected Boolean isClient = false; field
> when running in client mode, don't create server socket, when running in
> server mode, never close sockets on errors.
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>  

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: suitability for writing UDP servers

Oleksiy Stashok
In reply to this post by Radim Kolar
Hello Radim,

>> If I understand the issue properly, you would like to have one packet per SelectionKey?
>>    
> no, i would like to have one selection key per (client ip:client port:server ip:server port).
>
> statefull firewalls can do something like this and it can be possibly simulated in Java:
> Code something like ACCEPT for UDP:
>
> After receiving UDP packet on server socket, check own database of client ip/client port combos. If client ip/port combo is found in database it is probably
> safe to ignore packet because it should arrive on second channel too.
>
> If no known client is found, create new UDP channel and bind it to server ip,port (with addr reuse) and then connect() it to client ip/port. you will get
> new SelectionKey - unique to client
>
> This is a bit OS dependent because UDP packet from client can be received by both channels or just by newly created channel. Modern OSes should forward received UDP packet just to new channel but it needs some testing.
>  
I investigated possibility of implementing such behavior.
Basically the main problem is that it's impossible (or I didn't find the
way) to retrieve remote address on server side udp socket, until UDP
packet is not read. Because
datagramChannel.socket().getRemoteSocketAddress() returns null for
server side Datagram channel. And only
datagramChannel.receive(ByteBuffer) returns SocketAddress.
So in case, if UDP packet will come to both channels - it means read
operation should be executed twice, which seems could lead to perf. issues.

As alternative I can think about implementing own SelectionKey
mechanism, so we will have own map of host:port -> SelectionKey
correspondence. And pass these custom SelectionKeys, when performing
server side UDP processing.

What do you think?

Thanks.
WBR,
Alexey.

>  
>>>         * at lot of places in code filters/other components are canceling
>>> SelectionKeys on io errors (such as UDP output buffer full). If they
>>> cancel SelectionKey, server socket is closed and nobody other
>>> can use server until server is restarted.
>>>      
>> Hum, that would be a bug if that's the case. When the SelectionKey is
>> cancelled, it means the connection is either closed by the client of
>> something unexpected happens. Do you have a test case I can use to
>> reproduce the problem?
>>    
> Its timing dependent. test case is like this:
>  client creates UDP socket connect it to server, sends packet,closes socket.
> server reads packet,sends reply (UDP stack returns error because client closed socket), server closes socket
> and no other client can connect to it.
>
>  
>>>         * DefaultSelectionKeyHandler closes server socket after 30 secs of
>>>         inactivity
>>>      
>> Yes that's a design decision we took. You can disable the feature by doing:
>>    
> you are closing listening socket if no client connects to TCP server in last 30
> seconds by default? Because in UDP case, it closes socket but Controller select
> loop is still running, so result is that nobody can connect to it anymore.
> While it can be good design decision to close client sockets after 30 seconds,
> it should not close server socket.
>
>  
>> It close socket only when an exception happens. Are you seeing something
>> different? If yes, it means there is a bug :-)
>>    
> UDPSelectorHandler needs
> protected Boolean isClient = false; field
> when running in client mode, don't create server socket, when running in
> server mode, never close sockets on errors.
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>  

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: suitability for writing UDP servers

Radim Kolar
> I investigated possibility of implementing such behavior.
> Basically the main problem is that it's impossible (or I didn't find the
> way) to retrieve remote address on server side udp socket, until UDP packet
> is read.
you are right. need to wait for packet.

> Because datagramChannel.socket().getRemoteSocketAddress()
> returns null for server side Datagram channel. And only
> datagramChannel.receive(ByteBuffer) returns SocketAddress.
> So in case, if UDP packet will come to both channels
in most cases it will not. I think Linux and BSD are right, not sure about
windows, but it is unlikely that someone runs production server on windows
and nobody cares about performance hit for development system (most ppl develop on Windows, deploy on unix).

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: suitability for writing UDP servers

Oleksiy Stashok

>> Because datagramChannel.socket().getRemoteSocketAddress()
>> returns null for server side Datagram channel. And only
>> datagramChannel.receive(ByteBuffer) returns SocketAddress.
>> So in case, if UDP packet will come to both channels
>>    
> in most cases it will not. I think Linux and BSD are right, not sure about
> windows, but it is unlikely that someone runs production server on windows
> and nobody cares about performance hit for development system (most ppl develop on Windows, deploy on unix).
>  
I checked on Windows XP with JDK 1.5.0_11. Packet comes just on one of
the channels, but from run to run it chooses different channel:
sometimes new registered ("dedicated"), sometimes old "server" channel.
Looks like this trick could be unpredictable.

Thanks.

WBR,
Alexey.
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>  

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: suitability for writing UDP servers

Radim Kolar
> I checked on Windows XP with JDK 1.5.0_11. Packet comes just on one of the
> channels, but from run to run it chooses different channel: sometimes new
> registered ("dedicated"), sometimes old "server" channel.
> Looks like this trick could be unpredictable.
this is easy to work with. if they arrive to dedicated channel, just process them and you must check all arriving packets on server channel against list of clients anyway.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: suitability for writing UDP servers

Oleksiy Stashok


Radim Kolar SF.NET wrote:
>> I checked on Windows XP with JDK 1.5.0_11. Packet comes just on one of the
>> channels, but from run to run it chooses different channel: sometimes new
>> registered ("dedicated"), sometimes old "server" channel.
>> Looks like this trick could be unpredictable.
>>    
> this is easy to work with. if they arrive to dedicated channel, just process them and you must check all arriving packets on server channel against list of clients anyway.
>  
yes. But problem I see (on Windows XP, not sure about other OSs), that
after creating dedicated channel in response to the first came
message... it happens that *all* following client messages are coming
either just to dedicated channel or just to server channel from run to
run. Here is simple test I wrote (attached).

WBR,
Alexey.
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [hidden email]
> For additional commands, e-mail: [hidden email]
>
>  

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package test;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author Oleksiy
 */
public class UDPTesterSingleSelector {
    private static final String HOST = "localhost";
    private static final int PORT = 19000;
   
    private static final long TIME_OUT = 2000;
   
    private boolean isStoped;
    private Selector selector;
   
    private AtomicBoolean flag = new AtomicBoolean(true);
   
    public static void main(String[] args) throws IOException {
        new UDPTesterSingleSelector().process();
    }

    private void process() throws IOException {
        try {
            startServer();
            runClient();
        } finally {
            isStoped = true;
        }
    }

    private void runClient() throws IOException {
        DatagramSocket socket = new DatagramSocket();
        try {
            SocketAddress socketAddress = new InetSocketAddress(HOST, PORT);
            for (int i = 0; i < 10; i++) {
                String testString = "Packet #" + i;
                byte[] buf = testString.getBytes();
                DatagramPacket packet = new DatagramPacket(buf, buf.length, socketAddress);
                socket.send(packet);
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                }
            }
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
    }

    private void startServer() throws IOException {
        final CountDownLatch latch = new CountDownLatch(1);
       
        new Thread() {
            @Override
            public void run() {
                try {
                    selector = Selector.open();
                    DatagramChannel datagramChannel = DatagramChannel.open();

                    DatagramSocket datagramSocket = datagramChannel.socket();
                    datagramSocket.setReuseAddress(true);
                    datagramSocket.bind(new InetSocketAddress(HOST, PORT));
                    datagramChannel.configureBlocking(false);

                    datagramChannel.register(selector, SelectionKey.OP_READ);
                   
                    latch.countDown();
                   
                    runSelect("SERVER", selector, new ReceiveCallback() {
                        public void onReceived(SocketAddress address) throws IOException {
                            if (flag.getAndSet(false)) {
                                runDedicatedChannel(address);
                            }
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }.start();
       
        try {
            latch.await();
        } catch (Exception e) {
        }
    }

    private void runSelect(String name, Selector selector, ReceiveCallback receiveCallback) throws IOException {
        ByteBuffer buf = ByteBuffer.allocate(8192);
        while (!isStoped) {
            int keys = selector.select(TIME_OUT);
            if (keys > 0) {
                Set<SelectionKey> keySet = selector.selectedKeys();
                Iterator<SelectionKey> it = keySet.iterator();
                while (it.hasNext()) {
                    SelectionKey key = it.next();
                    it.remove();
                    if (key.isReadable()) {
                        DatagramChannel channel = (DatagramChannel) key.channel();
                        SocketAddress address = channel.receive(buf);
                       
                        System.out.println("Receiver \"" + name + "\" Channel: " + channel + " remoteAddress: " + channel.socket().getInetAddress() + " Selector: " + selector + " received message from: " + address);
                        buf.clear();
                        if (receiveCallback != null) {
                            receiveCallback.onReceived(address);
                        }
                    }
                }
            }
        }
    }
   
    private void runDedicatedChannel(final SocketAddress address) throws IOException {
        DatagramChannel datagramChannel = DatagramChannel.open();

        DatagramSocket datagramSocket = datagramChannel.socket();
        datagramSocket.setReuseAddress(true);
        datagramSocket.bind(new InetSocketAddress(HOST, PORT));
        datagramSocket.connect(address);
        datagramChannel.configureBlocking(false);

        datagramChannel.register(selector, SelectionKey.OP_READ);
        System.out.println("Registered dedicated channel: " + datagramChannel + " on selector: " + selector);
    }

    private interface ReceiveCallback {
        public void onReceived(SocketAddress address) throws IOException;
    }
}


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]