2015-03-18 20 views
6

Scanner sınıfı ile çok garip bir sorunla karşılaşıyorum. Özel bir EOF belirteci ile Socket gelen iletileri okumak için Scanner kullanıyorum. İstemci tüm istekleri bir kerede yazıyorsa veya istekleri veri içeriyorsa her şey iyi çalışır, ancak engelleme hasNext() işlemi sunucuda askıya alınır ve sırayla istemci, iletiler parçalar halinde yazılır ve sonraki belirteç boş bir dize olmalıdır. .java.util.Scanner hasNext() üzerinde askıda kalıyor()

Buna ne sebep olur? Bundan kaçınmak için nasıl gidebilirim?

Yapmaya çalıştığım şeyin basitleştirilmiş bir sürümü, \n sınama amacıyla kullanılıyor, sınırlayıcının herhangi bir dize olabileceğini varsayalım.

Sunucu Kodu:

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

Çalışma Müşteri:

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

Asma Müşteri:

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

cevap

0

Sen kabul soket kapanış edilmez.

'Özel bir EOF belirteci' gerekmez. Akışın sonu belli değil.

+0

, bu örtük yatan soket (ler) kapatır Programın, soketin kapatılması gerekmeden çok uzun süre askıda kalmasına rağmen. Simge bir mesaj sınırlayıcı olarak kullanılır, böylece tek bir oturum sırasında birden fazla mesaj gönderilebilir, bu bir akışın sonunu tespit etmekle ilgili değildir.Akışın sonunun hasNext() işlevinin geri dönmesine neden olacağının farkındayım. –

+0

Lütfen bu sorunu yeniden üretmenin bir örneği olduğunu unutmayın, üretim kodu aslında engelleme olmayan soket kanalını kullanır, bağlantıları sürekli olarak kabul eder ve soketlerin okunması, yazılması ve kapatılması için çalışan iş parçacığı üretir. –

0

Kodu izlemek için biraz zaman harcadım ve sorun kesinlikle Scanner sınıfındaki bir kusurdur.

public boolean hasNext() { 
    ensureOpen(); 
    saveState(); 
    while (!sourceClosed) { 
     if (hasTokenInBuffer()) 
      return revertState(true); 
     readInput(); 
    } 
    boolean result = hasTokenInBuffer(); 
    return revertState(result); 
} 

hasNext() aramalar

private boolean hasTokenInBuffer() { 
    matchValid = false; 
    matcher.usePattern(delimPattern); 
    matcher.region(position, buf.limit()); 

    // Skip delims first 
    if (matcher.lookingAt()) 
     position = matcher.end(); 

    // If we are sitting at the end, no more tokens in buffer 
    if (position == buf.limit()) 
     return false; 

    return true; 
} 

hasTokenInBuffer()hasTokenInBuffer() hep javadoc açıklandığı gibi eğer varsa ilk sınırlayıcı atlar.

aşağıdaki() ve hasNext() yöntemleri ve (örneğin, nextInt() ve hasNextInt (as)) ilkel tip tamamlayıcı yöntemler birinci sınırlayıcı deseni ile eşleşen bir girdi atlama ve daha sonra bir sonraki geri girişimi jeton. Her ikisi deNext ve sonraki yöntemler daha fazla giriş için beklemeyi engelleyebilir. Bir hasNext yönteminin, ilişkili sonraki yöntemin engellenip engellenmeyeceği ile ilgili bir bağlantısının olup olmadığı.

Önce son isteğinden tampon hala belirteci atlamak, o zaman biz bu yüzden biz döngü sonra, bu durumda sadece \n yılında, readInput() diyoruz bizim tamponu içinde yeni veri yok fark Yine bizim sınırlayıcıyı atlayan hasTokenInBuffer()'a geri dönün!

Bu noktada Sunucu daha fazla girdi bekliyor ve Müşteri bir yanıt bekliyor. Kilitlenme.

son belirteci atlanır olmadığını kontrol, bu kolayca önlenebilir

... tüm giriş okunduktan sonra ben soket kanalını kapatıyorum

private boolean skippedLast = false; 

private boolean hasTokenInBuffer() { 
    matchValid = false; 
    matcher.usePattern(delimPattern); 
    matcher.region(position, buf.limit()); 

    // Skip delims first 
    if (!skippedLast && matcher.lookingAt()) { 
     skippedLast = true; 
     position = matcher.end(); 
    } else { 
     skippedLast = false; 
    } 

    // If we are sitting at the end, no more tokens in buffer 
    if (position == buf.limit()) 
     return false; 

    return true; 
} 
İlgili konular