Possible DatagramChannel (can't be)Bug??! Must be me, what am I doing wrong?

Hi all NIO smarties please tell me why this code does not work :slight_smile:
I also posted to the Java networking forums (Duke Dollars and all:-)) but there is always so much more action over here.
Here’s the set up, a simple DatagramChannel test app. This can’t be a BUG?

From my forum post:
“I have two class, one is reading, one is writing, using DatagramChannels that are connected, i.e. I am not using receive/send, I am using read/write. I say I am using but it’s not working, so I should say I WANT to. :slight_smile: It does connect and I get one good read but then selector.select() blocks OR if I disconnect and reconnet everytime (as commented out) it works, but what’s the point of connecting then I could just use receive…”

What am I doing wrong?
Here’s the compilable code. Give it a try…

/*
 * Main.java
 *
 * Created on June 9, 2005, 8:17 PM
 */
package network;

import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.util.*;

public class Main
{
    public static void main(String[] args)
    {
        int id = 100;
        try
        {
            // Create the server socket channel
            DatagramChannel server = DatagramChannel.open();
            // nonblocking I/O
            server.configureBlocking(false);
            // host-port 8000
            String host = "LocalHost";
            server.socket().bind(new java.net.InetSocketAddress(host,8000));
            // Create the selector
            Selector selector = Selector.open();
            // Recording server to selector
            server.register(selector,SelectionKey.OP_READ);
            System.out.println(server);
            
            // Infinite server loop
            boolean connected = false;
            for(;;)
            {
                Thread.currentThread().yield();
                // Waiting for events
                System.out.println("reading1...");
                selector.select();
                System.out.println("reading1.1...");
                // Get keys
                Set keys = selector.selectedKeys();
                Iterator i = keys.iterator();
                if ( !connected )
                {
                    server.connect(new InetSocketAddress(host,8000));
                    connected = true;
                }
                // For each keys...
                while(i.hasNext())
                {
                    System.out.println("reading2...");
                    SelectionKey key = (SelectionKey) i.next();
                    // Remove the current key
                    i.remove();
                    //for(;;)
                    {
                        try
                        {
                            Thread.currentThread().sleep(500);
                        }
                        catch(Exception e)
                        {};
                        int BUFFER_SIZE = 32;
                        ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
                        try
                        {
                            int count = server.read(buffer);
                            System.out.println(count);
                        }
                        catch (Exception e)
                        {
                            // client is no longer active
                            e.printStackTrace();
                            continue;
                        }
                        
                        //client.disconnect();
                        // Show bytes on the console
                        buffer.flip();
                        Charset charset=Charset.forName("ISO-8859-1");
                        CharsetDecoder decoder = charset.newDecoder();
                        CharBuffer charBuffer = decoder.decode(buffer);
                        System.out.print(charBuffer.toString());
                    }
                }        
            }
        }
        catch(Exception e)
        {e.printStackTrace();}
    }
}
/*
 * Client.java
 *
 * Created on June 10, 2005, 11:28 PM
 */

package network;

import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.util.*;

public class Client
{
    public static void main(String[] args)
    {
        String host = "LocalHost";
        int id = 100;
        try
        {
            // Create client SocketChannel
            DatagramChannel client = DatagramChannel.open();
            
            // Connection to host port 8000
            Thread.currentThread().sleep(5000);
            client.connect(new java.net.InetSocketAddress(host,8000));
            
            // nonblocking I/O
            client.configureBlocking(false);
            
            // Create selector
            Selector selector = Selector.open();
            
            // Record to selector
            SelectionKey clientKey = client.register(selector, SelectionKey.OP_WRITE);
            
            while (selector.select(500)> 0)
            {
                // Get keys
                Set keys = selector.selectedKeys();
                Iterator i = keys.iterator();
                System.out.println("writing1...");
                // For each key...
                while (i.hasNext())
                {
                    SelectionKey key = (SelectionKey)i.next();
                    System.out.println("writing2...");
                    
                    // Remove the current key
                    i.remove();
                    
                    // Get the socket channel held by the key
                    DatagramChannel channel = (DatagramChannel)key.channel();
                    
                    // Write continuously on the buffer
                    ByteBuffer buffer = null;
                    for (;;)
                    {
                        try
                        {
                            Thread.currentThread().sleep(1000);
                        }
                        catch(Exception e)
                        {};
                        System.out.println("writing...");
                        buffer =
                                ByteBuffer.wrap(
                                new String(" Client " + id + " ").getBytes());
                        channel.write(buffer);
                        buffer.clear();
                    }
                }
            }
        }
        catch(Exception e)
        {e.printStackTrace();}
    }
}

Gah. UDP in NIO always does my head in ;). A few thoughts off the top of my head:

  • there are some issues with “connect”'ing a DatagramChannel that mean you end up having to re-register it with the selector as soon as you make it into a connected DC; off the top of my head I cannot remmeber what, and don’t take this as universal advice, but … perhaps an avenue to think about.

  • read-once problems are nearly always a mistake in your interaction with the selector. Usually (3 times in 5) these are bugs in Sun’s NIO implementation :P. The rest of the time it’s usually an extra register() or incorrect interestops. Looked through your code but couldn’t see anything obviously wrong (but I’m v.tired at the moment, so not much helkp :frowning: )

  • a connected DC should behave exactly the same as an SC - certainly the grexengine uses the same codepaths for both once the former is connected. Try rewriting it to use an SC throughout and check it still (doesn’t) works?

  • replace that client with something like Siege, which is programmed to send a stream of UDP packets at your server, and display the bytes to screen. Very useful for rinding bugs in your code - otherwise you are simultaneously trying to debug both client and server :frowning:

I havent looekd at the code closely yet, but keep in mind that the selector set is no necessarily thread safe. In general they have top
be messed withonly in your select loop.

It might be to do with the way you’re removing SelectionKey instances that you’ve processed. You’re using the iterator to remove them rather than removing them from the original set (if this is a problem its an implementation bug). Its been a while but if the keys arn’t actually being removed from the original set then the select presumably doesn’t notice any change in state and doesn’t return? Maybe… um…

If I was at home I’d try it :slight_smile:

Kev

Nice idea, but this is the standard pattern that most people use (including me :)), because it avoids annoying CCME’s and requires only one line of code, so IMHO very unlikely to be the problem.

Also, on the iterator.remove()… this exmaple was made from skeleton I got in books, online, etc. I didn’t pick that, it was what was in the examples. I never found a complete working example using DatagramChannel connected using read() so I made this.
Anyone else? Try compiling and running it, you’ll see, no worky without a repeating connect()/disconnect() which as I understand it should not be needed, and mroeover the whole point of connect is to avoid some checking on the data on it’s way in and out of the JVM…Mystery…

So as an alternate to this mess…

I just want example server/client code that uses DatagramChannels WITH connect() that works.
Sites, link, code, anything. I went and bought Java Network Programming and it describes it and has other samples but never shows a working example and this is what I got when I followed the examples :frowning:

BTW, I have done networking code before as well, back before NIO.

Thanks again for any help/pointers/links etc!

Tut tut, don’t know why I didn’t spot this one early on.

You’re connecting to the wrong address. You’ve connected the main’s socket to its own address rather than that which is the source of the first packet. So, what you’ve done is told the socket that it may only recieve packets from its own address (by this point its already read and cached the first one). So, the first packet is available and every packet afterwards fails to get through security check.

This means that you must use recieve() to get the first lump of data since you need to get hold of the remote address that you’re going to connect to:


if ( !connected )
 {
     ByteBuffer firstPacket = ByteBuffer.allocate(32);
     SocketAddress remoteAddress = server.recieve(firstPacket);
     server.connect(removeAddress);
     connected = true;
}  

Works for me then but you’re first lump of data is read in a different way. :frowning:

Kev

Woo-Hoo! 'tis true, you have discovered the flaw and thus, YOU ROCK!

Thanks. when I have a little better code perhaps I will post a working example in the code section but this forum will help others none-the-less.