2013-05-21 22 views
0

我正在面对此问题,在我的一个套件中使用ServerSocket,我们只需将其称为:FooBundleOSGi套接字:套件已停止,套接字仍然打开

This FooBundle其中包括SocketListener.java类。这个类是主题,并做出了一点概述,我会贴一些伪代码:

public class SocketListener implements Runnable{ 

ServerSocket providerSocket; 
Socket connection = null; 
private boolean closeIt = false; 

    public void run() { 
     try { 
      //Create the server socket 
      providerSocket = new ServerSocket(41000, 10); 
     } catch (IOException e1) { 
      //catching the exception.... 
     } 
     while(!closeIt){ 
     try{ 
      connection = providerSocket.accept(); 
      in = new Scanner(new InputStreamReader(onnection.getInputStream())); 
      while(in.hasNext() !=false) 
        message = message + " "+in.next(); 
       // bla bla bla... 
      } catch (IOException e) { 
       //bla bla... 
      } 
     finally{ 
     try{ 
       if (message.equalsIgnoreCase("bye")) 
        providerSocket.close(); 
          closeIt = true; 
      } 
     catch(IOException ioException){ 
     //........ 
     } 
} 

正如你可以看到,这是一个简单的线程等待,直到它从一个接收消息的连接SocketClients“再见”

这是我面对现在的问题:当捆绑为停止,我确实需要重启整个OSGi框架:如果我试图重新启动包,一个java.net.BindException消息抛出:“地址已在使用”。所以,我停止了捆绑,但套接字尚未关闭。

在OSGi中,您需要注意Activator中的stop()方法必须包含的内容,但我无法将任何匿名线程的引用传递给激活器。

试想一下,这是捆绑在我的类图:

**FooBundle** 
|__FooBundleActivator 
|__FooImpl 
|__SocketListener (thread) 

SocketListener线程从FooImpl类称为一个匿名线程

我的问题是:是否有任何适当的方法来控制匿名线程,特别是在我的情况下,非闭合套接字端口,在OSGi范例内?

在此先感谢。

回答

3

如果你的包被告知要停止,那么假设停止的人知道他在做什么。是的,你的协议期望'再见',但是狗屎发生了,任何有这些问题的协议对于现实世界来说太脆弱了。一般来说,OSGi中的所有任务都应该有一个生命周期。所以这将是我的代码(使用DS而不是激活器)。

@Component 
public class ProtocolServer extends Thread { 
    volatile ServerSocket server; 
    volatile Socket connection; 

    public ProtocolServer() { 
    super("Protocol Server on 4100"); // to identify the thread 
    } 

    @Activate void activate() { 
    setDaemon(true); 
    start(); 
    } 

    @Deactivate void deactivate() { 
    interrupt(); 

    // best effort close (even if null) 
    try { server.close(); } catch(Exception e) {} 
    try { connection.close(); } catch(Exception e) {} 

    join(10000); // waits 10 secs until thread exits 
    } 

    public void run() { 
    // loop for active component 
    while(!isInterrupted()) 
    try { 
     doServer(); 
    } catch(Exception e) { 
     log(e); 

     // bad error, accept failed or bind failed 
     // or server socket was closed. If we should remain 
     // active, sleep to prevent overloading the 
     // system by trying too often, so sleep 

     if (!isInterrupted()) 
     try { Thread.sleep(5000); } catch(Exception e) {} 
    } 
    } 

    private void doServer() throws Exception { 
     server = new ServerSocket(4100) 
     try { 
     while(!isInterrupted()) 
      doConnection(server); 
     } finally { 
     server.close(); 
     } 
    } 

    private void doConnection(ServerSocket server) throws Exception { 
    connection = server.accept(); 
    try { 
     doMessages(connection); 

     // the pseudo code exits here, but that seems 
     // kind of weird? If desired, interrupt 
     // this object, this will exit the thread 

    } catch(Exception e) { 
     log(e); // the connection failed, is not uncommon 
    } finally { 
     connection.close(); 
     connection = null; 
    } 
    } 

    private void doMessages(Socket connection) { 
    MyScanner s = new MyScanner(socket); 
    String msg; 
    while(!isInterrupted() && !"bye".equals(msg=s.getMessage())) 
     process(msg); 
    } 
} 

OSGi中一个重要的设计考虑因素是,即使存在故障,组件也能继续工作。在网络中,你经常会有短暂的错误消失。即使他们不这样做,服务器在解决问题时仍然继续尝试。你的伪代码在实践中将是一场噩梦,因为它会在任何错误中消失。任何具有多个此类组件的系统往往会变得很快不稳定。

有一点让我感到惊讶的是,您一次只支持一个连接。一般来说,最好不要限制它,并在自己的线程中处理消息。在这种情况下,您必须确保为连接创建的每个处理程序也适当关闭。

+0

只接收一个客户端的问题是由于伪代码,我的真实代码接受多个连接。非常感谢您的回答。不仅对于代码,而且对于建议。从彼得·克里恩斯那里得到答案真的很荣幸。谢谢! –

1

在退出线程函数之前,无论消息如何,您都需要关闭该监听套接字。那么对你来说真正有用的是在该套接字上调用setReuseAddress(true)以允许绑定端口,而旧连接在超时状态下挂起。

而且,请请请,用更好的压痕技术在你的代码...

+0

感谢您的回答尼古拉。但是,将ReuseAddress参数设置为true并不能真正解决此问题,因为我仍然得到相同的绑定异常。我的线程需要保持活动状态,直到消息“再见”到达为止,但不能有任何其他可能的条件(它必须连续运行)。 OSGi捆绑包的问题在于,您可以随时手动阻止它们。如果此特定捆绑包已停止,则套接字不会自行关闭。哦,对于缩进感到抱歉,只是手动编写了伪代码:S。 +1帮助 –

+0

您无法“重新绑定”相同的套接字。您必须关闭它并创建一个新的,或者停止调用accept(),然后再恢复。 –

+0

这个问题是我无法创建一个新的,因为我有很多客户端转发到这个特定的端口。现在的解决方案是停止整个OSGi框架,这对我的应用程序来说并不“真正”。应该有任何一种方法来控制捆绑内的匿名线程“制造”,但我找不到合适的解决方案。 –

2

实例化的ServerSocket外(可能在活化剂),并通过构造函数将其传递给SocketListener。你可以在Activator的stop函数中调用serverSocket.stop()。

如果调用ServerSocket.stop(),将抛出一个SocketException,它是IOException的子类。请考虑在while迭代中处理IOException,以确保它会停止执行迭代。

+0

感谢您的回答Balazs。我面临的唯一问题是,我已经“激活了”激活器太多,所以我不想再使用其他代码。启动和停止方法应该很简单,以避免延迟激活捆绑包。无论如何,你给了我一个未来部署的好主意。 +1 –

+0

那么,在这种情况下,您必须以某种方式将启动和停止逻辑与函数和类分开。但是,如果你创建新的类,他们也必须具有停止/关闭功能。无论如何,唯一的解决方案是你可以在捆绑停止时停止服务器套接字。 –