В настоящее время я работаю над проектом проекта с использованием многоадресной рассылки UDP. Я пытаюсь реализовать NIO, так как мне приходится иметь дело с 3-мя каналами. В основном одна из целей состоит в том, чтобы разбить файл на куски, а затем отправить эти фрагменты в группу многоадресной рассылки для хранения.
Моя проблема в том, что я на самом деле отправляю все куски, но не получаю их всех. От куска 1 до куска 4 обычно идет хорошо, но потом он пропускает куски, и, пропуская, я имею в виду, что канал не читается.
Здесь код, который имеет отношение к селектору и каналам:
//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 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 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();
}
}
Вот распечатка того, что я получаю:
Он отправил, предположительно, 218 кусков, как я подтвердил через Wireshark, но, как говорится в изображении, он пропускает многие из них.
Может быть, он блокирует выбор, но я не могу понять, почему.
Любая помощь будет оценена,
заранее спасибо
Редактировать # 1 - Кажется, что если я вставляю Thread.sleep после отправки каждой дейтаграммы, он работает правильно, есть ли еще один aproach?
Вы, похоже, не знаете, что UDP не является надежным, т.е. Он не обладает функциями надежности, поэтому потеря пакетов никогда не обнаруживается и не компенсируется. Добавление сон снизило скорость передачи и, следовательно, скорость потери пакетов. В вашем протоколе необходимо реализовать некоторые функции надежности.