Zdaję sobie bardzo dziwny problem z klasą Scanner
. Używam Scanner
do czytania wiadomości od Socket
ze specjalnym tokenem EOF. Wszystko działa poprawnie, jeśli klient zapisuje wszystkie żądania naraz lub żądania mają dane, ale operacja blokowania zatrzymuje się na serwerze, a z kolei na kliencie, gdy wiadomości są zapisywane w porcjach, a następny token powinien być pustym ciągiem .java.util.Scanner zawiesza się na hasNext()
Co by to spowodowało? Jak mogę tego uniknąć?
Oto uproszczona wersja tego, co próbuję zrobić, \n
jest używany do celów testowych, załóżmy, że ogranicznik może być dowolnym ciągiem znaków.
Kod serwera:
ServerSocketChannel serverChannel = null;
try {
serverChannel = ServerSocketChannel.open();
ServerSocket serverSocket = serverChannel.socket();
serverSocket.bind(new InetSocketAddress(9081));
SocketChannel channel = serverChannel.accept();
Socket socket = channel.socket();
InputStream is = socket.getInputStream();
Reader reader = new InputStreamReader(is);
Scanner scanner = new Scanner(reader);
scanner.useDelimiter("\n");
OutputStream os = socket.getOutputStream();
Writer writer = new OutputStreamWriter(os);
while (scanner.hasNext()) {
String msg = scanner.next();
writer.write(msg);
writer.write('\n');
writer.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (serverChannel != null) {
try {
serverChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client robocza:
Socket socket = new Socket();
try {
socket.connect(new InetSocketAddress("localhost", 9081));
InputStream is = socket.getInputStream();
Reader reader = new InputStreamReader(is);
Scanner scanner = new Scanner(reader);
scanner.useDelimiter("\n");
OutputStream os = socket.getOutputStream();
Writer writer = new OutputStreamWriter(os);
writer.write("foo\n\nbar\n");
writer.flush();
System.out.println(scanner.next());
System.out.println(scanner.next());
System.out.println(scanner.next());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
schowek Klient:
Socket socket = new Socket();
try {
socket.connect(new InetSocketAddress("localhost", 9081));
InputStream is = socket.getInputStream();
Reader reader = new InputStreamReader(is);
Scanner scanner = new Scanner(reader);
scanner.useDelimiter("\n");
OutputStream os = socket.getOutputStream();
Writer writer = new OutputStreamWriter(os);
writer.write("foo\n");
writer.flush();
System.out.println(scanner.next());
writer.write("\n");
writer.flush();
System.out.println(scanner.next());
writer.write("bar\n");
writer.flush();
System.out.println(scanner.next());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Mam zamykania kanału wejściowego gniazda mimo wszystko został odczytany, to domyślnie zamyka gniazdo podstawowy (s) , niezależnie od tego, czy program się zawiesza na długo przed zamknięciem gniazda. Token jest używany jako ogranicznik wiadomości, więc wiele wiadomości może zostać wysłanych podczas pojedynczej sesji, nie chodzi tu o wykrycie końca strumienia.Jestem świadomy, że koniec strumienia spowodowałby powrót funkcji hasNext(). –
Należy pamiętać, że jest to tylko przykład do odtworzenia problemu, kod produkcyjny faktycznie wykorzystuje niezablokowany kanał gniazd, stale akceptuje połączenia i spawns wątki robocze do obsługi odczytu, zapisu i zamykania gniazd. –