尽管可读,但选择器仍未激活

时间:2015-03-23 13:22:41

标签: java multithreading selector nio

我目前正致力于使用UDP多播的教师项目。 我试图实施NIO,因为我必须处理3个频道。 基本上,其中一个目标是将文件拆分为块,然后将这些块发送到多播组以便存储。

我的问题是,我实际上发送了所有块,但是并没有全部收到它们。从块1到块4通常它会很好,但随后它跳过块,并且跳过我的意思是通道没有& #39; t可读。

以下是处理选择器和频道的代码:

//Peer Class Constructor
public Peer(String mcast_addr, int mcast_port, String mcastbackup_addr, int mcastbackup_port, String mcastrestore_addr, int mcastrestore_port) throws IOException {
    this.ni = NetworkInterface.getByName("eth0");

    this.mcast_port = mcast_port;
    this.mcast_addr = mcast_addr;
    this.controlAddr = InetAddress.getByName(mcast_addr);
    this.ctrladr = new InetSocketAddress(mcast_addr,mcast_port);

    this.mcastbackup_addr = mcastbackup_addr;
    this.mcastbackup_port = mcastbackup_port;
    this.backupAddr = InetAddress.getByName(mcastbackup_addr);
    this.backadr = new InetSocketAddress(mcastbackup_addr, mcastbackup_port);

    this.mcastrestore_addr = mcastrestore_addr;
    this.mcastrestore_port = mcastrestore_port;
    this.restoreAddr = InetAddress.getByName(mcastrestore_addr);
    this.restadr = new InetSocketAddress(mcastrestore_addr, mcastrestore_port);

    this.selector = this.initSelector();

}               //TODO Check all methods/variables used in several thread and check concurrency
@Override
public void run() {
    System.out.println("Waiting...\n");
    while (true) {
        try {
            // Process any pending changes
            synchronized (this.pendingChanges) {
                Iterator changes = this.pendingChanges.iterator();
                while (changes.hasNext()) {
                    ChangeRequest change = (ChangeRequest) changes.next();
                    switch (change.type) {
                        case ChangeRequest.CHANGEOPS:
                            SelectionKey key = change.socket.keyFor(this.selector);
                            key.interestOps(change.ops);
                            break;
                    }
                }
                this.pendingChanges.clear();
            }

            // Wait for an event in one of the registered channels
            int readyChannels = selector.select();
            if (readyChannels == 0) continue;

            // Iterate over the set of keys for which events are available
            Iterator selectedKeys = this.selector.selectedKeys().iterator();

            while (selectedKeys.hasNext()) {
                SelectionKey key = (SelectionKey) selectedKeys.next();
                selectedKeys.remove();

                if (!key.isValid()) {
                    System.out.println("Not Valid Key");
                    continue;
                }
                // Check what event is available and deal with it
                if (key.isReadable()) {
                    System.out.println("Readable");
                    this.read(key);
                } else if (key.isWritable()) {
                    System.out.println("Writable");
                    this.write(key);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//Channels Initialization - OPTION: REUSEADRR, MULTICAST_LOOPBACK, MULTICAST ON INTERFACE, MULTICAST TIME TO LIVE = 1
public Selector initSelector() throws IOException {
    Selector channelSelector = Selector.open();

    this.controlChannel = DatagramChannel.open(StandardProtocolFamily.INET)
            .setOption(StandardSocketOptions.SO_REUSEADDR, true)
            .setOption(StandardSocketOptions.IP_MULTICAST_LOOP, false)
            .setOption(StandardSocketOptions.IP_MULTICAST_IF, ni)
            .setOption(StandardSocketOptions.IP_MULTICAST_TTL, 1)
            .bind(new InetSocketAddress(mcast_port));
    controlChannel.configureBlocking(false);
    MembershipKey key = controlChannel.join(controlAddr, ni);
    controlChannel.register(channelSelector, SelectionKey.OP_READ);

    this.backupChannel = DatagramChannel.open(StandardProtocolFamily.INET)
            .setOption(StandardSocketOptions.SO_REUSEADDR, true)
            .setOption(StandardSocketOptions.IP_MULTICAST_LOOP, false)
            .setOption(StandardSocketOptions.IP_MULTICAST_IF, ni)
            .setOption(StandardSocketOptions.IP_MULTICAST_TTL, 1)
            .bind(new InetSocketAddress(mcastbackup_port));
    backupChannel.configureBlocking(false);
    MembershipKey key1 = backupChannel.join(backupAddr, ni);
    backupChannel.register(channelSelector, SelectionKey.OP_READ);


    return channelSelector;
}

//Read from channel method - Deploys runnable to execute the protocol
public void read(SelectionKey key) throws IOException {
    DatagramChannel channel = (DatagramChannel) key.channel();

    this.readBuffer.clear();

    System.out.println("Listening....");
    String in;

    try {
        InetSocketAddress sa = (InetSocketAddress) channel.receive(this.readBuffer);

        readBuffer.flip();
        ByteBuffer message = readBuffer.slice();

        int i = getReceivingChannel(sa);
        switch(i)
        {
            case BC:
                System.out.println("Backup Service for: " + sa.getHostName());
                initiateBackupService(message);
                break;

            case RC:
                System.out.println("Restore");
                break;

            case CC:
                System.out.println("Control");
                break;
        }

       // System.out.println(Arrays.toString(readBuffer.array()));
        /*String s = new String(readBuffer.array(), 0, readBuffer.limit());
        in = s;*/
       // System.out.println("Received: " + s + " from " + sa.getHostName() + " at port: " + sa.getPort());

    } catch (IOException e) {
        key.cancel();
        channel.close();
        e.printStackTrace();
        return;
    }

    this.selector.wakeup();
}

//Writes to channel pending information when exists
private void write(SelectionKey key) throws IOException {
    DatagramChannel channel = (DatagramChannel) key.channel();

    synchronized (this.pendingData) {
        List queue = (List) this.pendingData.get(channel);
        // Write until there's not more data ...
        while (!queue.isEmpty()) {
            ByteBuffer buf = ByteBuffer.allocate(Chunk.CHUNK_SIZE);

            buf.clear().flip();
            buf = (ByteBuffer) queue.get(0);

            int i = channel.send(buf, backadr);
            //System.out.println("Sending to address: " + backadr.getHostName() + " :\n" + new String(buf.array(), 0, buf.array().length) + "\n");
            if (buf.remaining() > 0) {
                // ... or the socket's buffer fills up
                break;
            }
            queue.remove(0);
        }

        if (queue.isEmpty()) {
            System.out.println("no data");
            // We wrote away all data, so we're no longer interested
            // in writing on this socket. Switch back to waiting for
            // data.

            key.interestOps(SelectionKey.OP_READ);
        }
    }
}

//Send Method - When the user triggers the backup button this is the class where we add what we want to send to the pendingdata
public void send(DatagramChannel channel, byte[] data) throws IOException {

    synchronized (this.pendingChanges) {
        // Indicate we want the interest ops set changed
        this.pendingChanges.add(new ChangeRequest(channel, ChangeRequest.CHANGEOPS, SelectionKey.OP_WRITE));

        // And queue the data we want written
        synchronized (this.pendingData) {
            List queue = (List) this.pendingData.get(channel);
            if (queue == null) {
                queue = new ArrayList();
                this.pendingData.put(channel, queue);
            }
            System.out.println(queue.size());
            queue.add(ByteBuffer.wrap(data));
        }
    }

    // Finally, wake up our selecting thread so it can make the required changes
    this.selector.wakeup();
}

public void initiateBackupFileProtocol(File file, int repDegree) throws IOException {
    //TODO Create runnable to execute de backupsubprotocol

    Future future = executor.submit(new BackupSubProtocol(this.executor, this, controlChannel, backupChannel, file, repDegree, backadr));

    try {
        if(future.get() == null){
            System.out.println("Backup Sub-Protocol finished correctly\n");
        }
        else
            System.out.println("Error while executing BackupSubProtocol.");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

    /* FileEntry filee = new FileEntry(file);

    Chunk c = new Chunk(filee, 1, 2,"coisas".getBytes());

    Message back = new PutChunkMessage(1, 0, filee.getFileID(), 0, 1, new byte[0]);

    System.out.println(new String(back.constructMessage(), "UTF-8"));
    System.out.println();

    Message sto = new StoredMessage(1, 0, filee.getFileID(), 0);

    System.out.println(new String(sto.constructMessage(), "UTF-8"));


    Message get = new GetChunkMessage(1, 0, filee.getFileID(), 0);

    System.out.println(new String(get.constructMessage(), "UTF-8"));


    Message ch = new ChunkMessage(1, 0, filee.getFileID(), 0, new byte[0]);

    System.out.println(new String(ch.constructMessage(), "UTF-8"));
    System.out.println();

    Message del = new DeleteMessage(1, 0, filee.getFileID());

    System.out.println(new String(del.constructMessage(), "UTF-8"));

    Message rem = new RemovedMessage(1, 0, filee.getFileID(), 0);

    System.out.println(new String(rem.constructMessage(), "UTF-8"));
*/

    //send(backupChannel, back.constructMessage());
    //send(controlChannel, str.getBytes());
}

public class ChangeRequest {
    public static final int CHANGEOPS = 2;

    public DatagramChannel socket;
    public int type;
    public int ops;

    public ChangeRequest(DatagramChannel socket, int type, int ops) {
        this.socket = socket;
        this.type = type;
        this.ops = ops;
    }
}

public int getReceivingChannel(InetSocketAddress sa)
{
    int port = sa.getPort();

    if(port == mcastbackup_port)
    {
        return BC;
    }
    else if(port == mcast_port)
    {
        return CC;
    }
    else if(port == mcastrestore_port)
    {
        return RC;
    }

    return -1;
}

public void initiateBackupService(ByteBuffer m) throws UnsupportedEncodingException {
    Future future = executor.submit(new BackupService(this.executor, m));

    try {
        if(future.get() == null){
            System.out.println("Backup Sub-Protocol finished correctly\n");

        }
        else
            System.out.println("Error while executing BackupSubProtocol.");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}

public void initiateRestoreFileProtocol(byte[] FileId) throws IOException {
    //TODO Create runnable to execute de backupsubprotocol

    Future future = executor.submit(new RestoreSubProtocol(this.executor, this, controlChannel, restoreChannel, FileId));

    try {
        if(future.get() == null){
            System.out.println("Backup Sub-Protocol finished correctly\n");
        }
        else
            System.out.println("Error while executing BackupSubProtocol.");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}

这是我收到的内容的印刷品:

receive

据我通过Wireshark证实,它发送了218个块,但是正如图像所示,它会跳过许多块。

也许是选择阻止,但我无法理解为什么。

任何帮助将不胜感激,
提前致谢

编辑#1 - 似乎如果我在发送每个数据报之后插入一个Thread.sleep它正常工作,还有另一个方法吗?

1 个答案:

答案 0 :(得分:0)

您似乎并不知道UDP不可靠:也就是说,它缺乏任何可靠性功能,因此永远不会检测或补偿数据包丢失。添加睡眠会降低传输速率,从而降低丢包率。您需要在协议中实现某种可靠性功能。