Вопрос по java, client-server, sockets – многопоточный клиент-серверный чат с использованием сокетов

0

Сервер и клиент общаются с моим собственным протоколом, который выглядит как XMPP. Я должен реализовать приложение чата. Поэтому, когда один пользователь пишет String, он немедленно должен быть отправлен другому клиенту через сервер. У меня есть метод sendToAll на сервере. Но пользователь видит сообщение другого пользователя только тогда, когда нажимает ввод. How can user receive messages without pressing enter button?

Итак, это мой клиент:

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;
    }
}
только что отредактировал. Igor Masternoy
Здесь есть вопрос? cjstehno
Я хочу читать из потока iput в потоке backgroud. Igor Masternoy

Ваш Ответ

2   ответа
1

ДелатьNot use BufferedReader() with PrintWriter..... PrintWriter сам является мостом между данными сокета байтового уровня и символьной формой.

Например:

Я показываю для одного клиента, используйте цикл while для n nos клиентов

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

Хорошо, спасибо всем, ребята Igor Masternoy
Почему я не должен использовать это? Igor Masternoy
Хорошо ... это так ... При чтении ввода из сокета мы можем использовать InputStreamReader, который является мостом между низкоуровневым байтовым потоком Socket и символьными данными BufferedReader. Во время записи вывода PrintWriter обрабатывает сам символ, поэтому в BufferedWriter нет необходимости
1

так и сервера. Клиенту потребуется один поток, который прослушивает сообщения от сервера и записывает их на свой экран, и один поток, который ожидает ввода с клавиатуры и отправляет его на сервер. Аналогично для каждого соединения с сервером ему потребуется поток, ожидающий ввода от клиента, и один поток, отправляющий выходные данные от других пользователей клиенту.

Причина, по которой вы не видите входящие сообщения до тех пор, пока не нажмете клавишу ввода, заключается в цикле «клиент». Это закомментировано сейчас, но похоже, что ваш цикл раньше:
- читать входящие сообщения с сервера
- Читать ввод с клавиатуры
- Отправить вход на сервер

Таким образом, вы читаете все, что было доступно с сервера, а затем клиент ждет большего ввода с клавиатуры, прежде чем снова читать с сервера (в следующей итерации).

Еще один совет, насколько я понимаю, создание JAXBContext может быть дорогой операцией. Вам не нужно пересоздавать его каждый раз, когда вы отправляете сообщение. Попробуйте инициализировать один на своем сервере и клиенте, а затем повторно использовать его для каждого маршала / анмаршала.

Сделал все как ты говоришь, и все отлично работает. Один поток прослушивает данные с сервера, другой прослушивает данные из командной строки пользователя и отправляет их на сервер. (Я делаю консольный чат) Какие проблемы с синхронизацией могут существовать на стороне клиента? На стороне сервера отправка данных всем клиентам синхронизирована. Igor Masternoy
Я действительно не вижу проблем с синхронизацией для такого приложения. Вы не являетесь клиентом, который изменяет какое-либо состояние на стороне сервера. Это просто отправка сообщений и использование сервера в качестве маршрутизатора. Возможно, вы могли бы использовать синхронизацию, чтобы гарантировать порядок сообщений: например, я отправляю & quot; A & quot; и через секунду вы отправляете «B», я могу получить «B»; сначала без синхронизации. Но это не имеет большого значения для меня.

Похожие вопросы