I have read on some question of StackOverflow that we usually use the condition (c = inputStream.read()) == -1)
to detect if "end of stream" is reached. However, I've also read that the OutputStream of a socket only sends an "end of stream" message if the socket is closed.
This makes this server code...
public class Server {
public static int port = 27018;
public static void main(String[] args) throws IOException, InterruptedException {
char c = ' ';
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Server started");
Socket connectionSocket = serverSocket.accept();
serverSocket.close();
String bufferOuputString = "";
InputStream in = connectionSocket.getInputStream();
OutputStream out = connectionSocket.getOutputStream();
System.out.println("Connection established on port " + port);
out.write("Welcome to my server !".getBytes());
System.out.println("Waiting for client messages");
while(c != '/'){
c = (char) in.read();
if(c == '*'){
System.out.println(bufferOuputString);
bufferOuputString = "";
} else {
bufferOuputString += c;
}
}
connectionSocket.close();
}
}
...alongside this client code...
public class Client {
public static int port = 27018;
public static void main(String[] args) throws IOException {
try (Socket socket = new Socket("127.0.0.1", port)) {
int c;
Scanner sc = new Scanner(System.in);
String bufferInputString = "";
String bufferString = "";
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
while((c = in.read()) != -1){
bufferInputString += (char) c;
}
System.out.println(bufferInputString);
while(!bufferString.contains("/")){
System.out.println("You can write messages to server:");
bufferString = sc.nextLine() + "*";
out.write(bufferString.getBytes());
}
}
}
}
... useless. Because the client awaits for the end of stream signal (in.read() = -1
) that never comes, so it stays blocked by the InputStream.read()
call.
For the record, I tried this as server code to further check this "rule" of sockets:
public class Server {
public static int port = 27018;
public static void main(String[] args) throws IOException, InterruptedException {
char c = ' ';
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("Server started");
Socket connectionSocket = serverSocket.accept();
serverSocket.close();
String bufferOuputString = "";
InputStream in = connectionSocket.getInputStream();
OutputStream out = connectionSocket.getOutputStream();
System.out.println("Connection established on port " + port);
out.write("Welcome to my server !".getBytes());
Thread.sleep(3000);
connectionSocket.close();
}
}
And after 3 seconds, client printed...
...and proceeded to go into infinite looping because I forgot to change the second while
loop. So this confirms that socket's OutputStream
object only sends "end of stream" at Socket.close()
. My question is : why this design choice ?
I'm going to close this question.
My conclusion is that the only way to know the end of a message at socket level is really the socket closing. This is because "socket" is at OSI level 5: in TCP/IP, the "end of a message" is indeed the end of the connection. That's why the socket signals "end of connection" only when connection is closed.
The level of abstraction required to identify and separate messages inside a socket's stream must be done at Application level (OSI level 7, typically HTTP), TCP/IP cannot do it by design.
So the solution for this problem is either to define a "end of message" character (like CRLF or \r\n
in a Java String), or to use an existing application protocol (like HTTP) to exchange messages. From a discovery and experimentation POV, I find that both are interesting!
I realize now this question was more a misunderstanding of the OSI layers than a Java problem. Thank you for taking the time to answer me!