Насколько надежным является поток flush () потока сокета?

Рассмотрим эту (упрощенную) часть кода:

public class Test { // assigned elsewhere InetSocketAddress socketAddress; String socketHost; int socketPort; Socket socket; int COMMAND = 10; int CONNECTION_TIMEOUT = 10 * 1000; int SOCKET_TIMEOUT = 30 * 1000; DataOutputStream dos; DataInputStream dis; protected void connect() throws IOException, InterruptedException { socket.connect(socketAddress != null ? socketAddress : new InetSocketAddress(socketHost, socketPort), CONNECTION_TIMEOUT); socket.setSoTimeout(SOCKET_TIMEOUT); socket.setTcpNoDelay(true); } void initializeDataStreams() throws IOException { dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream(), socket.getSendBufferSize())); dis = new DataInputStream( new BufferedInputStream( socket.getInputStream(), socket.getReceiveBufferSize())); } void run() { try { connect(); initializeDataStreams(); sendCommand(COMMAND, true); sendIdAndUsername(true); sendSyncPreference(true); sendBlockedIds(true); sendHeaders(); // reading from 'dis' here // ... } catch (InterruptedException | IOException e){ /* ... */ } } void sendCommand(int command, boolean buffered) throws IOException { dos.write(command); if (!buffered) { dos.flush(); } } void sendIdAndUsername(boolean buffered) throws IOException { sendId(true); // always buffered String username = "user name"; dos.writeBoolean(username != null); if (username != null) { dos.writeUTF(username); } if (!buffered) { dos.flush(); } } void sendId(boolean buffered) throws IOException { dos.writeUTF("user id"); if (!buffered) { dos.flush(); } } void sendSyncPreference(boolean buffered) throws IOException { boolean fullSync = true; dos.writeBoolean(fullSync); if (!buffered) { dos.flush(); } } void sendBlockedIds(boolean buffered) throws IOException { Set<String> blockedCrocoIds = new HashSet<>(); ObjectOutputStream oos = new ObjectOutputStream(dos); oos.writeObject(blockedCrocoIds); if (!buffered) { oos.flush(); } } private void sendHeaders() throws IOException { dos.writeUTF("some string"); dos.writeInt(123); // some other writes... // this should flush everything, right? dos.flush(); } } 

Я оставил его намеренно со всеми методами, на всякий случай, когда я сделал там какую-то ужасную ошибку. Когда я выполняю Test.run (), иногда (очень сложно предсказать, когда именно) кажется, что flush () в sendHeaders () вообще не работает.

Серверная сторона ничего не получает на своем сервере ServerSocket.accept () в течение следующих 22 секунд (не спрашивайте меня, откуда этот номер, часть тайны).

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

Итак, что случилось с этим кодом? Как обеспечить, чтобы записи в мой поток были надежными / немедленными, чтобы сервер мог прочитать его как можно скорее?

Я также принимаю ответ «нет ничего плохого», в этом случае это должно быть что-то, что выполняется параллельно и влияет на сетевой стек Android.

EDIT: код сервера действительно ничего особенного:

 ListeningThread listeningThread = new ListeningThread(); listeningThread.start(); listeningThread.join(); 

а потом:

 public class ListeningThread extends Thread { private ServerSocket serverSocket; public ListeningThread() { try { // unbound server socket serverSocket = new ServerSocket(); serverSocket.setReuseAddress(true); serverSocket.bind(new InetSocketAddress(NetworkUtil.APP_SERVER_PORT)); } catch (IOException e) { log(e); } } @Override public void run() { log("run"); while (serverSocket.isBound() && !isInterrupted()) { try { Socket socket = serverSocket.accept(); new CommandThread(socket).start(); } catch (IOException e) { log(e); } } try { serverSocket.close(); } catch (IOException e) { log(e); } } } 

и наконец:

 public class CommandThread extends Thread { private final Socket socket; public CommandThread(Socket socket) { log("CommandThread"); this.socket = socket; } @Override public void run() { log("run"); try { socket.setSoTimeout(NetworkUtil.SOCKET_TIMEOUT); socket.setTcpNoDelay(true); InputStream is = socket.getInputStream(); int cmd = is.read(); // <========= so actually this is failing switch (cmd) { // handling of the command case COMMAND: new DownloadMessagesThread(socket).start(); break; } } catch (IOException | SQLException e) { log(e); } } } 

Как уже упоминалось в комментариях, я мог бы согласиться на что-то не так с объектными потоками и коо, но проблема в том, что я не могу достичь (опять же, это просто иногда очень случайный случай) CommandThread run () , Поэтому, если я не пропущу что-то еще, объектные потоки не могут вызвать такой отказ.

EDIT 2: Коррекция: это не прием () Я не могу достичь, это первая операция чтения:

03-07 11: 22: 42.965 00010 CommandThread: CommandThread

03-07 11: 22: 42.966 00108 CommandThread: run

[… ничего не происходит …]

03-07 11: 23: 04.549 00111 DownloadMessagesThread: run

Может ли это быть вызвано перемещением потока объектов и потока данных в конце концов?

Вы должны убедиться, что создание sendBlockedIds в sendBlockedIds не является виновником. У меня уже были некоторые «блокировки» протокола при смешивании DataStreams и ObjectStreams, поскольку создание пары Writer / Reader ObjectStreams подразумевает какое-то рукопожатие, которое может сбой при смешивании этих потоков.

РЕДАКТИРОВАТЬ: снова прочитав свой вопрос, я понял, что я не ответил на него. Так что да, это надёжно. И +1 для ответа EJP.

Чтобы ответить на вопрос в вашем названии, он на 100% надежный, поскольку он ничего не делает. Только методы flush() потоков, которые буферизуются, фактически что-то делают, и это включает только PrintStream и BufferedOutputStream и PrintStream зависимости от того, как вы его создаете. Not DataOutputStream , а не выходной поток самого сокета.

Таким образом, в этом случае единственным методом flush, который делает что-либо, является буферный выходной поток, и вы можете, конечно, полагаться на это, поскольку он всего лишь код, и работает уже двадцать лет.

Если это влияет на скорость accept() , в вашем цикле приема должно быть что-то нечетное, которое вы нам не показали: как правило, выполнение ввода-вывода в цикле принятия, а не в начальном потоке.

И вы, конечно же, не должны создавать ObjectOutputStream в середине соединения. Создайте его в начале и используйте его для всего, и ObjectInputStream на другом конце.

NB, устанавливая размеры буфера на размеры буфера сокета соответственно, на самом деле довольно бессмысленно. Значения по умолчанию являются адекватными.