2014-10-09 45 views
2

我正在为计算机科学课程制作客户端/服务器程序。HashMap InputStream/Scanner服务器问题

这个想法是,我们有一个服务器程序,它接受控制台命令列表,执行它们,并通过输入/输出流返回响应。由于命令的格式化,我必须检查空格和数字,并相应地拆分字符串(我已经完成)。问题似乎是从InputStream中检索命令。

命令应该被接受:

放[字符串] [INT] - 这应该存储在HashMap中的字符串(键)和int(值)

得到[字符串] - 这应该返回与此字符串

键集 相关的INT - 返回所有键

值 - 返回所有值

映射 - 返回所有映射

再见 - 退出客户端

帮助 - 没有做任何事情,但会列出所有的命令及其语法

教授给我们提供了服务器的大部分代码,但我认为它可能存在错误,因为我一直在扫描仪上收到异常。看到下面的服务器代码:

package mapserver; 

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.InputStreamReader; 
import java.io.OutputStream; 
import java.io.PrintWriter; 
import java.net.ServerSocket; 
import java.net.Socket; 
import java.util.Arrays; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
import java.util.Scanner; 

public class MapServer 
{ 

    public static void main(String[] args) throws IOException 
    { 
     ServerSocket serverSocket = new ServerSocket(50000); 
     while (true) 
     { 
      Socket activeSock = serverSocket.accept(); 
      Thread th = new Thread(new MapServerThread(activeSock)); 
      th.start(); 
     } 
    }  
} 

class MapServerThread implements Runnable 
{ 
    private Map<String, Integer> map = new HashMap<>(); 
    private Socket sock; 
    public MapServerThread(Socket s) 
    { 
     sock = s; 

     List<String> strs = Arrays.asList("aaa a", "b", "a"); 
     for (String str : strs) 
     { 
      map.put(str, str.length()); 
     } 
    }  

    @Override 
    public void run() 
    { 
     try 
     { 
      InputStream in = sock.getInputStream(); 
      OutputStream out = sock.getOutputStream(); 
      BufferedReader reader = 
        new BufferedReader(new InputStreamReader(in)); 
      PrintWriter writer = new PrintWriter(out, true); 

      // welcome message 
      writer.println("Welcome to the map service."); 

      String inputLine = null; 
      while ((inputLine = reader.readLine()) != null) 
      { 
       Scanner sc = new Scanner(inputLine); 
       String fullLine = 
         sc.nextLine().toLowerCase().trim().replaceAll("\\s+", " "); 
       writer.println(fullLine); 

       int cmdLoc = 0; 
       for (int k = 0; k <fullLine.length(); k++) 
       { 
        if (fullLine.charAt(k)==' '); 
        { 
         cmdLoc = k; 
        } 
       } 

       String cmd; 
       if (cmdLoc == 0) 
       { 
        cmd = fullLine; 
        writer.println(cmd); 
       } 
       else 
       { 
        cmd = fullLine.substring(0, cmdLoc+1); 
        writer.println(cmd); 
       } 

       int startloc = cmd.length() + 1; 
       switch(cmd) 
       { 
        case "put": 
         int intlocation = startloc; 
         for (int k = 0; k < fullLine.length(); k++) 
         { 
          if (Character.isDigit(fullLine.charAt(k))) 
          { 
           intlocation = k; 
          } 
         } 

         // if the int is located at the beginning, the format 
         // is wrong. Let the user know 
         if (intlocation == startloc) 
         { 
          writer.println("Invalid entry. Correct format " 
            + "is \"put <string> <integer>\""); 
         } 

         // Split the user's entry for putting 
         else 
         { 
          String stringToPut = 
            fullLine.substring(startloc, intlocation+1); 
          int intToPut = 
            Integer.parseInt(fullLine.substring(intlocation)); 
          map.put(stringToPut, intToPut); 
          writer.println("Ok!"); 
         } 

         continue; 

        case "get": 
         int returnvalue = 
           map.get(fullLine.substring(startloc)); 
         writer.println(returnvalue); 
         continue; 

        case "keyset": 
         String result = map.keySet().toString(); 
         writer.println(result); 
         continue; 

        case "values" : 
         String result1 = map.values().toString(); 
         writer.println(result1); 
         continue; 

        case "mappings" : 
         writer.println(map.size()); 
         map.forEach(
            (k, v) -> 
             { writer.println(k + " " + v);} 
            );       
         continue; 

        case "bye" : 
         writer.println("See you later."); 
         sock.shutdownOutput(); 
         sock.close(); 
         return; 

        case "help" : 
         continue; 
        default : 
         writer.println("Not a recognized command"); 

       } 
      } 
     } catch (IOException ex) 
     { 
      throw new RuntimeException(ex); 
     } 

    }  
} 

我几乎100%确定问题是在服务器程序,因为我一直在用Telnet进行测试。我试过直接使用BufferedReader代替扫描器,但服务器似乎变得空白的字符串。有没有人有任何想法?我已经摆弄了几个小时,现在我无法弄清楚。

果壳中的问题:

后,我登录时,服务器抛出:

Exception in thread "Thread-0" java.util.NoSuchElementException: No line found 
    at java.util.Scanner.nextLine(Scanner.java:1540) 
    at mapserver.MapServerThread.run(MapServer.java:67) 
    at java.lang.Thread.run(Thread.java:745) 

,我想不通为什么。如果我不使用扫描仪,出于某种原因服务器正在接收空白输入,无论我输入什么内容。

这里是正确的互动应该是什么样子:

Welcome to the MapService Client 
Enter the IP address of the server: localhost 
Please wait while I connect you... 
Welcome to the map service. 
Map service>mappings 
3 
a 1 
b 1 
aaa a 5 
Map service>put North Central College 2014 
Ok. 
Map service>keyset 
[a, b, aaa a, North Central College] 
Map service>get North Central  College 
2014 
Map service>help 
7 
help 
get key 
put key value 
values 
keyset 
mappings 
bye 
Map service>values 
[1, 1, 5, 2014] 
Map service>bye 
See you later. 
+0

你还没有真正告诉我们是什么问题 – ControlAltDel 2014-10-09 18:28:01

+0

我编辑它添加一些更 – aufty 2014-10-09 18:38:56

+0

我固定字符串错误。感谢您指出了这一点。在第一条命令后,我仍然遇到异常。 – aufty 2014-10-09 18:43:40

回答

1

您的代码被打破,因为它试图解析两次在同一行:

String inputLine = null; 
while ((inputLine = reader.readLine()) != null) //#1 

//... 

String fullLine =sc.nextLine().toLowerCase().trim().replaceAll("\\s+", " ");//#2 

您可以修复与特定部分:

String fullLine =inputLine.toLowerCase().trim().replaceAll("\\s+", " "); 

如果您收到一个空白的inputLine,无论出于何种原因,您可以跳过它w第i:

if(inputLine.trim().size()==0){ 
    continue;//invokes the next loop iteration 
} 

编辑:

我重写了类,并试图分裂的部分,使其更容易把握。请给一个反馈,即使您将其标示解决:

class MapServerThread implements Runnable { 

    private enum Commands { 
     PUT("(put)\\s(\\S+)\\s(\\d)"), 
     //add all your commands here and give an approriate regular expression 
     UNKNOWN(".+"); 

     private final String pattern; 

     Commands(String regexPattern) { 
      pattern = regexPattern; 
     } 

     private static Commands parseCommand(String s) { 
      Commands result = UNKNOWN; 

      s = s.toLowerCase(Locale.getDefault()); 

      for (Commands command : values()) { 
       if (command != UNKNOWN && command.pattern.matches(s)) { 
        result = command; 
        break; 
       } 
      } 
      return result; 
     } 
    } 

    private Map<String, Integer> map = new HashMap<>(); 
    private Socket sock; 

    public MapServerThread(Socket s) { 
     sock = s; 

     List<String> strs = Arrays.asList("aaa a", "b", "a"); 
     for (String str : strs) { 
      map.put(str, str.length()); 
     } 
    } 

    @Override 
    public void run() { 
     try { 
      BufferedReader reader = new BufferedReader(new InputStreamReader(sock.getInputStream())); 

      PrintWriter writer = new PrintWriter(sock.getOutputStream(), true); 

      writer.println("Welcome to the map service."); 

      String inputLine = null; 
      while ((inputLine = reader.readLine().trim()) != null) { 
       Commands command = Commands.parseCommand(inputLine); 

       writer.println(command.name()); 

       execute(command, inputLine); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    private void execute(Commands command, String inputLine) { 

     switch (command) { 
      case PUT: 
       String[] args = inputLine.split(" "); 
       map.put(args[1], Integer.parseInt(args[2])); 
       break; 
      //handle the other commands accordingly 
      default: 
       // notify about an error 
       break; 

     // 
     // get [string] - this should return the int associated with this string 
     // 
     // keyset - return all keys 
     // 
     // values - return all values 
     // 
     // mappings - return all mappings 
     // 
     // bye - quit the client 
     // 
     // help - doesn't do anything yet, but will list all commands and their 
     // syntax 

     } 
    } 

} 
+0

这消除了错误,但现在它在完成输入的命令后发送空白命令,导致返回的默认开关情况。 – aufty 2014-10-09 18:55:48

+0

是的,我一直在阅读你的代码。 Iam“着迷”写在那里的东西。给我一点时间。 – 2014-10-09 19:00:17

+0

看起来有更多的问题比我意识到的。由于某种原因,我的循环找到第一个空白始终返回1,导致cmd始终为3个字符长 – aufty 2014-10-09 19:13:29