multithread client-server chat, using sockets

℡╲_俬逩灬. 提交于 2019-12-19 04:57:13

问题


Server and client communicating with my own protocol which looks like XMPP. I should to realize chat application. So when one user write String it immedeatly should be sended to other client through the server. I have method sendToAll on server. But user see the message of other user only when it press enter. How can user receive messages without pressing enter button?

So this is my client:

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();
}

}

And Server with 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;
    }
}

回答1:


You will need to multi-thread both the client and server. The client will need one thread that listens for messages from the server and writes them to his/her screen and one thread that waits for his/her keyboard input and sends it to the server. Likewise for each connection to the server, it will need a thread waiting for input from the client and one thread sending output from other users to the client.

The reason you don't see incoming messages until you press enter is because of the client while loop. It's commented out now, but it looks like your loop used to:
- Read incoming messages from server
- Read input from keyboard
- Send input to server

So you read whatever was available from the server, and then the client waits for more keyboard input before reading from the server again (in the next iteration).

Another word of advice, from my understanding, creating JAXBContext can be an expensive operation. You don't need to recreate it every time you send a message. Consider initializing one in your server and client and then reusing it for each marshall/unmarshall.




回答2:


Try this,

Do Not use BufferedReader() with PrintWriter..... PrintWriter is itself the Bridge between byte level socket data and character form.

Eg:

I am showing for a single client, use the while loop for n nos of clients

ServerSocket s = new ServerSocket(4444);

Socket incoming = s.accept();

OutputStream output = s.getOutputStream();

PrintWriter pw = new PrintWriter(output,true);

System.out.println(pw.write(new Scanner(System.in).nextLine()));



来源:https://stackoverflow.com/questions/10961683/multithread-client-server-chat-using-sockets

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!