Làm cách nào tôi có thể làm gián đoạn phương thức ServerSocket accept ()?


143

Trong luồng chính của tôi, tôi có một while(listening)vòng lặp gọi accept()đối tượng ServerSocket của tôi, sau đó bắt đầu một luồng máy khách mới và thêm nó vào Bộ sưu tập khi một máy khách mới được chấp nhận.

Tôi cũng có một luồng Quản trị viên mà tôi muốn sử dụng để đưa ra các lệnh, như 'thoát', điều này sẽ khiến tất cả các luồng máy khách bị tắt, tự tắt và tắt luồng chính, bằng cách chuyển sang nghe sai.

Tuy nhiên, accept()cuộc gọi trong các while(listening)khối vòng lặp và dường như không có cách nào để ngắt nó, vì vậy điều kiện while không thể được kiểm tra lại và chương trình không thể thoát!

Có cách nào tốt hơn để làm điều này? Hoặc một số cách để làm gián đoạn phương pháp chặn?



Đối với pepole, người muốn đợi x thời gian và sau đó phản ứng, sử dụng setSoTimeout ().
Abdullah Orabi

Câu trả lời:


151

Bạn có thể gọi close()từ một chủ đề khác, và accept()cuộc gọi sẽ ném a SocketException.


2
Cảm ơn, quá rõ ràng, thậm chí không xảy ra với tôi! Tôi đã gọi close () sau khi tôi thoát khỏi vòng lặp.
lukeo05

4
Thật kỳ lạ, không có thông tin này trong tài liệu: download.oracle.com/javase/6/docs/api/java/net/ trên phương thức không được đánh dấu là ném SocketException. Nó chỉ được đề cập ở đây download.oracle.com/javase/1.4.2/docs/api/java/net/
mẹo

1
Gọi close()tôi có ổn không , ý tôi là nó sẽ ném ngoại lệ khi thực hiện nó, vậy có cách nào khác (không ném ngoại lệ, cũng không dựa trên thời gian chờ) để ngừng lắng nghe yêu cầu?
Kushal

3
Và phải làm gì nếu kết nối đã được chấp nhận và luồng đang chờ một số dữ liệu: while ((s = in.readLine ())! = Null)?
Alex Fedulov

1
@AlexFedulov Tắt ổ cắm cho đầu vào. readLine()sau đó sẽ trả về null và các hoạt động đóng bình thường mà luồng nên có tại chỗ sẽ xảy ra.
Hầu tước Lorne

31

Đặt thời gian chờ bật accept(), sau đó cuộc gọi sẽ hết thời gian chặn sau thời gian được chỉ định:

http://docs.oracle.com/javase/7/docs/api/java/net/SocketOptions.html#SO_TIMEOUT

Đặt thời gian chờ cho các Sockethoạt động chặn :

ServerSocket.accept();
SocketInputStream.read();
DatagramSocket.receive();

Tùy chọn phải được đặt trước khi vào thao tác chặn để có hiệu lực. Nếu hết thời gian và hoạt động sẽ tiếp tục bị chặn, java.io.InterruptedIOExceptionđược nâng lên. Không Socketđược đóng trong trường hợp này.



4

Bạn chỉ có thể tạo ổ cắm "void" cho break serverocket.accept ()

Phía máy chủ

private static final byte END_WAITING = 66;
private static final byte CONNECT_REQUEST = 1;

while (true) {
      Socket clientSock = serverSocket.accept();
      int code = clientSock.getInputStream().read();
      if (code == END_WAITING
           /*&& clientSock.getInetAddress().getHostAddress().equals(myIp)*/) {
             // End waiting clients code detected
             break;
       } else if (code == CONNECT_REQUEST) { // other action
           // ...
       }
  }

Phương pháp phá vỡ chu kỳ máy chủ

void acceptClients() {
     try {
          Socket s = new Socket(myIp, PORT);
          s.getOutputStream().write(END_WAITING);
          s.getOutputStream().flush();
          s.close();
     } catch (IOException e) {
     }
}

4

Lý do ServerSocket.close()ném một ngoại lệ là vì bạn có outputstreamhoặc inputstream gắn vào ổ cắm đó. Bạn có thể tránh ngoại lệ này một cách an toàn bằng cách trước tiên đóng luồng đầu vào và đầu ra. Sau đó thử đóng ServerSocket. Đây là một ví dụ:

void closeServer() throws IOException {
  try {
    if (outputstream != null)
      outputstream.close();
    if (inputstream != null)
      inputstream.close();
  } catch (IOException e1) {
    e1.printStackTrace();
  }
  if (!serversock.isClosed())
    serversock.close();
  }
}

Bạn có thể gọi phương thức này để đóng bất kỳ ổ cắm từ bất cứ đâu mà không có ngoại lệ.


7
Các luồng đầu vào và đầu ra không được gắn vào ServerSocketnhưng với a Socketvà chúng ta đang nói về việc đóng ServerSocketkhông phải Socket, vì vậy ServerSocketcó thể đóng mà không đóng Socketcác luồng.
icza


1

OK, tôi đã làm việc này theo cách giải quyết câu hỏi của OP trực tiếp hơn.

Tiếp tục đọc câu trả lời ngắn cho một ví dụ Chủ đề về cách tôi sử dụng này.

Câu trả lời ngắn:

ServerSocket myServer;
Socket clientSocket;

  try {    
      myServer = new ServerSocket(port)
      myServer.setSoTimeout(2000); 
      //YOU MUST DO THIS ANYTIME TO ASSIGN new ServerSocket() to myServer‼!
      clientSocket = myServer.accept();
      //In this case, after 2 seconds the below interruption will be thrown
  }

  catch (java.io.InterruptedIOException e) {
      /*  This is where you handle the timeout. THIS WILL NOT stop
      the running of your code unless you issue a break; so you
      can do whatever you need to do here to handle whatever you
      want to happen when the timeout occurs.
      */
}

Ví dụ thế giới thực:

Trong ví dụ này, tôi có một ServerSocket đang chờ kết nối bên trong một Thread. Khi tôi đóng ứng dụng, tôi muốn tắt luồng (cụ thể hơn là ổ cắm) một cách sạch sẽ trước khi tôi đóng ứng dụng, vì vậy tôi sử dụng .setSoTimeout () trên ServerSocket sau đó tôi sử dụng ngắt được ném sau khi hết thời gian để kiểm tra và xem phụ huynh có đang cố gắng tắt chuỗi không. Nếu vậy, sau đó tôi đặt đóng ổ cắm, sau đó đặt cờ cho biết luồng đã hoàn thành, sau đó tôi thoát ra khỏi vòng lặp Chủ đề trả về giá trị null.

package MyServer;

import javafx.concurrent.Task;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

import javafx.concurrent.Task;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

public class Server {

public Server (int port) {this.port = port;}

private boolean      threadDone        = false;
private boolean      threadInterrupted = false;
private boolean      threadRunning     = false;
private ServerSocket myServer          = null;
private Socket       clientSocket      = null;
private Thread       serverThread      = null;;
private int          port;
private static final int SO_TIMEOUT    = 5000; //5 seconds

public void startServer() {
    if (!threadRunning) {
        serverThread = new Thread(thisServerTask);
        serverThread.setDaemon(true);
        serverThread.start();
    }
}

public void stopServer() {
    if (threadRunning) {
        threadInterrupted = true;
        while (!threadDone) {
            //We are just waiting for the timeout to exception happen
        }
        if (threadDone) {threadRunning = false;}
    }
}

public boolean isRunning() {return threadRunning;}


private Task<Void> thisServerTask = new Task <Void>() {
    @Override public Void call() throws InterruptedException {

        threadRunning = true;
        try {
            myServer = new ServerSocket(port);
            myServer.setSoTimeout(SO_TIMEOUT);
            clientSocket = new Socket();
        } catch (IOException e) {
            e.printStackTrace();
        }
        while(true) {
            try {
                clientSocket = myServer.accept();
            }
            catch (java.io.InterruptedIOException e) {
                if (threadInterrupted) {
                    try { clientSocket.close(); } //This is the clean exit I'm after.
                    catch (IOException e1) { e1.printStackTrace(); }
                    threadDone = true;
                    break;
                }
            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
};

}

Sau đó, trong lớp Trình điều khiển của tôi ... (Tôi sẽ chỉ hiển thị mã có liên quan, xoa bóp nó vào mã của riêng bạn khi cần)

public class Controller {

    Server server = null;
    private static final int port = 10000;

    private void stopTheServer() {
        server.stopServer();
        while (server.isRunning() {
        //We just wait for the server service to stop.
        }
    }

    @FXML private void initialize() {
        Platform.runLater(()-> {
            server = new Server(port);
            server.startServer();
            Stage stage = (Stage) serverStatusLabel.getScene().getWindow();
            stage.setOnCloseRequest(event->stopTheServer());
        });
    }

}

Tôi hy vọng điều này sẽ giúp ai đó xuống đường.


0

Một điều khác bạn có thể thử để làm sạch hơn, đó là kiểm tra cờ trong vòng chấp nhận và sau đó khi luồng quản trị viên của bạn muốn tắt chuỗi chặn trên chấp nhận, đặt cờ (làm cho luồng an toàn) và sau đó tạo một ổ cắm máy khách kết nối với ổ cắm nghe. Việc chấp nhận sẽ dừng chặn và trả lại ổ cắm mới. Bạn có thể tìm ra một số điều giao thức đơn giản nói cho luồng nghe thoát ra khỏi luồng một cách sạch sẽ. Và sau đó đóng ổ cắm ở phía khách hàng. Không có ngoại lệ, sạch sẽ hơn nhiều.


điều này vô nghĩa ... khi bạn có mã như socket này = serverSocket.accept (); tại thời điểm đó, việc chặn bắt đầu và vòng lặp không phải là một phần của mã của chúng tôi, vì vậy có cách để mã chặn đó tìm kiếm một lá cờ mà tôi đặt ... ít nhất là tôi không thể tìm ra cách để làm điều này. .. nếu bạn có mã làm việc, xin vui lòng chia sẻ?
Michael Sims

Vâng, có vẻ như tôi đã bỏ qua một chút thông tin quan trọng sẽ khiến nó không có nhiều ý nghĩa. Bạn cần làm cho ổ cắm chấp nhận không bị chặn và chỉ chặn trong một khoảng thời gian trước khi vòng lặp được lặp lại, trong đó khu vực bạn sẽ kiểm tra cờ thoát.
stu

Làm thế nào, chính xác, bạn làm cho ổ cắm chấp nhận, không chặn?
Michael Sims

dài trên = 1L; if (ioctl (socket, (int) FIONBIO, (char *) & on))
stu

@stu Câu hỏi này dành cho Ổ cắm của Java.
Kröw
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.