2014-09-02 140 views
1

好吧,爲了好玩我現在在Java中練習多線程和網絡,但我遇到了一些非常奇怪的事情。我有我的代碼結構如下。客戶端和服務器是線程,其中包含PacketListener,這些線程也是在接收到數據包時將數據包添加到ConcurrentLinkedQueue的線程。Java多線程,奇怪的東西

我的服務器類看起來像這樣

public Server(int port) { 
     setThreadName("ServerThread"); 
     this.clients = new ArrayList<SocketAddress>(); 
     try { 
      //this.socket = new DatagramSocket(null); 
      //this.socket.setReuseAddress(true); 
      //this.socket.bind(new InetSocketAddress(port)); 
      //this.socket.setReuseAddress(true); 
      //this.socket.bind(new InetSocketAddress(port)); 
      //sender = new PacketSender(socket); 
      this.listener = new PacketListener(port); 

     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     listener.start(); 
    } 

    synchronized private void processPacket(DatagramPacket packet) { 
      if (packet != null) { 
       String data = new String(packet.getData(), 0, packet.getLength()); 
       System.out.println("Received a packet " + data); 
       if (data.equalsIgnoreCase("connecting")) { 
        System.out.println("wut"); 
       } else { 
        System.out.println("Packet from :" + packet.getAddress().toString() + " saying: " + data); 
       } 
      } else { 
       System.out.println("Packet == null"); 
      } 
     } 



     @Override 
     public void run() { 
      System.out.println("running server on port " + socket.getLocalPort()); 
      while (isRunning()) { 
       if (listener.hasPacket()) { 
        System.out.println("listener has a packet"); 
        processPacket(listener.getNextPacket()); 
       } 
       sleep(1); // sleep for 1ms (keeps cpu usage from sky rocketing) 
      } 
     } 

我PacketListener類看起來像這樣

public PacketListener(int port) throws IOException { 
     this.socket = new DatagramSocket(null); 
     this.socket.setReuseAddress(true); 
     this.socket.bind(new InetSocketAddress(port)); 
     System.out.println("Packet listener bound @ " + socket.getLocalAddress() + " on port " + socket.getLocalPort()); 
     receivedPackets = new ConcurrentLinkedQueue<DatagramPacket>(); 
    } 

    synchronized private void addPacket(DatagramPacket packet) { 
     if (!receivedPackets.add(packet)) { 
      System.err.println("We dropped a packet because full queue"); 
     } else { 
      System.out.println("We added a received packet! - " + receivedPackets.size()); 
     } 
    } 

    synchronized public boolean hasPacket() { 
     return !receivedPackets.isEmpty(); 
    } 

    synchronized public DatagramPacket getNextPacket() { 
     return receivedPackets.poll(); 
    } 

    @Override 
    public void run() { 
     byte[] buffer = new byte[256]; 
     DatagramPacket inPacket = new DatagramPacket(buffer, buffer.length); 
     while (isRunning()) { 
      try { 
       socket.receive(inPacket); 
       addPacket(inPacket); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

現在奇怪的是,在客戶端,說我送了幾包。 比方說,我發送「你好」,然後在「測試1」,然後「測試2」 服務器將打印出

Packet received 
We added a received packet! - 1 
listener has a packet 

Packet received 
Received a packet test1 
We added a received packet! - 1 
Packet from :/127.0.0.1 saying: test1 
listener has a packet 


Packet received 
Received a packet test2 
We added a received packet! - 1 
Packet from :/127.0.0.1 saying: test2 
listener has a packet 

這實際上應該打印出來沿

We added a received packet! - 1 
listener has a packet 
Received a packet hello 
Packet from :/127.0.0.1 saying: hello 

We added a received packet! - 1 
listener has a packet 
Received a packet test1 
Packet from :/127.0.0.1 saying: test1 

We added a received packet! - 1 
listener has a packet 
Received a packet test2 
Packet from :/127.0.0.1 saying: test2 
+1

有什麼奇怪的?你期望看到什麼? – markspace 2014-09-02 23:21:20

+0

它打印出來的一切嗎?你真的發送全部3個數據包,「你好」,「test1」和「test2」嗎?你的輸出是有意義的*如果你發送「test1」*第一個*,並且如果在打印數據包內容之後,你的監聽器將打印「test2」等。 – 2014-09-02 23:22:28

+0

我有客戶端設置,以便我輸入字符串並將它們發送到服務器,這是服務器正在接收和打印的內容。我也有客戶在發送之前通過照片驗證包數據。應該說,像 我們增加了接收數據包 - 1個 聽者從/127.0.0.1包 收到一個數據包你好 包說:你好 等所有三個 – grundyboy34 2014-09-02 23:27:22

回答

3

我覺得東西線問題在於你一遍又一遍重複使用DatagramPacket的同一個實例。

這意味着一個線程接收數據包,但實際上它只是始終「更新」DatagramPacket的同一個實例。隊列將包含幾次相同的實例。

第二個線程從隊列中拉出同一個實例,這個實例在執行各種打印時發生變化,所以打印結果不穩定。此外,我不知道DatagramPacket類的內部,但同步的內部鎖可能會導致一個線程等待其他或類似的東西。

只是爲了確保有兩個線程之間沒有變異的東西,我會寫:

while (isRunning()) { 
     try { 
      byte[] buffer = new byte[256]; 
      DatagramPacket inPacket = new DatagramPacket(buffer, buffer.length); 
      socket.receive(inPacket); 
      addPacket(inPacket); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
+0

嗯,我會嘗試改變,看看它是否有幫助。我明白你在說什麼。我想你在這裏,因爲我也不認爲DatagramSocket類是併發的。 – grundyboy34 2014-09-02 23:36:37

+0

嗯,所以這確實有些幫助,但仍然是,發送的第一個數據包導致服務器只打印「偵聽器有一個數據包」。我不知道如何打印,但打印後立即運行的功能無法運行? – grundyboy34 2014-09-03 00:09:33

+0

你能用新代碼更新問題嗎? – 2014-09-03 00:10:48