2017-09-06 87 views
0

我創建了一個基於服務器的聊天程序,該程序有多個工作線程,每個線程處理套接字上的客戶端。它有一個服務器套接字將客戶端傳遞給工作線程。Java使用客戶端套接字的多個線程

public static void main(String[] args) { 
    ExecutorService executor = Executors.newFixedThreadPool(5); 
    ServerSocket serverSocket = null; 
    try { 
     serverSocket = new ServerSocket(4001); 
     System.out.println("Listening server"); 
     while (true) { 
      Socket socket = serverSocket.accept(); 
      System.out.println("Connected"); 
      Random rand= new Random(); 
      int port=4001+rand.nextInt(5); 
      Worker worker = new Worker(port); 
      executor.execute(worker); 
      System.out.println("Thread started"); 
      new PrintWriter(socket.getOutputStream(), true).println(port); 
      socket.close(); 
      System.out.println("Closed"); 
      // break; 
     } 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 

} 

public class Worker implements Runnable { 

private int port; 
public Worker(int i) { 
    port=i; 
} 
@Override 
public void run() { 
    worker(); 
} 
private static Socket socket; 
private static PrintWriter out; 
private static BufferedReader in; 

private void worker() { 
    try { 
     ServerSocket serverSocket = new ServerSocket(port); 
     socket = serverSocket.accept(); 
     out = new PrintWriter(socket.getOutputStream(), true); 
     in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
     String line; 
     while ((line = in.readLine()) != null) { 
      System.out.println("Received: " + line); 
      switch (line) { 
       case ("Button Press"): 
        System.out.println("Handled button"); 
        out.println("Button acknowledged"); 
        break; 
       case ("Give me some data"): 
        System.out.println("Handled data"); 
        out.println("Have some data"); 
        break; 
      } 
     } 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } finally { 
     try { 
      out.close(); 
      in.close(); 
      socket.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

    } 
} 

這工作得很好,然而,問題是當我有由客戶端的自動請求來檢查消息和用戶提供的相同類型的某些輸入。這會導致衝突,因爲實際方法需要運行幾秒鐘,並且如果接收到更多輸入,則請求將不會被處理,因爲它位於方法的中間。例如:

private static BufferedReader in; 
private static PrintWriter out; 
public static void main(String[] args) { 
    Main main=new Main(); 
    main.createWindow(); 
    try { 
     Socket init = new Socket(InetAddress.getLocalHost(), 4001); 
     int port 
      =Integer.parseInt 
       (new BufferedReader 
        (new InputStreamReader(init.getInputStream())).readLine()); 
     init.close(); 
     Socket socket=new Socket(InetAddress.getLocalHost(), port); 
     out = new PrintWriter(socket.getOutputStream(), true); 
     in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 


    while(true){ 
     try { 
      Thread.sleep(5000); 
      out.println("Give me some data"); 
      if(in.readLine().equals("Have some data")){ 
       System.out.println("Data recieved"); 
      }else{ 
       System.out.println("Data not recieved"); 
      } 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

    } 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 


private void createWindow(){ 
    JFrame frame =new JFrame("This is a button"); 
    Container pane=getContentPane(); 
    JButton button=new JButton("This is a button"); 
    button.addActionListener(this); 
    pane.add(button); 
    frame.setTitle("Messaging"); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.setContentPane(pane); 
    frame.setVisible(true); 
    frame.setSize(400, 350); 
} 

@Override 
public void actionPerformed(ActionEvent e) { 
    System.out.println("Button press"); 
    try { 
     out.println("Button Press"); 
     if(in.readLine().equals("Button acknowledged")){ 
      System.out.println("Button complete"); 
     }else{ 
      System.out.println("Button fail"); 
     } 
    } catch (IOException e1) { 
     e1.printStackTrace(); 
    } 
} 

如果按鈕被按下,而服務器獲取數據它衝突和錯誤的響應發送。那麼我會如何處理這個問題呢?如果我創建一個單獨的套接字來處理自動檢查,這是服務器必須處理的線程數量的兩倍。有更好的解決方案嗎? 請你可以嘗試解釋,因爲這對我來說是一個新的領域。

+0

你的第一個問題是這些變量都不應該是靜態的,而那些指向特定客戶端的變量應該在'Worker'類中,而不是服務器類中。 – EJP

+0

是的服務器工人類中的變量可能不應該是靜態的,但這是因爲這是我在5分鐘內扔在一起的東西,以提供我的問題的一個例子。 而引用特定客戶端的變量不在工作者類中,就我所見,它只是服務器套接字,最初的套接字和線程池。那麼哪些不是在那裏?但正如我已經說過的那樣,這只是一些快速拼湊在一起的代碼,試圖幫助解決我的問題,所以它不會是完美的。 – Nightfortress

回答

2

你的問題是你有兩個線程與套接字,你的主線程和Swing事件處理線程進行交互。這意味着兩個線程可以排隊兩個不同的東西,並且響應可能被錯誤的線程拾取。獲取想要的位置的最簡單方法是將所有套接字交互放在一個線程中。

有兩種方法可以做到這一點。一個是主線程將定期自動檢查排隊到Swing事件處理線程。然而,這有點複雜,也可能是錯誤的,因爲實際的Swing線程模型與記錄的不同。

另一種做法是讓Swing事件處理線程將按鈕排列到主線程。在這種情況下,您可以使用適當的同步將按鈕排隊到主線程。主線程的主循環將檢查按鈕按下並通過套接字發送並等待正確的響應。

+0

好吧,那麼如何將按鈕按到主線程?任何建議,以便我可以查找如何使用它們? @Warren露水 – Nightfortress

+0

Nevermind我找到了一個解決方案,https://stackoverflow.com/a/16268778/5521916在你的答案幫助下。 – Nightfortress