Bir ServerSocket accept () yöntemini nasıl kesebilirim?


143

Ana iş parçacığımda ServerSocket nesnesimi while(listening)çağıran accept(), daha sonra yeni bir istemci iş parçacığı başlatan ve yeni bir istemci kabul edildiğinde bir koleksiyona ekleyen bir döngü var .

Ayrıca, 'off' gibi komutları vermek için kullanmak istediğim bir Yönetici iş parçacığım var, bu da tüm istemci iş parçacıklarının kapatılmasına, kendini kapatmasına ve ana iş parçacığının kapatılmasına neden olarak yanlış dinlemeye dönüşür.

Ancak, döngüdeki accept()çağrı while(listening)engeller ve kesmenin herhangi bir yolu yoktur, bu yüzden while koşulu tekrar kontrol edilemez ve program çıkamaz!

Bunu yapmanın daha iyi bir yolu var mı? Ya da engelleme yöntemini kesmenin bir yolu var mı?



X zamanı beklemek ve daha sonra tepki vermek isteyen pepole için setSoTimeout () kullanın.
Abdullah Orabi

Yanıtlar:


151

close()Başka bir mesaj dizisinden arama yapabilirsiniz ve accept()çağrı a atacaktır SocketException.


2
Teşekkürler, çok açık, bana bile olmadı! Ben döngü çıktıktan sonra close () çağırıyordu.
lukeo05

4
Garip, dokümanlarda bu bilgi bulunmuyor: download.oracle.com/javase/6/docs/api/java/net/… yöntemi SocketException özelliğinin atanması olarak işaretlenmiyor. Sadece burada bahsedilmektedir download.oracle.com/javase/1.4.2/docs/api/java/net/…
Vladislav Rastrusny

1
Aramak uygun mu close(), yani bunu yapmak için istisnayı atacak, yani istekleri dinlemeyi durdurmak için başka bir yol var (istisna atmıyor, zaman aşımına da dayanmıyor)?
Kushal

3
Ve bağlantı zaten kabul edilmişse ve iş parçacığı bazı veriler bekliyorsa ne yapmalı: while ((s = in.readLine ())! = Null)?
Alex Fedulov

1
@AlexFedulov Giriş için bu soketi kapatın. readLine()sonra null değerini döndürür ve iş parçacığının önceden olması gereken normal kapatma işlemleri gerçekleşir.
Lorne Marquis

31

Zaman aşımı ayarlandığında accept(), arama belirtilen süreden sonra engelleme zaman aşımına uğrar:

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

Engelleme Socketişlemlerinde zaman aşımı ayarlayın :

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

Bu seçeneğin yürürlüğe girmesi için bir engelleme işlemine girmeden önce ayarlanması gerekir. Zaman aşımı süresi dolarsa ve işlem engellenmeye devam ederse java.io.InterruptedIOExceptionyükseltilir. SocketBu durumda kapalı değil.



4

Sadece break serverocket.accept () için "void" soketi oluşturabilirsiniz

Sunucu tarafı

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
           // ...
       }
  }

Sunucu kırma döngüsü yöntemi

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

4

Sebep ServerSocket.close() bir istisnası var , o sokete bir outputstreamveya inputstreamekli olmanızdır . Öncelikle giriş ve çıkış akışlarını kapatarak bu istisnayı güvenli bir şekilde önleyebilirsiniz. Ardından ServerSocket. İşte bir örnek:

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();
  }
}

İstisnasız herhangi bir soketi herhangi bir yerden kapatmak için bu yöntemi çağırabilirsiniz.


7
Giriş ve çıkış akışları bağlı değildir ServerSocketancak etmek Socketve kapanış bahsediyoruz ServerSocketdeğil Socketbu yüzden, ServerSocketbir kapatmadan kapalı olabilir Socket'ın akışları.
icza


1

Tamam, bu çalışmayı OP'nin sorusunu daha doğrudan ele alacak şekilde aldım.

Bunu nasıl kullandığımın bir Thread örneği için kısa cevabı geçmeye devam edin.

Kısa cevap:

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.
      */
}

Gerçek dünya örneği:

Bu örnekte, bir iş parçacığı içinde bir bağlantı bekleyen bir ServerSocket var. Uygulamayı kapattığımda, uygulamanın kapanmasına izin vermeden önce iş parçacığını (daha spesifik olarak soket) kapatmak istiyorum, bu yüzden ServerSocket üzerinde .setSoTimeout () kullanıyorum ve sonra atılan kesmeyi kullanıyorum zaman aşımından sonra ebeveynin iş parçacığını kapatmaya çalışıp çalışmadığını kontrol edin. Eğer öyleyse, daha sonra soketi kapattım, sonra iş parçacığının yapıldığını gösteren bir bayrak ayarladım, sonra bir boş döndüren Threads döngüsünün dışına çıkıyorum.

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;
    }
};

}

Sonra, Controller sınıfımda ... (Sadece ilgili kodu göstereceğim, gerektiğinde kendi kodunuza masaj yapacağım)

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());
        });
    }

}

Umarım bu yoldan birine yardım eder.


0

Temizleyebileceğiniz başka bir şey, kabul döngüsünde bir bayrağı kontrol etmek ve ardından yönetici iş parçacığınız kabul üzerindeki iş parçacığı engellemesini öldürmek istediğinde, bayrağı ayarlayın (iş parçacığını güvenli hale getirin) ve sonra bir istemci soketi yapmaktır. dinleme soketine bağlantı. Kabul engellemeyi durdurur ve yeni soketi döndürür. Dinleyen iş parçacığından iş parçacığından temiz çıkmasını söyleyen bazı basit protokol şeyleri çalışabilirsiniz. Ve sonra istemci tarafındaki soketi kapatın. İstisna yok, çok daha temiz.


bu mantıklı değil ... bu soket gibi bir kodunuz olduğunda = serverSocket.accept (); o anda, engelleme başlar ve döngü kodumuzun bir parçası değildir, bu yüzden engelleme kodunun ayarladığım bir bayrağı aramasını sağlamanın bir yolu vardır ... en azından bunu yapmanın bir yolunu bulamadım. .. eğer çalışma kodunuz varsa, lütfen paylaşın?
Michael Sims

Evet, çok mantıklı olmayacak çok önemli bir bilgi bıraktım. Kabul soketini engellememeniz ve döngü tekrarlanmadan önce belirli bir süre bloke etmeniz gerekir; bu durumda çıkış bayrağını kontrol edersiniz.
stu

Tam olarak, kabul soketini engelleme yapmadan nasıl yaparsınız?
Michael Sims

uzun = 1L; if (ioctl (soket, (int) FIONBIO, (char *) ve üzeri))
stu

@stu Bu soru Java'nın Soketleri içindir.
Kröw
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.