2013-10-18 43 views
0

我有一個簡單的客戶端和服務器,我已經寫了教自己一點網絡。它的設置方式是我有一個主要的服務器類,它將處理創建/銷燬套接字,以及代表每個連接的ConnectionThread類(每個連接都有自己的線程)。客戶非常簡單。Java套接字I/O流空指針異常

問題在於在ConnectionThread類中創建輸入/輸出流。我不知道到底是什麼問題,但在簡單的測試客戶端試圖連接它崩潰了,給我這個:

~~MMO Server Alpha .1~~ 
Constructed Server 
Server Initialized, preparing to start... 
Server preparing to check if it should be listening... 
Server should be listening, continuing as planned. 
ServerSocket passed to ConnectionThread: ServerSocket[addr=0.0.0.0/0.0.0.0,localport=6969] 
Constructing ConnectionThread. 
Socket[addr=/10.0.1.10,port=55332,localport=6969] 
ConnectionThread constructed. 
Exception in thread "main" java.lang.NullPointerException 
    at ConnectionThread.init(ConnectionThread.java:65) 
    at Server.listen(Server.java:98) 
    at Server.start(Server.java:62) 
    at Server.main(Server.java:122) 
ConnectionThread added to queue. 
Establishing in and out streams: 
null 

這裏是(修訂爲簡潔起見)類:

public class Server { 
    int PORT; 
    boolean shouldListen; 
    ArrayList<ConnectionThread> connections = new ArrayList<ConnectionThread>(); 
    ServerSocket serverSocket; 



    public Server() { 

     try { 
      PORT = 6969; 
      shouldListen = true; 
      serverSocket = new ServerSocket(PORT); 
     } 
     catch (IOException e) { 

      System.out.println("Error in server constructor."); 
      System.exit(1); 
     } 
    } 



    public void start() { 
     System.out.println("Server preparing to check if it should be listening..."); 
     listen(); 
     System.out.println("Server finished listening."); 
    } 


    public void listen() { 
     while (shouldListen) { 
      ConnectionThread conn = null; 
      System.out.println("Server should be listening, continuing as planned."); 
      try { 
      conn = new ConnectionThread(serverSocket); 
      } 
      catch (Exception e) { 
       System.out.println("____Error constructing ConnectionThread. Could there be another instance of the server running?"); 
       System.exit(1); 
      } 
      System.out.println("ConnectionThread constructed."); 

      connections.add(conn); 
      System.out.println("ConnectionThread added to queue."); 

      conn.init(); 
      System.out.println("Finished ConnectionThread initialization, verifying..."); 

      if (conn.isInitialized) { 
       System.out.println("ConnectionThread Initialized, preparing to start new thread."); 
       (new Thread(conn)).start(); 
      } 
     } 
    } 


    public static void main(String[] args) { 
     System.out.println("~~MMO Server Alpha .1~~"); 
     Server server = new Server(); 
     System.out.println("Constructed Server"); 
     server.init(); 
     System.out.println("Server Initialized, preparing to start..."); 
     server.start(); 

    } 

} 

這裏的ConnectionThread類:

public class ConnectionThread implements Runnable { 
    boolean shouldBeListening = true; 
    boolean isThereAnUnsentOutgoingMessage = false; 
    String outgoingMessage = "OUTGOING UNINITIALIZED"; 
    boolean IsThereAnUnsentIncomingMessage = false; 
    String incomingMessage = "INCOMING UNITIALIZED"; 
    boolean isInitialized = false; 
    PrintWriter out; 
    BufferedReader in; 

    String currentInputMessage = "Test Input Message from the Server ConnectionThread"; 
    String previousInputMessage = null; 


    Socket socket; 





    public ConnectionThread(ServerSocket s) { 
     System.out.println("ServerSocket passed to ConnectionThread: " + s); 
     /* 
     * The purpose of the constructor is to establish a socket 
     * as soon as possible. All transmissions/logic/anything else 
     * should happen in init() and/or run(). 
     */ 
     System.out.println("Constructing ConnectionThread."); 
     try { 
     Socket socket = s.accept(); 
     System.out.println(socket); 
     } 
     catch (IOException e) { 
      System.out.println("Error in ConnectionThread constructor"); 
      System.exit(1); 
     } 
    } 




    public void init() { 
     /* 
     * Everything should be set up here before run is called. 
     * Once init is finished, run() should be set to begin work. 
     * This is to ensure each packet is efficiently processed. 
     */ 
     try { 
      System.out.println("Establishing in and out streams:"); 
      System.out.println(socket); 
      out = new PrintWriter(socket.getOutputStream(), true); 
      System.out.println("ConnectionThread: Output Stream (PrintWriter) Established"); 

      in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
      System.out.println("ConnectionThread: InputStream (BufferedReader) Established"); 
     } 
     catch (IOException e) { 
      System.out.println("Error in ConnectionThread method Init."); 
      System.exit(1); 
     } 



     isInitialized = true; 
    } 

和可選,這裏的測試客戶端:

public class TestClient { 
    static PrintWriter out; 
    BufferedReader in; 
    public final int PORT = 6969; 
    Socket socket = null; 
    InetAddress host = null; 

    public TestClient() { 
     out = null; 
     in = null; 
     socket = null; 
     host = null; 



    } 



    public void connectToServer() { 
     System.out.println("Connecting to server..."); 
     try { 
      host = InetAddress.getLocalHost(); 
      socket = new Socket(host.getHostName(), PORT); 
     } 
     catch (Exception e) { 
      System.out.println("Error establishing host/socket"); 
      System.exit(1); 
     } 




     try { 
      System.out.println("Establishing I/O Streams"); 
      out = new PrintWriter(socket.getOutputStream(), true); 
      in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
     } 
     catch (Exception e) { 
      System.out.println("Error establishing in/out streams"); 
      System.exit(1); 
     } 
    } 


    public static void main(String[] args) { 
     System.out.println("~~TestClient Alpha .1~~"); 
     TestClient c = new TestClient(); 
     c.connectToServer(); 
     System.out.println("Should be connected to server. Sending test message..."); 
     while (true) { 
      System.out.println("here"); 
      out.println("Hello there"); 
     } 

    } 

} 

回答

2

ConnectionThread的構造函數中的'socket'變量不應該是本地的。它隱藏了成員變量。

通常在listen()循環中調用accept(),並將接受的套接字傳遞給ConnectionThread。

+0

哦,男人。多麼疏忽。非常感謝你指出這一點。 StackOverflow的善良和實用性永遠不會讓人驚歎。 –

1

正如EJP said,在你ConnectionThread構造你認爲你的價值分配給socket場,但是你實際上是分配值到socket方法變量,因此socket領域保持爲空,而在init()你看到socket爲空。

1

除了EJP回答:你沒有提供ConnectionThread.run()方法,但我相信你會在你的run()方法使用的字段inoutsocket。由於這些字段未標記爲volatilefinal,根據您的運氣和計算機上的核心數量,您可能還會在run()方法中獲得NullPointerException。

這是因爲新變量值可能不會在緩存之間傳播,並且新線程不會看到更改的值。

這個可能的問題的解釋在這裏 - The code example which can prove "volatile" declare should be used

+0

我不這麼認爲。啓動線程刷新緩存。 – EJP

+0

謝謝,沒有意識到線程開始創建_happens before_。 –