我目前正致力于使用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();
}
}
这是我收到的内容的印刷品:
据我通过Wireshark证实,它发送了218个块,但是正如图像所示,它会跳过许多块。
也许是选择阻止,但我无法理解为什么。
任何帮助将不胜感激,
提前致谢
编辑#1 - 似乎如果我在发送每个数据报之后插入一个Thread.sleep它正常工作,还有另一个方法吗?
答案 0 :(得分:0)
您似乎并不知道UDP不可靠:也就是说,它缺乏任何可靠性功能,因此永远不会检测或补偿数据包丢失。添加睡眠会降低传输速率,从而降低丢包率。您需要在协议中实现某种可靠性功能。