2015-10-12 135 views
0

我試圖用我一直在努力的客戶端/服務器程序實現多線程。我需要允許多個客戶端同時連接到服務器。我目前有4個類:一個客戶端,一個服務器,一個協議和一個工作者來處理線程。下面的代碼是什麼,我有這些類:多線程與客戶端服務器程序

的SocketServer類:

public class SocketServer { 


    public static void main(String[] args) throws IOException { 

     int portNumber = 9987; 

     try ( 
      ServerSocket serverSocket = new ServerSocket(portNumber); 
      Socket clientSocket = serverSocket.accept(); 
      PrintWriter out = 
       new PrintWriter(clientSocket.getOutputStream(), true); 
      BufferedReader in = new BufferedReader(
       new InputStreamReader(clientSocket.getInputStream())); 

     ) { 
      Thread thread = new Thread(new ClientWorker(clientSocket)); 
      thread.start(); //start thread 

      String inputLine, outputLine; 

      // Initiate conversation with client 
      Protocol prot = new Protocol(); 
      outputLine = prot.processInput(null); 
      out.println(outputLine); 

      while ((inputLine = in.readLine()) != null) { 
       outputLine = prot.processInput(inputLine); 
       out.println(outputLine); 
       if (outputLine.equals("quit")) 
        break; 
      } 
     } catch (IOException e) { 
      System.out.println("Exception caught when trying to listen on port " 
       + portNumber + " or listening for a connection"); 
      System.out.println(e.getMessage()); 
     } 
    } 
} 

SocketClient類:

public class SocketClient { 
    public static void main(String[] args) throws IOException 
    { 

     String hostName = "localhost"; 
     int portNumber = 9987; 

     try (
      Socket socket = new Socket(hostName, portNumber); 
      PrintWriter out = new PrintWriter(socket.getOutputStream(), true); 
      BufferedReader in = new BufferedReader(
       new InputStreamReader(socket.getInputStream())); 
     ) { 
      BufferedReader stdIn = 
      new BufferedReader(new InputStreamReader(System.in)); 
      String fromServer; 
      String fromUser; 

      while ((fromServer = in.readLine()) != null) { 
       System.out.println("Server: " + fromServer); 
       if (fromServer.equals("quit")) 
        break; 

       fromUser = stdIn.readLine(); 
       if (fromUser != null) { 
        System.out.println("Client: " + fromUser); 
        out.println(fromUser); 
       } 
      } 
     } catch (UnknownHostException e) { 
      System.err.println("Don't know about host " + hostName); 
      System.exit(1); 
     } catch (IOException e) { 
      System.err.println("Couldn't get I/O for the connection to " + 
       hostName); 
      System.exit(1); 
     } 
    } 
} 

協議類:

public class Protocol { 

    private static final int waiting   = 0; 
    private static final int sentPrompt  = 1; 


    private int status = waiting; 

    public String processInput(String theInput) { 
     String theOutput = null; 

     if (status == waiting) { 
      theOutput = "Please enter what you would like to retrieve: 'customer' or 'product' "; 
      status = sentPrompt; 
     } 
     else if (status == sentPrompt) { 
      if (theInput.equalsIgnoreCase("product")) { 
       File f = new File("product.txt"); 
       Scanner sc = null; 
       try { 
        sc = new Scanner(f); 
       } catch (FileNotFoundException ex) { 
        Logger.getLogger(Protocol.class.getName()).log(Level.SEVERE, null, ex); 
       } 

       while (sc.hasNextLine() ) { 
        String line = sc.nextLine(); 
        theOutput = "The current product entries are : " + line; 
       } 
       return theOutput; 
      } 
      else if (theInput.equalsIgnoreCase("customer")) { 
       File f = new File("customer.txt"); 
       Scanner sc = null; 
       try { 
        sc = new Scanner(f); 
       } catch (FileNotFoundException ex) { 
        Logger.getLogger(Protocol.class.getName()).log(Level.SEVERE, null, ex); 
       } 

       while (sc.hasNextLine() ) { 
        String line = sc.nextLine(); 
        theOutput = "The current customer entries are : " + line; 
       } 
       return theOutput; 

      } 
      else if (theInput.equalsIgnoreCase("quit")) { 
       return "quit"; 
      } 
      else { 
       return "quit"; 
      } 
     } 
     return theOutput; 
    } 
} 

的ClientWorker類:​​

public class ClientWorker implements Runnable { 
    private final Socket client; 

    public ClientWorker(Socket client) { 
     this.client = client; 
    } 

    @Override 
    public void run() { 
     String line; 
     BufferedReader in = null; 
     PrintWriter out = null; 
     try { 
      System.out.println("Thread started with name:"+Thread.currentThread().getName()); 
      in = new BufferedReader(new InputStreamReader(client.getInputStream())); 
      out = new PrintWriter(client.getOutputStream(), true); 
     } catch (IOException e) { 
      System.out.println("in or out failed"); 
      System.exit(-1); 
     } 

     while (true) { 
      try { 
       System.out.println("Thread running with name:"+Thread.currentThread().getName()); 
       line = in.readLine(); 
       //Send data back to client 
       out.println(line); 
       //Append data to text area 
      } catch (IOException e) { 
       System.out.println("Read failed"); 
       System.exit(-1); 
      } 
     } 
    }  
} 

當我運行服務器和客戶端時,一切正常,如預期。然後,當我嘗試運行另一個客戶端時,它只是掛在那裏,並沒有提示客戶給出迴應。任何洞察到我所缺少的是非常感謝!

+0

確切部位在哪裏你的程序掛起?我會猜測在ServerSocket的while循環中。 – hotzst

+0

@hotzst:是的。我願意使用其他方式,我也一直在尋找線程池。但我需要在每個線程中使用協議。 – perlsufi

+2

關於代碼質量的備註:此代碼幾乎非常「不乾淨」。我全心全意地推薦從Robert Martin開始「Clean code」,以瞭解您可以通過多少種方式來改進源代碼。 – GhostCat

回答

4

您的服務器代碼應該實現以下功能。

  1. 繼續接受來自ServerSocket插座在while循環

  2. 創建新的線程後接受()調用通過傳遞客戶端套接字即Socket

  3. 在客戶端套接字線程如ClientWorker

    待辦事項IO處理你的情況。

看一看這個article

你的代碼應該是

ServerSocket serverSocket = new ServerSocket(portNumber); 
while(true){ 
    try{ 
    Socket clientSocket = serverSocket.accept(); 
    Thread thread = new ClientWorker(clientSocket); 
    thread.start(); //start thread 
    }catch(Exception err){ 
    err.printStackTrace(); 
    } 
} 
+0

謝謝!我是否爲協議procesInput保留我的try/catch,或者將其移至ClientWorker run()方法? – perlsufi

+0

try {} catch {}中的ClientWorker應該沒問題 –

2

serverSocket.accept()被調用了多少次? 一次。 這就是它將處理的客戶數量。 試圖聯繫的後續客戶端不會有任何人傾聽接收它們。

要處理更多客戶端,您需要在循環中調用serverSocket.accept()