2017-06-15 96 views
2

我需要打开N个多播套接字(其中N来自参数列表的大小)。然后,我会将相同的数据发送到循环中的N个套接字中的每个套接字,最后关闭每个套接字。我的问题是,我如何使用try-with-resources块来做到这一点?下面是我如何与一个单一的资源做到这一点:Java试用资源未知资源计数

final int port = ...; 
try (final MulticastSocket socket = new MulticastSocket(port)) { 
    // Do a bunch of sends of small packet data over a long period of time 
    ... 
} 

我能想到的唯一的办法具有多个端口要做到这一点是:

final List<Integer> ports = ...; 
final List<MulticastSocket> sockets = new ArrayList<>(ports.size()); 
try { 
    for (final Integer port : ports) { 
     sockets.add(new MulticastSocket(port)); 
    } 

    // Do a bunch of sends of small packet data over a long period of time 
    ... 
} finally { 
    for (final MulticastSocket socket : sockets) { 
     try { 
      socket.close(); 
     } catch (final Throwable t) { 
      // Eat the exception 
     } 
    } 
} 

有没有更简洁的方式为了实现这一点,还是我提出的解决方案一样好?

回答

0

由麦克Nakis提出这个想法的启发,我想出了下面的类...

package myNamespace; 

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.Iterator; 
import java.util.List; 
import java.util.ListIterator; 

import myNamespace.ThrowingFunction; 
import myNamespace.ThrowingSupplier; 

/** Collection of AutoCloseable objects */ 
public class ResourceCollection<T extends AutoCloseable> 
     implements Iterable<T>, AutoCloseable { 

    /** Resources owned by this instance */ 
    private final List<T> myResources; 

    /** 
    * Constructor 
    * @param allocator Function used to allocate each resource 
    * @param count  Number of times to call the allocator 
    * @throws E Thrown if any of the allocators throw 
    */ 
    public <E extends Throwable> ResourceCollection(
      final ThrowingSupplier<T, E> allocator, final int count) 
      throws E { 
     myResources = new ArrayList<>(count); 
     try { 
      while (myResources.size() < count) { 
       final T resource = allocator.getThrows(); 
       myResources.add(resource); 
      } 
     } catch (final Throwable e) { 
      close(); 
      throw e; 
     } 
    } 

    /** 
    * Constructor 
    * @param allocator Function used to allocate each resource 
    * @param input  List of input parameters passed to the allocator 
    * @throws E Thrown if any of the allocators throw 
    */ 
    public <U, E extends Throwable> ResourceCollection(
      final ThrowingFunction<U, T, E> allocator, final Collection<U> input) 
      throws E { 
     myResources = new ArrayList<>(input.size()); 
     try { 
      for (final U value : input) { 
       final T resource = allocator.applyThrows(value); 
       myResources.add(resource); 
      } 
     } catch (final Throwable e) { 
      close(); 
      throw e; 
     } 
    } 

    /** 
    * Gets the number of resources in the collection 
    * @return The number of resources in the collection 
    */ 
    public int size() { 
     return myResources.size(); 
    } 

    /** 
    * Gets whether the collection contains no resources 
    * @return Whether the collection contains no resources 
    */ 
    public boolean isEmpty() { 
     return myResources.isEmpty(); 
    } 

    /** 
    * Gets the resource at index i 
    * @param i The index of a resource, in the range [0, size()) 
    * @return The resource at index i 
    */ 
    public T get(final int i) { 
     return myResources.get(i); 
    } 

    @Override 
    public Iterator<T> iterator() { 
     return myResources.iterator(); 
    } 

    @Override 
    public void close() { 
     final ListIterator<T> resourceIter = 
       myResources.listIterator(myResources.size()); 
     while (resourceIter.hasPrevious()) { 
      final T resource = resourceIter.previous(); 
      if (resource != null) { 
       try { 
        resource .close(); 
        resourceIter.remove(); 
       } catch (final Throwable t) { 
        // Eat the exception 
       } 
      } 
     } 
    } 

} 
1

你正在做的事情几乎和它一样好。

您可以创建一个AutoCloseable通用多接近它包含一个List<AutoCloseable>并接受作为构造参数closeables和工厂的计数来调用创建的每个关闭的,然后全部关闭调用它的close()时,所以你可以这样使用它:

try(MultiCloser<MulticastSocket> multiCloser = 
     new MultiCloser<>(ports.size(), i -> new MulticastSocket(ports.get(i))) 
{ 
    for(MulticastSocket socket : multiCloser.getItems()) 
    { 
     do something with the socket 
    } 
} 

...但它可能会是一个矫枉过正。

+0

什么是'port'在lambda表达式? – saka1029

+0

@ saka1029啊,那里,修好了。但那不是重点。关键是构造。细节留给学生练习。 –

2

就做递归保持的保证尝试 - 与资源:

void foo(List<Integer> ports, List<Socket> sockets) { 
    if (sockets.size() == ports.size()) { 
    // Do something with your sockets. 
    } else { 
    try (Socket s = new MulticastSocket(ports.get(sockets.size())) { 
     sockets.add(s); 
     foo(ports, sockets); 
     // You could call sockets.remove(sockets.size()-1) here. 
     // Not convinced whether it's worth it. 
    } 
    } 
} 
+0

所以你不能循环,但你可以创建一个递归调用?这真的很好 – Eugene

+0

我不得不阅读这两次,以了解你在做什么。 +1的创造力和简洁性,但我认为我更喜欢Mike建议创建一个实用程序AutoCloseable类。它似乎更具可读性,并滥用递归这只是感觉...脏。 –

+0

@JeffG你应该做你感觉舒服的事情,因为最终你必须保持它;但我认为你会发现它很杂乱,很难实现完全等效的多个TWR块的等价语义,而没有这样的东西。 –

0

什么是使用ArrayList存储MulticastSocket实例的地步?

你说:

我会再发送相同的数据给N个插槽的 循环中,最后,关闭每个插座。

因此,您可以在循环中创建它们并为每次迭代发送相同的处理。
要做到这一点,你应该稍微改变一下设计。
MulticastSocket的处理任务可以通过功能接口来执行,该接口也允许指定要使用的端口。

例如:

@FunctionalInterface 
public interface SocketProcessor { 
    void process(MulticastSocket multicastSocket) ; 
} 

可能您需要为参数此功能界面的方法来应用处理:从客户端代码

public static void processSocket(SocketProcessor socketProcessor, Integer port) throws IOException { 
    try (final MulticastSocket socket = new MulticastSocket(port)) { 
    socketProcessor.process(socket); 
    } 
} 

最后,你可以创建一个带有拉姆达的socket处理器实例:

SocketProcessor socketProcessor = (MulticastSocket socket) -> { 
    socket.send(...); 
    socket.send(...); 
}; 

然后你可以在端口循环才能调用processSocket与合适的端口和刚创建的SocketProcessor实例:

for (final Integer port : ports) { 
    try { 
     processSocket(socketProcessor, port); 
    } catch (IOException e) { 
     // do processing 
    } 
} 

该解决方案是没有必要的缩短(没有被真正更长的时间),但它确实是更清晰。
两个主要的担忧是分开的:

  • processSocket(SocketProcessor)执行所述锅炉板代码

  • SocketProcessor定义具体的任务。

+0

您的回答让我意识到,我忘记指定数据包需要交错发送(例如,在所有套接字上发送数据包1,然后在所有套接字上发送数据包2,...)。鉴于这是我的要求之一(这在我的问题中肯定没有),这个答案需要为每个需要发送的数据包创建一个新的套接字。 –

+0

@Jeff G在这种情况下,确实提出的答案不适合您的需要。 – davidxxx