套接字在LAN或NET上无法正常工作,但完全在本地

时间:2015-01-11 10:32:59

标签: java sockets

我正在用Java编写网络软件,但是通过“真正的”网络使用我的应用程序时遇到了一个真正的问题。

让软件成为主机,并监听客户端连接。 这是我的服务器循环:

public void run() {                                                 
    while (mServerSocket != null) {                                        
        try {                                                              
            Socket wClient = mServerSocket.accept();                       
            System.out.println("Client connecté");                         
            wClient.setSoTimeout(50);                                      
            wClient.setTcpNoDelay(false);                                  
            Client c = new Client(wClient);                                
            synchronized(this) {                                           
                mWaitingClients.add(c);                                    
                c.start();                                                 
            }                                                              
        } catch(Exception ex) {                                              
            System.out.println("Server error : " + ex.getMessage());       
        }                                                                  
    }                                                                      
}                                                                          

当客户端尝试连接到服务器时,我使用此功能:

public Client connect(InetAddress addr, int port) throws Exception {
    Socket socket = new Socket(addr, port);                        
    socket.setSoTimeout(50);                                       
    socket.setTcpNoDelay(false);                                   
    Client c = new Client(socket);                                 
    c.start();                                                     
    return c;                                                      
}                                                                  

这是客户端循环:

public void run() {                                                             
    try {                                                                       
        ObjectOutputStream out = new ObjectOutputStream(mSocket.getOutputStream());                                                                                 
        ObjectInputStream in = new ObjectInputStream(mSocket.getInputStream()); 

        while(mSocket.isConnected() && !mSocket.isClosed()) {                   
            for (int i = 0; i < mOutMessages.size(); i++) {                     
                Message msg = mOutMessages.get(i);                              
                out.writeObject(msg);                                           
            }                                                                   
            out.flush();                                                        
            mOutMessages.clear();                                               

            Thread.sleep(50);                                                   
            out.reset();                                                        

            while(true) {                                                       
                try {                                                           
                    Message m = (Message) in.readObject();                      
                    mInMessages.add(m);                                         
                } catch (Exception e) {                                         
                    break;                                                      
                }                                                               
            }                                                                   

            Thread.sleep(50);                                                   
        }                                                                       
    } catch(Exception ex) {                                                     
        try {                                                                   
            mSocket.close();                                                    
        } catch(Exception exx) {                                                
            exx.printStackTrace();                                              
        }                                                                       
        ex.printStackTrace();                                                   
    }                                                                           
}                                                                               

程序的其他一些部分执行Message并将它们放在客户端的输出列表中(mOutMessages)。 程序的其他一些部分从客户端的mInMessages读取消息。

但这有点不对劲。它在本地工作正常(服务器和客户端在同一台计算机上),但使用两台计算机(使用LAN或通过Internet)发生故障或有害(有些消息已发送但从未收到)。 服务器检测到来自客户端的连接,向客户端发送“握手”消息,但客户端从未接收到它们。

我更像是一个C程序员而不是Java程序员,我使用libc套接字从来没有遇到过这种问题,那么,为什么我的做法是错误的呢?

谢谢!

编辑: 我的服务器是使用此功能创建的:

public void open(int port) throws Exception {
    mServerSocket = new ServerSocket(port);  
    start(); // Call the run mentionned above.                                
}                                            

编辑: 这是我的解决方案,也许它不完美但它有效!

public void run() {                                                        
    try {                                                                  
        BufferedOutputStream buf_out = new BufferedOutputStream(           
            mSocket.getOutputStream()                                      
        );                                                                 
        BufferedInputStream buf_in = new BufferedInputStream(              
            mSocket.getInputStream()                                       
        );                                                                 
        ObjectOutputStream out = new ObjectOutputStream(buf_out);          
        out.flush();                                                       
        ObjectInputStream in = new ObjectInputStream(buf_in);              

        while(mSocket.isConnected() && !mSocket.isClosed()) {              
            for (int i = 0; i < mOutMessages.size(); i++) {                
                Message msg = mOutMessages.get(i);                         
                out.writeObject(msg);                                      
                out.flush();                                               
            }                                                              
            mOutMessages.clear();                                          
            out.reset();                                                   

            while(true) {                                                  
                try {                                                      
                    Message m = (Message) in.readObject();                 
                    mInMessages.add(m);                                    
                } catch (Exception e) {                                    
                    break;                                                 
                }                                                          
            }                                                              
        }                                                                  
    } catch(Exception ex) {                                                
        try {                                                              
            mSocket.close();                                               
        } catch(Exception exx) {                                           
            exx.printStackTrace();                                         
        }                                                                  
        ex.printStackTrace();                                              
    }                                                                      

1 个答案:

答案 0 :(得分:0)

如果我理解正确,客户端和服务器都使用run方法。如果客户端和服务器同时写入足够大的消息(不适合所涉及的缓冲区),则会出现死锁,因为两个伙伴都没有进入读取状态(这会耗尽整个缓冲区)。由于网络延迟,这可能仅发生在非本地场景中,即可能有足够的时间在mOutMessages缓冲区中堆积足够的消息。

请注意,Socket.setSoTimeout(您使用过的)的文档仅表示它会影响read()。 (例如,在我的JDK中,ObjectOutputStream似乎使用缓冲区大小为1024字节的BlockDataOutputStream。)

我建议使用单独的线程进行读/写,或者(如果知道最大消息大小)使用足够大的缓冲区(将SocketOutputStream包裹在BufferedOutputStream中)。如果您选择更大的缓冲区,您可能还希望一次编写一条消息(并尝试在每个消息之后读取消息)。