Сокеты Многопоточность Тупик

У меня есть проблема, похожая на this, но я Я знаю, что когда я прошу прочитать строку, отправитель должен отправить конец строки.

Что меня смущает, так это то, что при отладке это работает. Вероятно, потому что порядок, который я перешагнул при отладке (о котором я даже не знал, что это может иметь значение до сих пор), но я хочу лучше понять его.

Я уже работал с потоками, но не очень много.

Вот мой класс сервера:

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class Server {

    protected static List<Game> games = new ArrayList<>();
    protected static List<ServerThread> players = new ArrayList<>();

    public static void main(String[] args) throws Exception {
        int serverPort = 8945;
        Server server = new Server();
        ServerSocket welcomeSocket = new ServerSocket(serverPort);

        while (true) {
            Socket connectionSocket = welcomeSocket.accept();
            ServerThread st = new ServerThread(server,connectionSocket);
            st.start();
            int gameId = 0;
            if(players.size()>0 && players.size()%2==0){
                gameId++;
                players.get(0).outToClient.write("START " + gameId
                        + " 123 456" +"\n");
                players.get(0).outToClient.flush();
                players.get(1).outToClient.write("START " + gameId
                        + " 456 123" +"\n");
                players.get(1).outToClient.flush();
            }
        }
    }
}

Тема (на основе этого)

import java.io.*;
import java.net.Socket;

public class ServerThread extends Thread {
    protected Server server;
    protected Socket socket;
    protected String playerName;   
    protected BufferedReader inFromClient;
    protected BufferedWriter outToClient;

    public ServerThread(Server server, Socket clientSocket) throws IOException {
        this.server = server;
        this.socket = clientSocket;
        this.inFromClient = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        this.outToClient = new BufferedWriter(new InputStreamReader(socket.getOutputStream()));
    }

    public void run() {
        while (true) {
            try {
                String line = inFromClient.readLine();
                if(line != null) {
                    String[] clientCommand = line.split(" ");
                    String commandType = clientCommand[0];
                    if (!commandType.equalsIgnoreCase("QUIT")) {
                        switch (commandType) {
                            case "JOIN":
                                playerName = clientCommand[1];
                                System.out.println(playerName + " joined");
                                Server.players.add(this);
                                break;
                            case "PLAY":
                                //nothing yet
                                break;
                            case "MSG":
                                //nothing yet
                                break;
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
        }
    }

И Клиент:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class Client {

    private static int gameID;
    private static int order;
    private static String opponent;

    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.out.println("Usage: java Client <serverIp>");
            System.exit(1);
        }
        String serverIP = args[0];
        int serverPort = 8945;
        BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
        Socket clientSocket = new Socket(serverIP, serverPort);
        BufferedWriter outToServer = new OutputStreamWriter(clientSocket.getOutputStream());
        BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

        String line = inFromUser.readLine();
        String[] commandSentence = line.split(" ");
        String userCommandType = commandSentence[0];
        while (!userCommandType.equals("/exit")){
            switch (userCommandType){
                case "/enter":
                    String nickname = commandSentence[1];
                    outToServer.write("JOIN "+ nickname + '\n');
                    outToServer.flush();
                    while (true){
                        String serverLine = inFromServer.readLine();
                        String[] serverCommand = serverLine.split(" ");
                        String serverCommandType = serverCommand[0];
                        if(serverCommandType.equalsIgnoreCase("START")){
                            gameID = Integer.parseInt(serverCommand[1]);
                            order = Integer.parseInt(serverCommand[2]);
                            opponent = serverCommand[3];
                            System.out.printf("%5s %5s %5s",gameID,order,opponent);
                            break;
                        }                            
                    }
                case "/play":
                    //nothing yet
                    break;
                case "/msg":
                    //nothing yet
                    break;
            }
        }
    }
}

Похоже, он где-то входит в тупик и по какой-то причине никогда не входит в это, если в Serverclass, который отправляет данные клиентам, если только он не работает в отладке

(Кстати, я использую get(0) и get(1) только для тестовых целей)

EDIT: Хорошо, моя глупая ошибка в том, что я забыл добавить outToServer.flush();, когда клиент отправляет данные на сервер. Но моя основная проблема сохраняется, когда я создаю двух клиентов, вводя «/ введите ‹nickname›» для каждого из них, когда последний добавляется в список, ожидается, что он введет этот оператор if на сервере.


person Patrick Bard    schedule 11.06.2015    source источник
comment
Описание вашей программы расплывчато. Что вы подразумеваете под тупиком? Что на самом деле происходит или не происходит? Какой if никогда не вводится, !st.isAlive()?   -  person David Schwartz    schedule 11.06.2015
comment
Простите за это. Теперь это зависит от того, я бы сказал да, когда я начал вопрос. Но, как сказал @EJP, я изменил DataOutputStream на BufferedReader, теперь он зависает еще раньше. Я не мог сейчас много чего проверить, но похоже, что это происходит, когда он пытается readLine() в ServerThread.   -  person Patrick Bard    schedule 11.06.2015
comment
Почему вы ожидаете, что будет введено if? Почему ветка не оживает?   -  person David Schwartz    schedule 11.06.2015
comment
Извините, у меня не было while(true) раньше, вот почему.   -  person Patrick Bard    schedule 11.06.2015
comment
Вы просто проверяете количество игроков при открытии сокета. Но ваш тест выполняется сразу после установки сокета и, возможно, до того, как клиент отправил JOIN.   -  person gfelisberto    schedule 11.06.2015
comment
Я могу это понять, но я не уверен, что точно знаю, как это исправить. Я чувствую себя немного тупым сейчас.   -  person Patrick Bard    schedule 11.06.2015


Ответы (2)


одна проблема связана с кодом клиента в строке, в которой вы отправляете команду на сервер. Строка, которую вы отправляете, имеет очень маленькую длину, поэтому ей нужен outToServer.flush(); работать правильно

person AntJavaDev    schedule 11.06.2015
comment
Да, я только что понял это. Но это не отменяет моей главной проблемы. Он не входит в if(players.size()>0 && players.size()%2==0), когда ожидается. - person Patrick Bard; 11.06.2015
comment
да, я сейчас проверяю эту строку, дайте мне немного времени - person AntJavaDev; 11.06.2015
comment
хорошо, решено, вам нужно открыть 3 клиента, чтобы войти в этот цикл, потому что у вас есть оператор if в основном потоке, поэтому он проверяет list.size(), и list.size() обновляется некоторое время позже потоком сервера, поэтому основной поток будет входить в if только после 3D-клиента - person AntJavaDev; 11.06.2015
comment
Мне нужно создавать игровой объект каждый раз, когда у меня появляется пара пользователей. Я не могу зависеть от третьего пользователя, чтобы другие играли. - person Patrick Bard; 11.06.2015
comment
да, то, что вы разработали, не соответствует тому, что вы хотите сделать. первая проблема в том что вы пытаетесь обслуживать клиента в основном потоке а не в том потоке который отвечает за указанного клиента - person AntJavaDev; 11.06.2015
comment
Раньше я угрожал в треде, но это сообщение отправляется только при создании игры, что происходит, когда присоединилась пара пользователей, и сервер знает эту пару. Но да, я подумываю как-то переделать его на самом деле. - person Patrick Bard; 11.06.2015
comment
Давайте продолжим это обсуждение в чате. - person Patrick Bard; 11.06.2015

Избавьтесь от теста ready(). Есть несколько правильных применений. Просто позвольте следующему блоку чтения.

NB: не смешивайте потоки с читателями и писателями. Если вы используете BufferedInputStream для чтения, вы должны использовать BufferedOutputStream для записи.

person user207421    schedule 11.06.2015
comment
Я добавляю проверку ready() потому что по крайней мере так она читалась бы правильно, до того как зависала при попытке чтения. Может быть, это была та же проблема с вводом и выводом, которую я не заметил. Я получил эту часть кода (перемешанный ввод и вывод) из шаблона, который дал мне мой профессор... - person Patrick Bard; 11.06.2015
comment
Есть ли что-то, что я должен рассмотреть, прежде чем выбирать из DataInputStream и DataOutputStream из BufferedInputStream и BufferedOutputStream. Какую пару мне использовать? - person Patrick Bard; 11.06.2015