在任何地方都有一个简单的基于NIO的UDP服务器示例或教程吗?

时间:2012-12-28 20:41:41

标签: java android udp nio

我正在为Android中的IPv4通信编写一个简单的UDP服务器。我一直在寻找网络上的NIO文档,以及很好的例子,但我发现的一切看起来都过于复杂,或者根本不起作用。理想的解决方案使用一个非阻塞线程,一旦读取了数据,就会调用一个侦听器/委托对象。下面的代码是我试图使用的 - 但它不起作用,我不知道为什么。任何人都可以指出我正确的方向,或解释这个代码缺少什么?

package com.npeinc.netutil;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.Buffer;
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.Timer;
import java.util.TimerTask;


public class AsyncUdpServer implements Runnable
{
    /** true if the socket should be IPv6. False for IPv4. */
    private boolean ipv6;

    private DatagramChannel channel;

    private ByteBuffer buffer;

    private Selector selector;

    private Listener delegate;

    private SelectionKey listenerKey;

    private boolean isLooping = false;

    private InetAddress address;
    private int port;

    public InetAddress getAddress()
    {
            return address;
    }
    public int getPort()
    {
            return port;
    }

    private AsyncUdpServer()
    {

    }

    public static AsyncUdpServer initIPv4()
    {
            AsyncUdpServer socket = new AsyncUdpServer();
            socket.ipv6 = false;

            return socket;
    }

    public static AsyncUdpServer initIPv6()
    {
            AsyncUdpServer socket = new AsyncUdpServer();
            socket.ipv6 = true;

            return socket;
    }

    public void delegateListener(Listener listener)
    {
            delegate = listener;
    }

    public void bindToPort(int port) throws IOException
    {
            selector = Selector.open();
            channel = DatagramChannel.open();
            InetSocketAddress isa;
            if (ipv6)
                    isa = new InetSocketAddress(port);
            else
                    isa = new InetSocketAddress((InetAddress) null, port);

            channel.socket().bind(isa);
            channel.configureBlocking(false);
            address = channel.socket().getLocalAddress();
            this.port = channel.socket().getLocalPort();

            listenerKey = channel.register(selector, SelectionKey.OP_READ, delegate);

            selector.wakeup();
    }

    public void enableBroadcast() throws IOException, NullPointerException
    {
            if (channel != null)
                    channel.socket().setBroadcast(true);
            else
                    throw new NullPointerException("Socket has not been initialized! You must call bindToPort(int) first.");
    }

    public void stop()
    {
            isLooping = false;
    }



    @Override
    public void run() 
    {
            isLooping = true;
            while(isLooping)
            {
                    try
                    {

                            selector.select();
                            Set<SelectionKey> keys = selector.selectedKeys();
                            Iterator<SelectionKey> iterator = keys.iterator();
                            while (iterator.hasNext())
                            {
                                    SelectionKey key = iterator.next();
                                    if (key == this.listenerKey)
                                    {
                                            Listener listener = (Listener) key.attachment();
                                            DatagramPacket packet = new DatagramPacket(null, 0);
                                            channel.socket().receive(packet);
                                            listener.onDataReceived(this, packet.getData());
                                            selector.wakeup();
                                    }
                                    iterator.remove();

                            }
                    }
                    catch (IOException e)
                    {
                            e.printStackTrace();
                    }
            }
    }


    public interface Listener
    {
            public void onDataReceived(AsyncUdpServer server, byte[] data);
    }
}

我也尝试添加这个方法,并在我的run()方法中启动它,但它只是向logcat打印了一堆难以辨认的垃圾:

public void receiveData(long timeout)
{
            if (timeout == 0)
                    return;

            final class Runny implements Runnable {

                    public boolean shouldLoop = true;

                    @Override
                    public void run() 
                    {
                            while(shouldLoop)
                            {
                                    try {
                                            ByteBuffer buffer = ByteBuffer.allocate(1024);
                                            channel.receive(buffer);
                                            byte[] dst = new byte[buffer.capacity()];
                                            buffer.get(dst);
                                            delegate.onDataReceived(AsyncUdpServer.this, dst);
                                    } catch (IOException e) {
                                            e.printStackTrace();
                                    }
                            }
                    }
            };

            final Runny runny = new Runny();

            final Thread thread = new Thread(runny);
            thread.start();

            if (!(timeout < 0))
            {
                    Timer t = new Timer();
                    t.schedule(new TimerTask(){

                            @Override
                            public void run() {
                                    runny.shouldLoop = false;
                            }

                    }, timeout);
            }

    }

2 个答案:

答案 0 :(得分:1)

这没有多大意义。

  • 您认为与IPv4或IPv6绑定的代码不会执行任何此类操作;
  • 无需唤醒刚刚创建的选择器,或者当前未在select()中阻止的选择器
  • 您无法在从空字节数组创建的DatagramPacket中收到任何内容
  • select()已经有一个超时参数,因此实现自己的超时机制是多余的; ......

解决所有问题并重试。

答案 1 :(得分:0)

我建议您为异步网络工作人员使用Netty库:它非常好用且速度快,可以消除大量的低级手写,并为并发提供良好的抽象层(就像现在只有一次执行一样)线程,它提供开箱即用的多线程解决方案)。它适用于TCP和UDP。