Java Networking: Explain InputStream and OutputStream in Socket - java

I have created a server by using ServerSocket. After that, I have created Client using Socket, and connect to this server.
After that, I do "some stuff" with InputStream and OutputStream is taken from Socket Object. But, I don't really understand inputStream and outputStream so much. Here is my simple code :
private Socket sock = null;
private InputStream sockInput = null;
private OutputStream sockOutput = null;
...
String msg = "Hello World";
byte[] buffer = null;
try {
sockOutput.write(msg.getBytes(), 0, test.length());
sockOutput.write("Hello StackOverFlow".getBytes(), 0, test.length());
buffer = new byte[test.length()];
sockInput.read(buffer, 0, test.length());
System.out.println(new String(buffer));
sockInput.read(buffer, 0, test.length());
System.out.println(new String(buffer));
} catch (IOException e1) {
e1.printStackTrace();
}
The result will be : "Hello World" and "Hello StackOverFlow".
Here is server side code :
private int serverPort = 0;
private ServerSocket serverSock = null;
public VerySimpleServer(int serverPort) {
this.serverPort = serverPort;
try {
serverSock = new ServerSocket(this.serverPort);
}
catch (IOException e){
e.printStackTrace(System.err);
}
}
// All this method does is wait for some bytes from the
// connection, read them, then write them back again, until the
// socket is closed from the other side.
public void handleConnection(InputStream sockInput, OutputStream sockOutput) {
while(true) {
byte[] buf=new byte[1024];
int bytes_read = 0;
try {
// This call to read() will wait forever, until the
// program on the other side either sends some data,
// or closes the socket.
bytes_read = sockInput.read(buf, 0, buf.length);
// If the socket is closed, sockInput.read() will return -1.
if(bytes_read < 0) {
System.err.println("Server: Tried to read from socket, read() returned < 0, Closing socket.");
return;
}
System.err.println("Server: Received "+bytes_read
+" bytes, sending them back to client, data="
+(new String(buf, 0, bytes_read)));
sockOutput.write(buf, 0, bytes_read);
// This call to flush() is optional - we're saying go
// ahead and send the data now instead of buffering
// it.
sockOutput.flush();
}
catch (Exception e){
System.err.println("Exception reading from/writing to socket, e="+e);
e.printStackTrace(System.err);
return;
}
}
}
public void waitForConnections() {
Socket sock = null;
InputStream sockInput = null;
OutputStream sockOutput = null;
while (true) {
try {
// This method call, accept(), blocks and waits
// (forever if necessary) until some other program
// opens a socket connection to our server. When some
// other program opens a connection to our server,
// accept() creates a new socket to represent that
// connection and returns.
sock = serverSock.accept();
System.err.println("Server : Have accepted new socket.");
// From this point on, no new socket connections can
// be made to our server until we call accept() again.
sockInput = sock.getInputStream();
sockOutput = sock.getOutputStream();
}
catch (IOException e){
e.printStackTrace(System.err);
}
// Do something with the socket - read bytes from the
// socket and write them back to the socket until the
// other side closes the connection.
handleConnection(sockInput, sockOutput);
// Now we close the socket.
try {
System.err.println("Closing socket.");
sock.close();
}
catch (Exception e){
System.err.println("Exception while closing socket.");
e.printStackTrace(System.err);
}
System.err.println("Finished with socket, waiting for next connection.");
}
}
public static void main(String argv[]) {
int port = 54321;
VerySimpleServer server = new VerySimpleServer(port);
server.waitForConnections();
}
My question is :
When I use sockOutput.write and I can get back those message back by sockInput.read. So, those message has been saved, right? If this true, does it saved on Server I have created or just saved in some other thing such as Socket Object.
If I have written to socket String A1, A2,... An so I will receive A1, A2, ... An String respectively, right?

A socket is an abstraction that you use to talk to something across the network. See diagram below...
In Java, to send data via the socket, you get an OutputStream (1) from it, and write to the OutputStream (you output some data).
To read data from the socket, you get its InputStream, and read input from this second stream.
You can think of the streams as a pair of one-way pipes connected to a socket on the wall. What happens on the other side of the wall is not your problem!
In your case, the server has another socket (the other end of the connection) and another pair of streams. It uses its InputStream (2) to read from the network, and its OutputStream (3) to write the same data back across the network to your client, which reads it again via its InputStream (4) completing the round trip.
Client Server
1. OutputStream -->\ /--> 2. InputStream -->
Socket <--> network <--> ServerSocket |
4. InputStream <--/ \<--3. OutputStream <--
Updated: in reply to comment:
Note that the streams and sockets just send raw bytes; they have no notion of a "message" at this level of abstraction. So if you send X bytes and another X bytes, then read X bytes and read another X bytes, then your system behaves as if there are two messages, because that's how you've divided up the bytes.
If you send X bytes, and another X bytes, then read a reply of length 2X, then you might be able to read a single combined "message", but as you've noticed, the underlying implementation of the streams can choose when to deliver chunks of bytes, so it might return X bytes, then X bytes, later, or 2X at once, or 0.5X four times...

InputStream and OutputStream are two completely separate streams. What you write into one has no a priori relation to what you read from the other. The InputStream gives you whatever data the server decides to send to you. I would also like to comment on this piece of your code:
sockOutput.write(msg.getBytes(), 0, test.length());
sockOutput.write("Hello StackOverFlow".getBytes(), 0, test.length());
You use the length of a string test (not shown in your code), which has nothing to do with the byte array you are passing as the first argument. This can cause an ArrayIndexOutOfBoundsException or truncation of your intended message.
Additional comments to your updated question
Reviewing your server-side code, it is not quite correctly written. You need to have try { handleConnection(...); } finally { socket.close(); } to ensure proper cleanup after an error, as well as when completing normally. Your code never closes anything on the server side.
Finally, and most critically, your entire code is written in a way that can result in a deadlock. Normally you need a separate thread to read and to write; otherwise the following may happen:
You attempt to write some data to the output;
The server reads it and tries to respond with data in your input;
But, since the buffers are too small, you don't manage to send everything because the server wants to first send something to you, then receive the rest; but you don't get to the receiving part before you have sent everything you've got.

Related

Socket hangs when trying to read input stream from web browser Java

I am attempting to read an input stream from a socket provided by a web browser client. Every approach I have taken has got the same results thus far, it just hangs and I don't know why. I have tried mark() marking the read limit to what is available and still no go.
inputStream.mark(inputStream.available());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 9];
int read;
while((read = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, read);
}
byte[] bytes = outputStream.toByteArray();
I have also tried clientSocket.shutdownInput() to tried to fix this issue, still no good.
Here is my attempt below:
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class Server {
public static void main(String[] args)
{
ServerSocket server = null;
try {
// Server is listening on port 3001
server = new ServerSocket(3001, 1, InetAddress.getByName("localhost"));
server.setReuseAddress(true);
// running infinite loop for getting
// client request
while (true) {
// socket object to receive incoming client
// requests
Socket client = server.accept();
// Displaying that new client is connected
// to Server
System.out.println("New client connected"
+ client.getInetAddress()
.getHostAddress());
// create a new thread object
ClientHandler clientSock
= new ClientHandler(client);
// This thread will handle the client
// separately
new Thread(clientSock).start();
}
}catch (IOException e) {
e.printStackTrace();
}
}
// ClientHandler class
private static class ClientHandler implements Runnable {
private final Socket clientSocket;
// Constructor
public ClientHandler(Socket clientSocket)
{
this.clientSocket = clientSocket;
}
public void run() {
InputStream inputStream = null;
OutputStream clientOutput = null;
try {
inputStream = clientSocket.getInputStream();
inputStream.mark(inputStream.available());
clientSocket.shutdownInput();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 9];
int numRead;
while((numRead = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, numRead);
}
byte[] bytes = outputStream.toByteArray();
String payloadString = new String(bytes, StandardCharsets.UTF_8);
System.out.println(payloadString);
clientOutput = clientSocket.getOutputStream();
clientOutput.write(("HTTP/1.1 \r\n" + "200 OK").getBytes());
clientOutput.write(("ContentType: " + "text/html" + "\r\n").getBytes());
clientOutput.write("\r\n".getBytes());
clientOutput.write("Hello World!".getBytes());
clientOutput.write("\r\n\r\n".getBytes());
clientOutput.flush();
inputStream.close();
clientOutput.close();
try{
clientSocket.close();
}catch(Exception ex){
ex.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Any help would be most appreciated! Thank you.
browsers suggests you should toss this all in the garbage and use HTTP, because, well, browsers.
But, if you insist, there are two problems here.
You've made it crazy complicated.
You can take all of that code and replace it all with this simple little line:
byte[] bytes = in.readAllBytes();
That replaces the lines starting with in.mark(in.available()) (this line does nothing at all, I have no idea where you got this from. If you think it is supposed to do something specific, you might want to mention that. Because it doesn't do anything. mark is useful if you ever reset, which you aren't, and you don't need to here, hence, useless), all the way to `byte[] bytes =...;
sockets don't close unless sender goes out of its way to close it
Your read code (yours, or the much simpler one-liner above) reads everything until the stream closes. In your second snippet, you close it right away, which obviously doesn't work. You cannot know when to close it, the sender does this job. Evidently it's not doing it.
I advise you to adapt protocols that pre-roll sizes, so you know how much to read and aren't dependent on closing the socket just to signal that the data is sent.
For example:
byte[] sizeRaw = in.readNBytes(4);
int size = ByteBuffer.wrap(bytes).getInt();
byte[] bytes = in.readNBytes(size);
You will of course have to adjust the sending code to send the size first (as a 32-bit value, big endian). One way or another you have to look at the sending code here. Either fix it so that it closes once done, or, better yet, adjust it so it sends size first.

Reading Bytes from an InputStreamReader goes into infinite loop

I've a test which fails because reading bytes from an InputStreamReader is going into infinite loop.
#Test
public void testSingleRequest() throws IOException {
Server server = new Server(9000, 100);
new Thread(server).start();
Socket clientSocket = new Socket("localhost", 9000);
OutputStream out = clientSocket.getOutputStream();
InputStream in = clientSocket.getInputStream();
String payload = "a";
byte[] load = buildPushPayload(payload);
out.write(load);
byte[] response = IOUtils.toByteArray(in);
server.stop();
assertEquals(0, response[0]);
}
This is the code that writes to the InputStream
byte[] resp = buildPushResponse();
output.write(resp);
private byte[] buildPushResponse() throws UnsupportedEncodingException {
ByteBuffer buffer = ByteBuffer.allocate(1);
buffer.order(ByteOrder.BIG_ENDIAN);
buffer.putInt(0);
return buffer.array();
}
Both the server and the client are write to the clientSocket Input Output stream.
I can't seem to understand why the infinite loop then?
What you are missing here is how to know when to stop reading. You are calling IOUtils.toByteArray(in) which will read until End Of File, which never happens (unless the other side closes their end of the socket).
There are a few schemes you can use to know how many bytes to read from the socket as a message packet before processing the complete message packet. For example, you could say that all packets are 8 bytes (in which case your read call would specify to read 8 bytes), you could say that the first byte is the count of bytes in the packet, you could say that a packet is variable length, and is terminated with a carriage return byte.
Once you have read a packet, you would normally process the packet and then go back to read another packet or close the socket if you're all done.
Here is a good example of socket programming: https://www.geeksforgeeks.org/socket-programming-in-java/
In that case, they are terminating a packet or message with an End Of Line mark - they are reading one line of text at a time with DataInputStream.readUTF(socket).
server = new ServerSocket(port);
socket = server.accept();
// Create a DataInputStream object that takes input from the socket
in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
String message = "";
// reads messages from client and prints them
// The loop exits if the message "Over" is received.
try
{
while (!"Over".equals(message))
{
message = in.readUTF();
System.out.println(message);
}
}
catch(IOException i)
{
// handle error
}
// close connection
socket.close();
in.close();

Send or receive an notification through TCP in java

Here is the main method of Client program which write to OutputStream for server and then wait for the server to send back a response.
public static void main(String[] args) throws IOException
{
Scanner input = new Scanner(System.in);
InetAddress server_addr = InetAddress.getByName(AUCTIONSERVER_IP_ADDRESS);
Socket client = new Socket(server_addr, BUYER_PORT);
user = "";
OutputStream out = client.getOutputStream();
InputStream in = client.getInputStream();
while (true)
{
String a = input.nextLine(); //read command from user
out.write(a.getBytes()); //send the command to server
byte[] data = new byte[10000];
in.read(data, 0, 10000); //receive the output
}
}
The server program which can accept multiple buyer at the same time and start each Thread below
The run() method for each Thread server create
public void run()
{
try
{
OutputStream out = this.socket.getOutputStream();
InputStream in = this.socket.getInputStream();
while (true)
{
byte[] data = new byte[100];
in.read(data, 0, 100); // do something with the data
out.write(result.getBytes()); // return the output to Buyer client
}
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
The client program will put something to OutputStream and server thread will read it (each client will be handled by 1 server thread) then send something back to client. Each write from client will match with one read from server and vice versa. However, when there is a special message sending from server to client (out of the cycle mentioned earlier), there is no way for client to receive it without messing up the cycle. Also, it will be stalled by input.nextLine() in client program so the client will not receive the notification unless he sends any command. Could anyone please suggest an efficient way to implement real-time notification for this problem?
I am thinking about making the server send an OutputStream to every thread, the one who actually have the notification will receive the message; the others will receive something like "-1". All the client program will check for inputStream at the beginning and handle it. However, this method seems inefficient for real server.

Proper way of closing Streams in Java Sockets

I saw some posts about this but I still can't find an answer.
This is how my server interacts with the client:
public void run () {
try {
//Read client request
InputStream is = server.getInputStream();
byte[] buff = new byte[1024];
int i;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((i = is.read(buff, 0, buff.length)) != -1) {
bos.write(buff, 0, i);
System.out.println(i + " bytes readed ("+bos.size()+")");
}
is.close();
is = null;
//Do something with client request
//write response
OutputStream os = server.getOutputStream();
os.write("server response".getBytes());
os.flush();
os.close();
os = null;
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
And this is the client side:
public void run() {
try {
InetAddress serverAddr = null;
serverAddr = InetAddress.getByName("10.0.2.2");
socket = new Socket(serverAddr, 5000);
//Send Request to the server
OutputStream os = socket.getOutputStream();
os.write(jsonRequest.toString().getBytes("UTF-8"));
os.flush();
os.close();
os = null;
//Read Server Response
InputStream is = socket.getInputStream();
byte[] buff = new byte[1024];
int i;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((i = is.read(buff, 0, buff.length)) != -1) {
bos.write(buff, 0, i);
System.out.println(i + " bytes readed ("+bos.size()+")");
}
is.close();
is = null;
//Do something with server response
} catch (UnknownHostException uhe) {
sendCallbackError(uhe);
} catch (IOException ioe) {
sendCallbackError(ioe);
}
}
As you can see, the client connects and send a request. Server read that request then writes a response that the client will read.
The problem with this code is the OutputStream.close() in the client and InputStream.close() in the server. As stated in the Javadocs, closing the stream will close the Socket. The result is that when the client tries to read the server response, the Socket is already closed.
I've managed to overcome this by calling Socket.shutdownInput and Socket.shutdownOutput instead. However I am still thinking whether this is the proper way of doing it
As a note, closing the streams with close() when server writes the response or when the client reads it doesn't create problems (I would guess the closing is synchronized between client and server).
So my questions are:
Is using the Socket shutdown methods a proper way?
Can I keep closing the last streams with close() (when sending and reading
response from server)
Could it happen that closing with shutdown would keep some data in
the buffer and wouldn't be sent?
You can do the following:
try{
}catch(){
}finally{
if(is!=null){
is.close();
}
if(os!=null){
os.close();
}
}
The problem with this code is the OutputStream.close() in the client and InputStream.close() in the server. As stated in the Javadocs, closing the stream will close the Socket.
Correct but the InputStream in the server isn't connected directly to a Socket: it is connected to something you don't know anything about. You can close it with impunity, although again you don't need to close it at all. You can close the OutputStream in the server if you like: although, again, as it isn't connected directly to a Socket, it may or may not have any effect other than flushing.
To address your actual question, you don't need to close the output stream in the client, but you do need to send an appropriate Content-Length: header. That way the server knows how much to read from the client. If this is only a GET request the content-length may well be zero. You don't need to call shutdownOutput(), although I guess there is nothing to stop you, and calling shutdownInput() doesn't do anything to the network anyway so again there is no point to it.

Android reading from Socket hangs on second read loop

I got to implement a chat in my application. Connection to a server is made using sockets. I should register to that server and the server will aknowledge that with a reply.
I have implemented this in a single method where I send the command using a BufferedWriter, and then start reading from the input stream until it tells me there is no more data.
I read properly the server reply. However, I never get the negative value from the second in.read call and thus my method stays blocked in the while loop (in the conditionnal statement where I make that call).
How should this be done with sockets? I usually do that with files or other input streams without problem.
If I should read only the bytes I am supposed to read, does that mean that I either have to:
Know in advance the length of the server response?
or make the server send a code to notify it has finished to send its response?
Currently I am doing the following:
private String sendSocketRequest(String request, boolean skipResponse) throws ChatException {
if (!isConnected()) openConnection();
try {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()), 2048);
out.append(request);
out.flush();
out = null;
} catch (IOException e) {
LogHelper.error("Unable to send socket request: " + request, e);
throw new ChatException("Unable to send socket request: " + request, e);
}
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()), 2048);
StringBuffer response = new StringBuffer();
char[] buffer = new char[2048];
int charsRead = -1;
// >>>>>>>> This is where it gets blocked <<<<<<<<<
while ((charsRead = in.read(buffer)) >= 0) {
if (charsRead > 0) response.append(new String(buffer, 0, charsRead));
}
return response.toString();
} catch (IOException e) {
LogHelper.error("Unable to read socket response: " + request, e);
throw new ChatException("Unable to read socket response: " + request, e);
}
}
Connection to the server is made with the following method:
public synchronized void openConnection() throws ChatException {
try {
socket = new Socket(Constants.API_CHAT_SERVER_ADDRESS, Constants.API_CHAT_SERVER_PORT);
socket.setKeepAlive(true);
LogHelper.debug("CHAT >> Connected to the chat server: " + Constants.API_CHAT_SERVER_ADDRESS);
} catch (UnknownHostException e) {
LogHelper.error("Unable to open chat connection", e);
throw new ChatException("Unable to open chat connection", e);
} catch (IOException e) {
LogHelper.error("Unable to open chat connection", e);
throw new ChatException("Unable to open chat connection", e);
}
}
The amount of data to be sent/received over a socket based connection is protocol dependend and not known to the TCP/IP stack, but only to the application layer.
The protocol used is developer dependend ... ;-) so coming to your questions:
If I should read only the bytes I am supposed to read, does that mean that I either have to:
Know in advance the length of the server response?
Yes, this is one possibility.
or make the server send a code to notify it has finished to send its response?
Also yes, as this is another possibility. Common markers are \n or \r\n. The NUL/'\0' character also might make sense.
A third option is to prefix each data chunk with a constant number of bytes describing the amount of bytes to come.
Instead of dealing with bytes, maybe it's simpler handling instances of ad-hoc classes, like - for instance - a Message class:
The server:
// Streams
protected ObjectInputStream fromBuffer = null;
protected ObjectOutputStream toBuffer = null;
// Listening for a new connection
ServerSocket serverConn = new ServerSocket(TCP_PORT);
socket = serverConn.accept();
toBuffer = new ObjectOutputStream(socket.getOutputStream());
fromBuffer = new ObjectInputStream(socket.getInputStream());
// Receiving a new Message object
Message data = (Message)fromBuffer.readObject();
The client then sends a message by simply:
// Sending a message
Message data = new Message("Hello");
toBuffer.writeObject(data);
Message can be as complex as needed as long as its members implement Serializable interface.

Categories