JxtaMulticastSocket覆写了java.net.MulticastSocket的bind方法:
@Override public void bind(SocketAddress addr) throws SocketException { if (isBound()) { throw new SocketException("Already bound"); } }
要求在JxtaMulticastSocket的构造函数中会调用bind方法,确保构造函数调用时socket没有被绑定,否则抛出SocketException异常
然而JxtaMulticastSocket的基类java.net.MulticastSocket的构造函数在jdk7和jdk8中的处理是不同的
然而JxtaMulticastSocket的基类java.net.MulticastSocket的构造函数在jdk7和jdk8中的处理是不同的
jdk7:
public MulticastSocket(SocketAddress bindaddr) throws IOException { super((SocketAddress) null); // Enable SO_REUSEADDR before binding setReuseAddress(true); if (bindaddr != null) { bind(bindaddr); } }
jdk8:
public MulticastSocket(SocketAddress bindaddr) throws IOException { super((SocketAddress) null); // Enable SO_REUSEADDR before binding setReuseAddress(true); if (bindaddr != null) { try { bind(bindaddr); } finally { if (!isBound()) close(); } } }
可见在jdk8中,如果未绑定,会额外调用close()方法,就是这个close()方法导致抛出了异常
其实在这里不能调用close()方法,从代码上看JxtaMulticastSocket在实现的时候要求此时不能绑定(见其覆写的bind方法)
而close方法也被JxtaMulticastSocket覆写了
而close方法也被JxtaMulticastSocket覆写了
/** * Closes this MutlicastSocket. */ @Override public synchronized void close() { if (closed) { return; } bound = false; closed = true; in.close(); outputPipe.close(); in = null; }
此时in尚未初始化,故in.close()会导致java.lang.NullPointerException异常
————————————
1 简单的话,可以修改JxtaMulticastSocket的构造函数,使其不再调用父类的构造函数以避免close()问题(父类构造函数中的相关代码要在子类中重新实现一遍)。
2 或者不修改JxtaMulticastSocket的源码,我们干脆自己实现一个子类,提供自己的构造函数初始化代码。
3 较好的方式那就要全面考虑JxtaMulticastSocket的实现了,那就比较复杂了
————————————————————————
。。。
1和2现在看起来不大现实,因为父类java.net.MulticastSocket最终都调用了如下构造方法:
/** * Create a MulticastSocket bound to the specified socket address. * <p> * Or, if the address is {@code null}, create an unbound socket. * * <p>If there is a security manager, * its {@code checkListen} method is first called * with the SocketAddress port as its argument to ensure the operation is allowed. * This could result in a SecurityException. * <p> * When the socket is created the * {@link DatagramSocket#setReuseAddress(boolean)} method is * called to enable the SO_REUSEADDR socket option. * * @param bindaddr Socket address to bind to, or {@code null} for * an unbound socket. * @exception IOException if an I/O exception occurs * while creating the MulticastSocket * @exception SecurityException if a security manager exists and its * {@code checkListen} method doesn't allow the operation. * @see SecurityManager#checkListen * @see java.net.DatagramSocket#setReuseAddress(boolean) * * @since 1.4 */ public MulticastSocket(SocketAddress bindaddr) throws IOException { super((SocketAddress) null); // Enable SO_REUSEADDR before binding setReuseAddress(true); if (bindaddr != null) { try { bind(bindaddr); } finally { if (!isBound()) close(); } } }
这意味着子类无论直接还是间接,最终都要调用到这个方法,故close()难以避免的啊!
那就只有修改JxtaMulticastSocket覆写的close()方法了,原close()方法:
/** * Closes this MutlicastSocket. */ @Override public synchronized void close() { if (closed) { return; } bound = false; closed = true; in.close(); outputPipe.close(); in = null; }
修改后:如果尚未绑定的话,close()不执行任何操作
/** * Closes this MutlicastSocket. */ @Override public synchronized void close() { // modified by cuizhf, 20131126 // @see http://www.cnblogs.com/cuizhf/admin/EditPosts.aspx?postid=3443599 if(!bound) { return; } if (closed) { return; } bound = false; closed = true; in.close(); outputPipe.close(); in = null; }
或者这样写:
/** * Closes this MutlicastSocket. */ @Override public synchronized void close() { // modified by cuizhf, 20131126 // @see http://www.cnblogs.com/cuizhf/admin/EditPosts.aspx?postid=3443599 if (!bound || closed) { return; } bound = false; closed = true; in.close(); outputPipe.close(); in = null; }
虽然不是很优雅,至少应该能解决眼前的问题。(实话说Jxse的代码确实算不上优雅)
————————————————————————————————————————————————————————
另外一种修改方式就是将java.net.MulticastSocket单独从JDK中提出到在自己的工程中,然后按自己的需要修改(在构造函数中去掉close()的调用),最后使JxtaMulticastSocket继承自修改后的这个类。
—————————————————————————————————————————————————————————
好吧,从今天开始,secondegg项目的开发全面转向JDK8(JavaFX8)。