我正在为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);
}
}
答案 0 :(得分:1)
这没有多大意义。
DatagramPacket
中收到任何内容select()
已经有一个超时参数,因此实现自己的超时机制是多余的; ...... 解决所有问题并重试。
答案 1 :(得分:0)
我建议您为异步网络工作人员使用Netty库:它非常好用且速度快,可以消除大量的低级手写,并为并发提供良好的抽象层(就像现在只有一次执行一样)线程,它提供开箱即用的多线程解决方案)。它适用于TCP和UDP。