2012-06-09 127 views
0

服務器和客戶端使用我自己的協議進行通信,看起來像XMPP。我應該實現聊天應用程序。因此,當一個用戶寫入字符串時,它應該通過服務器發送給其他客戶端。我有服務器上的sendToAll方法。但用戶只有按下回車鍵才能看到其他用戶的留言。 用戶如何在不按回車按鈕的情況下接收消息?多線程客戶端 - 服務器聊天,使用套接字

所以這是我的客戶:

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.io.PrintWriter; 
import java.net.Socket; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.JAXBException; 
import javax.xml.bind.Marshaller; 

import org.apache.log4j.Logger; 

import dataart.practice.protocols.XMLProtocol; 

public class Client { 
public static final String SERVER_HOST = "localhost"; 
public static final Integer SERVER_PORT = 4444; 
public static final Logger LOG = Logger.getLogger(Client.class); 
private static BufferedReader in; 
private static PrintWriter out; 
private static BufferedReader inu; 

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

    System.out.println("Welcome to Client side"); 
    XMLProtocol protocol = new XMLProtocol(); 
    Socket fromserver = null; 

    fromserver = new Socket(SERVER_HOST, SERVER_PORT); 

    in = new BufferedReader(new InputStreamReader(fromserver.getInputStream())); 

    out = new PrintWriter(fromserver.getOutputStream(), true); 

    inu = new BufferedReader(new InputStreamReader(System.in)); 

    String fuser, fserver; 
    while (true){ 
     if(in.ready()){//fserver = in.readLine()) != null) { 
     System.out.println("asdasdsd"); 

     fuser = inu.readLine(); 
     if (fuser != null) { 
      if (fuser.equalsIgnoreCase("close")) 
       break; 
      if (fuser.equalsIgnoreCase("exit")) 
       break; 

      protocol.setComId((long) 0); 
      protocol.setContent(fuser); 
      protocol.setLogin("Guest"); 

      try { 

       JAXBContext jaxbContext = JAXBContext.newInstance(XMLProtocol.class); 
       Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); 
       jaxbMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, false); 
       jaxbMarshaller.marshal(protocol, out); 
       out.flush(); 

      } catch (JAXBException e) { 
       LOG.error("Error while processing protocol" + e); 
      } 
     } 
     } 

    } 

    out.close(); 
    in.close(); 
    inu.close(); 
    fromserver.close(); 
} 

} 

而且服務器與ServerThread。

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

    LOG.trace("Server started"); 
    ServerSocket s = new ServerSocket(SERVER_PORT); 

    try { 
     while (true) { 
      LOG.trace("Waiting for connections..."); 
      Socket socket = s.accept(); 
      try { 
       // new ServerThread(socket); 
       BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 
       PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); 
       userCounter++; 
       addUser("Guest" + userCounter, out); 
       LOG.trace("User " + userCounter + " has been added!"); 
       exec.execute(new ServerThread(socket, in, out)); 

      } catch (IOException e) { 
       socket.close(); 
      } 
     } 
    } finally { 
     s.close(); 
    } 
} 

ServerThread。

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.PrintWriter; 
import java.io.StringReader; 
import java.net.Socket; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.JAXBException; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.transform.stream.StreamSource; 

import org.apache.log4j.Logger; 

import dataart.practice.protocols.XMLProtocol; 
import dataart.practice.serverUtils.Commands; 

public class ServerThread implements Runnable { 
    private static final Logger LOG = Logger.getLogger(ServerThread.class); 

    private XMLProtocol protocol; 
    private Socket socket; 
    private BufferedReader in; 
    private PrintWriter out; 
    private String buffer = "";// may be exist another. way but it's not working 
    private Boolean login = false; 

    public ServerThread(Socket s, BufferedReader in, PrintWriter out) throws IOException { 
     this.in = in; 
     this.out = out; 
     out.println("</XMLProtocol>"); 
     socket = s; 
     new Thread(this);  
    } 

    public void run() { 
     try { 
      while (true) {    
       if ((buffer = in.readLine()) != null) { 
        if (buffer.endsWith("</XMLProtocol>")) { 
         protocol = getProtocol(buffer); 
         //Server.onlineUserList.put(protocol.getLogin(), out); 
/*      if (!login){ 
          out.println("Maybe login first?"); 

         } 
*/      
         LOG.trace("Getting message from user: " + protocol.getLogin() + " recived message: " + protocol.getContent()); 
         ///out.println(protocol.getLogin() + " says:" + protocol.getContent()); 
         Server.sendToAll(protocol.getContent()+"</XMLProtocol>"); 


        } else { 
         LOG.trace("Nop protocol do not send with it end"); 
        } 
       } 
      } 
     } catch (IOException e) { 
      LOG.error("Error in reading from stream: " + e); 
     } catch (JAXBException e) { 
      LOG.error("Error in Marshalling: " + e); 
     } finally { 
      try { 
       socket.close(); 
       LOG.trace("Socket closed"); 
      } catch (IOException e) { 
       LOG.error("Socket no closed" + e); 
      } 
     } 
    } 

    public XMLProtocol getProtocol(String buffer) throws JAXBException { 
     JAXBContext jaxbContext = JAXBContext.newInstance(XMLProtocol.class); 
     Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); 
     return (XMLProtocol) jaxbUnmarshaller.unmarshal(new StreamSource(new StringReader(buffer))); 
    } 

    public Boolean loginIn(XMLProtocol protocol) { 

     return true; 
    } 
} 
+0

這裏有問題嗎? – cjstehno

+0

剛剛編輯。 –

+0

我想從backgroud線程中的iput流中讀取數據。 –

回答

1

您將需要多線程客戶端和服務器。客戶端需要一個線程來偵聽來自服務器的消息,並將它們寫入他/她的屏幕和一個等待他/她的鍵盤輸入並將其發送到服務器的線程。同樣,對於到服務器的每個連接,它都需要一個線程等待客戶端的輸入,一個線程將其他用戶的輸出發送到客戶端。

直到按下回車鍵之前,您看不到傳入消息的原因是因爲客戶端while循環。現在註釋掉了,但它看起來像使用你的循環:
- 從服務器讀取
傳入的消息 - 從鍵盤
讀取輸入 - 輸入發送給服務器

所以你讀什麼從服務器,然後客戶端在再次從服務器讀取數據之前等待更多的鍵盤輸入(在下一次迭代中)。

另一個建議的話,從我的理解,創建JAXBContext可能是一個昂貴的操作。每次發送消息時都不需要重新創建它。考慮在你的服務器和客戶端初始化一個,然後爲每個marshall/unmarshall重用它。

+0

是否所有人都喜歡你說的,而且完美地工作。一個線程偵聽來自服務器的數據,另一個偵聽來自用戶命令行的數據並將其發佈到服務器。 (我讓控制檯聊天)客戶端可能存在什麼同步問題?在服務器端發送數據到所有客戶端是同步的。 –

+0

我真的沒有看到像這樣的應用程序的任何同步問題。您的客戶端不會修改任何服務器端狀態。它只是發送消息並將服務器用作路由器。您可以使用同步來保證消息的排序:例如,我發送「A」,一秒鐘後發送「B」,我可以先接收「B」,而不同步。但這對我來說聽起來不是什麼大不了的事。 – jeff

1

試試這個,

Not use BufferedReader() with PrintWriter ..... PrintWriter本身字節級插座數據和字符形式之間的橋樑。

如:

我顯示單個客戶端,使用while循環,爲客戶

ServerSocket s = new ServerSocket(4444); 

Socket incoming = s.accept(); 

OutputStream output = s.getOutputStream(); 

PrintWriter pw = new PrintWriter(output,true); 

的System.out.println(pw.write(新的掃描儀(System.in正號).nextLine()));

+0

爲什麼我不應該使用它? –

+0

好吧..它是這樣的......當從socket讀取輸入時,我們可以使用InputStreamReader,它是Socket的低級字節流和BufferedReader的字符數據之間的橋樑。在寫輸出期間,PrintWriter自己處理字符事件,所以不需要BufferedWriter –

+0

好的,謝謝大家 –