После преобразования многопоточного сервера в SSL возникает проблема с возвратом сообщений - PullRequest
0 голосов
/ 30 августа 2018

У меня есть многопоточный сервер чата, который я преобразовал для работы с сокетами Java SSL. Вы можете увидеть версию без сокетов SSL по сравнению с той, которую я конвертировал здесь, на Github. (Основная ветвь имеет SSL, другая ветвь имеет обычные сокеты)

Эта оригинальная модель (без SSL) использует «ServerThreads», контролируемые Клиентом, для связи с другими Клиентами, отправляя сообщения своим «ClientThreads» на стороне сервера, которые затем выводят их сообщения на все другие ServerThreads.

Вот метод запуска ServerThread_w_SSL (на стороне клиента)

 @Override
public void run(){
    System.out.println("Welcome :" + userName);

    System.out.println("Local Port :" + socket.getLocalPort());
    System.out.println("Server = " + socket.getRemoteSocketAddress() + ":" + socket.getPort());
    //setup handshake
    socket.setEnabledCipherSuites(socket.getSupportedCipherSuites());

    try{
        PrintWriter serverOut = new PrintWriter(socket.getOutputStream(), false);
        InputStream serverInStream = socket.getInputStream();
        Scanner serverIn = new Scanner(serverInStream);
        // BufferedReader userBr = new BufferedReader(new InputStreamReader(userInStream));
        // Scanner userIn = new Scanner(userInStream);
        socket.startHandshake();

        while(!socket.isClosed()){
            if(serverInStream.available() > 0){
                if(serverIn.hasNextLine()){
                    System.out.println(serverIn.nextLine());
                }
            }
            if(hasMessages){
                String nextSend = "";
                synchronized(messagesToSend){
                    nextSend = messagesToSend.pop();
                    hasMessages = !messagesToSend.isEmpty();
                }
                serverOut.println(userName + " > " + nextSend);
                serverOut.flush();
            }
        } 

Вот метод запуска ClientThread_w_SSL (на стороне сервера)

@Override
public void run() {
    try{
        // setup
        this.clientOut = new PrintWriter(socket.getOutputStream(), false);
        Scanner in = new Scanner(socket.getInputStream());

        //setup handshake
        socket.setEnabledCipherSuites(socket.getSupportedCipherSuites());
        socket.startHandshake();
        // start communicating
        while(!socket.isClosed()){
            if(in.hasNextLine()){
                String input = in.nextLine();
                // NOTE: if you want to check server can read input, uncomment next line and check server file console.
                 System.out.println(input);
                for(ClientThread_w_SSL thatClient : server.getClients()){
                    PrintWriter thatClientOut = thatClient.getWriter();
                    if(thatClientOut != null){
                        thatClientOut.write(input + "\r\n");
                        thatClientOut.flush();
                    }
                }
            }
        }

Исходная программа работает с обычными сокетами, но после преобразования в сокеты SSL я столкнулся с проблемой: ввод не передается обратно из ClientThreads (на стороне сервера) в ServerThreads (на стороне клиента).

В моей первой попытке преобразования в SSL я использовал сертификаты, хранилища ключей и хранилища доверенных сертификатов. Тогда я столкнулся с той же проблемой, что и здесь, без них, а использовал только фабрику сокетов по умолчанию, которая опирается на файл cacerts, который поставляется вместе с JDK.

Обратите внимание, что до того, как возникла эта ошибка, первой проблемой, которая была устранена, был сбой рукопожатия между клиентом и сервером. Из-за способа работы SSL и класса Java PrintWriter рукопожатие инициируется при первом вызове PrintWriter.flush (), что происходит, как только клиент отправляет сообщение чата на сервер. Это решается только путем ручного включения поддерживаемых наборов шифров в ClientThread (сервер) и ServerThread (клиент), затем вызывая SSLSocket.StartHandshake () как минимум в ClientThread, если не в обоих.

Теперь сервер получает сообщения от клиента, но не передает их клиентам.

Когда я запускаю его в отладчике и пытаюсь пройтись по коду, я нахожу, что ClientThread получает сообщение клиента и отправляет его обратно, вызывая write () для PrintWriter для каждого ClientThread, а затем flush (). Предполагается, что ServerThread получает его, вызывая InputStream.available () для проверки ввода без блокировки, но available () всегда возвращает «0 байт» и поэтому никогда не переходит к Scanner.nextLine ()

Таким образом, либо Printwriter.write () и .flush () не отправляют данные, либо InputStream.available () не читает данные.

РЕДАКТИРОВАТЬ: После дополнительной отладки и тестирования я могу только сузить проблему до вывода со стороны сервера. Я определил это тем, что сервер немедленно отправил собственное сообщение, прежде чем ждать получения сообщений, и заставил клиента просто захватить nextLine () вместо проверки сначала с помощью available (). Поскольку этот тест не пройден, он показывает, что данные должны быть заблокированы каким-либо образом только со стороны сервера.

РЕДАКТИРОВАТЬ 2: я изменил код, чтобы использовать ObjectInputStreams и ObjectOuputStreams вместо использования Scanner и PrintWriters. Теперь я отправляю объекты «Message» из класса Serializable, который я создал для хранения строк. Это исправило проблему вывода для сообщений, приходящих с сервера. Если я заставлю клиента просто ждать ввода, вызывая readObject (), он будет получать сообщения с сервера. Тем не менее, если я сначала использую метод inputStream () метода InputStream, он по-прежнему будет возвращать только 0, даже если не должен. Поскольку InputStream serverInStream инициализируется с помощью socket.getInputStream (), он получает ssl.AppInputStream с ssl.InputRecord, и я предполагаю, что один из двух не реализует корректно доступный ().

1 Ответ

0 голосов
/ 05 сентября 2018

Я разобрался: проблема была доступна (), бесполезно с SSL в Java. Я получил решение из этого ответа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...