2013-04-03 1 views
0

Эй, переполнение стека.ArrayList для многопользовательской чат-программы

Итак, я работал над программой чата, чтобы научить себя некоторой Java, и я работал с ней до такой степени, что любое количество клиентов могло свободно подключаться к серверу, но каждый клиент мог общаться только с сервером.

Это, очевидно, плохая чат-программа, поэтому я реализовал массивList обработчиков с целью отправки того, что один клиент пишет всем клиентам. Проблема в том, что я не мог заставить ее работать, и теперь моя программа вылетает после того, как я набрал 3 строки текста между клиентом и сервером.

Пожалуйста, взгляните на мой код. Я расскажу о разделах кода, который я изменил при попытке реализовать список массивов. Кажется, я просто застрял в неправильных местах.

Серверный код:

import java.io.*; 
    import java.net.*; 
    import java.util.*;; 

    public class Server{ 
    //---------------------------------------------------- 
    ArrayList<Handler> handlers = new ArrayList<Handler>(); 
    //---------------------------------------------------- 
public static void main(String[] args){ 

    try{ 
     ServerSocket ss = new ServerSocket(8822); 
     while(true){ 

      Socket s = ss.accept(); 
      new Handler(s).start(); 



     } 
    }catch(Exception e){ 
     System.out.println(e.getMessage()); 
    } 
} 
    } 

    class Handler extends Thread{ 

Socket socket; 
boolean notdone; 
BufferedReader br; 
PrintWriter pw; 
String line; 


public Handler(Socket socket){ 
    this.socket = socket; 
    notdone = true; 
} 

public void run(){ 

    //------------------ 
    handlers.add(this); 
    //------------------ 

    try{ 
     br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
     pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true); 
        //-------------------------------------------------------- 
     Iterator<Handler> handlerIterator = handlers.iterator(); 
        //-------------------------------------------------------- 
     while(notdone){ 

      line = br.readLine(); 

      if(line.equals("bye")){ 
       System.out.println("Client said 'bye'"); 
       notdone = false; 
       break; 
      }else{ 
       System.out.println("Echo: " + line); 
          //---------------------------------------------- 
       while (handlerIterator.hasNext()){ 
        Handler current = handlerIterator.next(); 

        current.pw.println(line); 

       } 
          //---------------------------------------------- 
      } 

     } 
     br.close(); 
     pw.close(); 
     socket.close(); 

    }catch(Exception e){ 
     System.out.println(e); 
     System.out.println("Client severed connection."); 
    } 
} 
    } 

Что касается клиента, я думаю, что я должен что-то изменить в выделенном разделе, , но я не уверен, что.

Client Код:

import java.io.*; 
import java.net.*; 
import java.util.*; 

public class Client{ 

public static void main(String[] args){ 


    try{ 
     Socket s = new Socket("localhost", 8822); 

     BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); 
     PrintWriter pw = new PrintWriter(new OutputStreamWriter(s.getOutputStream()),true); 

     Scanner scan = new Scanner(System.in); 
     boolean notdone = true; 

     while(notdone){ 
      String outString = scan.nextLine(); 
      if(outString.equals("bye")){ 
       pw.println(outString); 
       notdone = false; 
      }else{ 
          //------------------ 
       pw.println(outString); 
       String inString = br.readLine(); 
          //------------------ 

       System.out.println("Received: " + inString); 
      } 
     } 
     br.close(); 
     pw.close(); 
     s.close(); 

    }catch(Exception e){ 
     System.out.println("Server severed connection."); 
    } 

} 
} 
+1

Если вы разместите некоторый журнал, указав ошибку, было бы легче отладить. –

+0

Ошибок нет. Он компилируется просто отлично. Когда я запускаю сервер и клиент в командной строке, он запускается, но после того, как я говорю, отправьте сервер, скажите .. привет дважды. Он просто висит. Если я закрою его, это просто даст мне мою отключенную связь, попробуй поймать. Существует логическая ошибка где-то с моим итератором или, возможно, «handlers.add (this)»; находится в неправильном месте. –

+0

Как насчет того, чтобы вы печатали исключение в блоке catch? –

ответ

2

Вот одна ошибка для вас:

Iterator<Handler> handlerIterator = handlers.iterator(); 
    while(notdone){ 
     // ... 
     while (handlerIterator.hasNext()){ 
      Handler current = handlerIterator.next(); 
      // .... 
     } 
    } 

Первый раз, когда вы идете через внешний цикл, вы будете проходить через внутренний цикл. второй время через внешний контур, handlerIterator уже исчерпан, поэтому он вернет false для hasNext(), и вы больше никогда не войдете во внутренний цикл.

Вместо этого следует использовать для-каждого цикла:

while(notdone){ 
     // ... 
     for (Handler current : handlers){ 
      // .... 
     } 
    } 

Это эквивалентно (но более емким, чем)

while(notdone){ 
     // ... 
     Iterator<Handler> handlerIterator = handlers.iterator() 
     while (handlerIterator.hasNext()){ 
      Handler current = handlerIterator.next(); 
      // .... 
     } 
    } 

Между тем, на стороне клиента,

  pw.println(outString); 
      String inString = br.readLine(); 

говорит, что вы читаете только с сервера и всегда после записи на сервер. Таким образом, клиент не может просто слушать. Это должно быть в порядке с одним клиентом, но если у вас есть два клиента, то вы получите это:

  1. Clientâ посылает «привет» Сервер
  2. печатает сервера «привет» и к клиентскому потоков
  3. Clientâ читает «привет» из своего буфера (ClientB уже ничего не читала)
  4. ClientB посылает «мир» на сервер
  5. ClientB читают «привет» из своего буфера
  6. печатают сервера «мир» на оба клиента потоки

Итак, теперь оба клиента видели сообщение ClientA, но не ClientB. Вы будете продолжать получать этот эффект с большой задержкой, и в итоге буферы будут заполняться и блокироваться.

Настоящим решением является то, что каждый клиент должен иметь отдельный поток для чтения и записи. Но это довольно большой скачок в сложности кода. Я думаю, вы должны посмотреть на BufferedReader#ready(), в котором говорится, есть ли данные, ожидающие чтения. На стороне клиента, вы можете использовать это в начале своего пока-не-сделано цикла:

while(br.ready()) { 
    String inString = br.readLine(); 
    // ... 
} 
// Now all the messages from the server have been printed 

Вы все еще будете иметь проблемы клиентов, которым нужно говорить постоянно, но до тех пор, как они делают это, все должно работать.

Смежные вопросы