2013-02-07 24 views
6

Tôi có một ứng dụng Client/Server đơn giản. Máy chủ được thiết lập sao cho nếu không có dữ liệu đến trong vòng N giây, thời gian chờ xảy ra và kết nối ổ cắm bị đóng. Tôi làm điều này thông qua Socket.setSoTimeout(). Đó là tất cả các công trình tốt nếu khách hàng treo cứng. Nếu khách hàng chết, mặc dù (ví dụ: tôi giết nó bằng Ctrl-C), sau đó readLine() không bao giờ hết giờ.Trong Java, tại sao readLine() chặn trên một kết nối socket đóng?

Dưới đây là mã máy chủ, nếu mà làm cho một sự khác biệt:

public void run() 
{ 
    PrintWriter out = null; 
    BufferedReader in = null; 

    try { 
     sock.setSoTimeout(10000); 

     out = new PrintWriter(sock.getOutputStream(), true); 
     in = new BufferedReader(new InputStreamReader(sock.getInputStream())); 

     String input; 
     while ((input = in.readLine()) != null) { 

tôi đã cố gắng đưa vào một handler tín hiệu vào khách hàng để gửi một tin nhắn Abend trên máy chủ nhưng điều đó không làm việc (tôi nghi ngờ socket được đóng trước khi ABEND được gửi đi, nhưng tôi đã không thực sự dành thời gian để cố gắng tìm ra điều đó).

Có cách nào để thức dậy theo định kỳ và kiểm tra trạng thái ổ cắm để xem nó có bị đóng không? Hoặc (tốt hơn) không có readLine() treo nếu socket đóng? Tôi có nên sử dụng một trình đọc không bị chặn không? Có một trình đọc không có bộ đệm hỗ trợ một cơ chế giống như readLine tồn tại không?

Tôi đang sử dụng Java 6 trên Linux.

CHỈNH SỬA: Tôi đang tự mình giết chính khách hàng trong thời gian không hoạt động; tất cả dữ liệu đã được gửi và nhận tại thời điểm này. Tôi đã xác minh (thông qua ps) rằng chương trình khách hàng không còn chạy; quá trình Java đã thực sự bị giết. Sử dụng netstat tôi có thể thấy rằng socket được đóng, trên cả hai đầu, nhưng readLine() vẫn bị treo.

+0

xem http://stackoverflow.com/questions/1577719/java-sockets-bufferedreader-and-readline-hang - có lẽ khách hàng đã bị giết ngay sau khi CR được gửi và readLine() không bao giờ tiêu thụ LF, do đó, nó choáng váng vì nó mãi mãi – mantrid

+0

Tôi đang tự giết bản thân khách hàng trong một thời gian nhàn rỗi; tất cả dữ liệu đã được gửi và nhận tại thời điểm này. –

+0

Loại Socket nào? TCP hoặc UDP? – ecbrodie

Trả lời

1

Tôi nghĩ rằng bạn đã chẩn đoán sai sự cố.

Nếu phía đầu vào của ổ cắm đã được đóng, sau đó gọi readLine() gọi phải sẽ đóng lại. Sau khi tất cả dữ liệu đệm đã được tiêu thụ, cuộc gọi phải trả lại null. (Bạn sẽ không nhận được một IOException nếu socket được đóng bình thường, nhưng bạn có thể nhận được một nếu kết nối lần ra ... ví dụ.)

Giải thích rõ ràng nhất là ổ cắm vẫn chưa được đóng. Hãy xem xét các tình huống trong đó đầu kia sẽ đóng socket và tìm ra lý do tại sao điều này chưa xảy ra.


Không có giải pháp nào trong số này là giải pháp, nhưng tôi sẽ trả lời cho đầy đủ.

Có cách nào để đánh thức định kỳ và kiểm tra trạng thái ổ cắm để xem trạng thái đã đóng chưa?

Một chủ đề khác có thể làm điều đó, nhưng không phải là chủ đề này.

Hoặc (tốt hơn) không có readLine() treo nếu ổ cắm đóng?

Đó là những gì được cho là xảy ra.

Tôi có nên sử dụng trình đọc chưa được lọc của một số loại không?

Nó sẽ không hữu ích. Bạn sẽ phải tự mình thực hiện readLine() và mã của bạn sẽ chặn tại cùng một điểm. Hành vi chặn đang xảy ra bên dưới lớp đệm và ký tự giải mã của chuỗi trình đọc.

Trình đọc không được hỗ trợ có hỗ trợ cơ chế giống như readLine không?

Không. Xem ở trên.

+0

"Giải thích rõ ràng nhất là ổ cắm vẫn chưa được đóng." - chương trình khách hàng không còn chạy; quá trình Java đã bị giết. Sử dụng netstat tôi có thể thấy rằng socket thực sự bị đóng, trên cả hai đầu, nhưng readLine() vẫn bị treo. Tôi sẽ thêm thông tin này ở trên. –

+0

Bạn có thể cung cấp SSCCE cho điều này không? http://sscce.org Những gì bạn đang báo cáo sẽ phản tác dụng của tôi ... –

+0

Xem mã * đang hoạt động * bên dưới. Cảm ơn sự giúp đỡ, xin lỗi vì đã lãng phí thời gian của bạn. –

0

Theo Javadoc, phương pháp readLine() sẽ ném một IOException. Và theo IOException's Javadoc, ngoại lệ dự kiến ​​sẽ được ném vào một ngắt. Nếu không có mã viết, tôi sẽ nói rằng bạn có thể xử lý trường hợp của luồng đóng sớm bằng cách bắt IOException.

+0

Vấn đề là, mặc dù, gián đoạn không bao giờ được ném. –

1

Bạn không ghét nó khi bạn bỏ ra hàng giờ để tìm ra một vấn đề không có ở đó? Trong nỗ lực của tôi để cung cấp SSCCE cho Stephen C tôi thấy rằng tôi không thể cung cấp một ví dụ như vậy bởi vì nó hoạt động. debug code của tôi đã tạo ra vấn đề ...

Đối với hồ sơ, đây là mã làm việc (trong trường hợp bất cứ ai khác có vấn đề tương tự):

sscceServer.java

import java.io.BufferedReader; 
import java.io.InputStreamReader; 
import java.io.PrintWriter; 
import java.net.InetAddress; 
import java.net.ServerSocket; 
import java.net.Socket; 


public class sscceServer 
{ 
    public static class EchoHandlerThread implements Runnable 
    { 
     private final Socket sock; 
     private final int id; 

     public EchoHandlerThread(Socket s, int i) 
     { 
      sock = s; 
      id = i; 
     } 

     public void run() 
     { 
      try { 
       //----- set socket read timeout to 10 seconds ----- 
       sock.setSoTimeout(10000); 

       PrintWriter out = new PrintWriter(sock.getOutputStream(), true); 
       BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream())); 

       String input; 
       while ((input = in.readLine()) != null) { 
        System.out.println("[" + id + "] <" + input + ">"); 
       } 

       out.close(); 
       in.close(); 
       sock.close(); 

       System.out.println("\tConnection #" + id + " closed"); 
      } 
      catch (Exception ex) { 
       throw new RuntimeException(ex); 
      } 
     } 
    } 

    public static class Listener 
    { 
     private final InetAddress bindip; 
     private final int portnum; 

     public Listener(String ipstr, int pn) 
     { 
      try { 
       bindip = InetAddress.getByName(ipstr); 
       portnum = pn; 
      } 
      catch (Exception ex) { 
       throw new RuntimeException(ex); 
      } 
     } 

     public void start() 
     { 
      try { 
       ServerSocket srvsock = new ServerSocket(portnum, 0, bindip); 
       System.out.println("Listening on " + bindip.getHostAddress() + ":" + portnum); 

       int connum = 0; 
       while (true) { 
        Socket sock = srvsock.accept(); 

        connum++; 
        InetAddress ipaddr = sock.getInetAddress(); 
        System.out.println("Connection #" + connum + " from: " + ipaddr.getHostAddress()); 

        new Thread(new EchoHandlerThread(sock, connum)).start(); 
       } 
      } 
      catch (Exception ex) { 
       throw new RuntimeException(ex); 
      } 
     } 
    } 


    public static void main(String[] args) 
    { 
     Listener lsnr = new Listener("localhost", 8988); 
     lsnr.start(); 
    } 
} 

sscceClient.java

import java.io.BufferedReader; 
import java.io.InputStreamReader; 
import java.io.PrintWriter; 
import java.net.Socket; 

public final class sscceClient 
{ 
    public static void main(String[] args) 
    { 
     try { 
      Socket sock = new Socket("localhost", 8909); 

      PrintWriter out = new PrintWriter(sock.getOutputStream(), true); 
      BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream())); 

      out.println("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); 
      out.println("Ut sem ante, mollis ut porttitor at, aliquet vel nulla."); 
      out.println("Suspendisse quis sapien ante, a cursus nunc."); 

      //---- sleep for 20 seconds ----- 
      Thread.sleep(20000); 

      out.close(); 
      in.close(); 
      sock.close(); 
     } 
     catch (Exception ex) { 
      throw new RuntimeException(ex); 
     } 
    } 
} 

Cảm ơn sự giúp đỡ!

Các vấn đề liên quan