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();
Related
I'm trying to make a video file transfer but am having problems getting the server to start sending bytes.
The first step is for the client to connect, the socket gets accepted. Then the client sends the video file name but the server never reads this.
This is the code for the server up until it blocks:
try(ServerSocket serverSocket = new ServerSocket(4005))
{
Socket socket = serverSocket.accept();
System.out.println("accepted");
OutputStream os = socket.getOutputStream();
BufferedReader receiveReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("This gets printed");
String request = receiveReader.readLine();//never passes this line
System.out.println("This doesn't get printed");
and this is the client up until it blocks waiting for the server to send the video bytes:
try(Socket socket = new Socket(IPAddress, 4005))
{
byte[] messageBytes = new byte[10000];
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
outputStream.writeBytes("REQUEST;"+videoPath);//This is the line that should send the bytes for the server to read, so it won't block.
String home = System.getProperty("user.home");
String path = home+"\\Downloads" + videoName;
path = path.trim();
FileOutputStream fos = new FileOutputStream(path);
BufferedOutputStream bos = new BufferedOutputStream(fos);
InputStream is = socket.getInputStream();
int bytesRead = 0;
System.out.println("Downloading file...");
while((bytesRead = is.read(messageBytes))!=-1)//This blocks here
Why on earth isn't the server reading the "Request" + videoPath bytes that the server is sending? I tried outputStream.flush() as well, no luck.
Usual problem. You're reading a line but you aren't writing a line. Add a line terminator to the sent message.
When you fix this you will then discover that you can't mix buffered streams and readers on the same socket. I suggest you do all the I/O via the DataInput/OutputStream classes, using read/writeUTF() for the name.
If you're sending multiple files see my answer there.
EDIT:
I have changed the code so that the integrity of the file is checked after the transfer.
But now when I put a breakpoint in the client at dos.write(buffer, 0, count), kill the server, and then resume client code execution, it hangs at serverMD5[i] = dataInputStream.readByte() indefinitely.
Even though the user now knows that the transfer was not successful (the application hangs and needs to be restarted), once again this is not doing what I expected it to do (throw an IOException).
Original post with changed code:
I have created an android client that connects to a server using SSLSocket and sends some data.
Here is the relevant client and server code
Client:
try {
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket();
sslsocket.connect(new InetSocketAddress(SERVER_IP, UPLOAD_PORT), 2000);
OutputStream outputStream = sslsocket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeInt(DEVICE_ID);
dataOutputStream.writeLong(FILE_LENGTH);
MessageDigest md = MessageDigest.getInstance("MD5");
DigestOutputStream dos = new DigestOutputStream(outputStream, md);
InputStream readingsInputStream = new FileInputStream(FILE_NAME);
int count;
byte[] buffer = new byte[10 * 1024];
while ((count = readingsInputStream.read(buffer)) > 0) {
dos.write(buffer, 0, count);
}
readingsInputStream.close();
byte[] md5 = md.digest();
byte[] serverMD5 = new byte[16];
DataInputStream dataInputStream = new DataInputStream(sslsocket.getInputStream());
for (int i = 0;i<16;i++) {
serverMD5[i] = dataInputStream.readByte();
if (md5[i] != serverMD5[i]) throw new Exception("MD5 mismatch");
}
sslsocket.close();
} catch (Exception e) {
...
}
Server:
try {
SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
InputStream inputStream = sslSocket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
int deviceID = dataInputStream.readInt();
long fileLength = dataInputStream.readLong();
MessageDigest md = MessageDigest.getInstance("MD5");
DigestInputStream dis = new DigestInputStream(inputStream, md);
OutputStream readingsOutputStream = new FileOutputStream("Device"+deviceID+".txt", false);
int count;
byte[] buffer = new byte[1];
do {
count = dis.read(buffer);
readingsOutputStream.write(buffer, 0, count);
fileLength -= count;
} while (fileLength > 0);
readingsOutputStream.close();
byte[] md5 = md.digest();
DataOutputStream md5OutputStream = new DataOutputStream(sslSocket.getOutputStream());
for (int i = 0;i<16;i++) md5OutputStream.writeByte(md5[i]);
sslSocket.close();
} catch (Exception e) {
...
}
Normally this all works as expected but the problem occurs when I put a breakpoint in the client at the line dos.write(buffer, 0, count) and then kill the server upon reaching the breakpoint.
After continuing code execution on the client, it doesn't throw an exception and just goes through the rest of this code block, leading me to believe that the file was successfully written to the server.
Of course this is not the case since the server was shutdown before the OutputStream was written to. This results in an empty DeviceX.txt (X being the number of the device) file on the server.
This is a big problem since the user might think that the data was successfully transferred and delete it from the device (the data that is sent gets deleted at some point after due to the nature of the application).
Since I have managed to produce this bug I figure it is a possibility that it will happen in a real-world scenario as well. This is my first time working with sockets and I am at a loss as to what to do to fix this issue.
Also if anyone notices anything else that could go wrong with this code block (another scenario where the result is not as expected but an exception isn't thrown) please let me know.
This is normal operation of TCP. Leaving SSL aside, your sends are buffered in the socket send buffer, and transmitted asynchronously after the send() function has returned. It is therefore impossible for the send() function to detect a peer outage immediately. If you keep sending, TCP's retries of the pending data will eventually fail and cause a subsequent send to fail, in the case of Java with an IOException: connection reset.
inputStream.read(deviceIDbuffer);
You can't assume that read() fills the buffer. You should use DataInputStream.readInt() here.
Looks like you need two way communication between your client and server. when the client has completed the upload you could get the client to send an end of transmission (ascii 0x4) character (or any other character(s) that your client/server deems to be a "special" sequence). When the server has received an end of transition character the server could reply with the number of bytes it got. On the client side, wait for the response and if a timeout is reached, tell the user that something went wrong.
Hi I have created a server socket for reading byte array from socket using getInputStream, But getInputStream.read is not exiting after endof data reaches. Below is my code.
class imageReciver extends Thread {
private ServerSocket serverSocket;
InputStream in;
public imageReciver(int port) throws IOException
{
serverSocket = new ServerSocket(port);
}
public void run()
{
Socket server = null;
server = serverSocket.accept();
in = server.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte buffer[] = new byte[1024];
while(true){
int s = 0;
s = in.read(buffer); //Not exiting from here
if(s<0) break;
baos.write(buffer, 0, s);
}
server.close();
return;
}
}
From the client if I sent 2048 bytes, the line in.read(buffer) should return -1 after reading two times, but it waiting there to read for the third time. How can I solve this ?
Thanks in advance....
Your server will need to close the connection, basically. If you're trying to send multiple "messages" over the same connection, you'll need some way to indicate the size/end of a message - e.g. length-prefixing or using a message delimiter. Remember that you're using a stream protocol - the abstraction is just that this is a stream of data; it's up to you to break it up as you see fit.
See the "network packets" in Marc Gravell's IO blog post for more information.
EDIT: Now that we know that you have an expected length, you probably want something like this:
int remainingBytes = expectedBytes;
while (remainingBytes > 0) {
int bytesRead = in.read(buffer, 0, Math.min(buffer.length, remainingBytes));
if (bytesRead < 0) {
throw new IOException("Unexpected end of data");
}
baos.write(buffer, 0, bytesRead);
remainingBytes -= bytesRead;
}
Note that this will also avoid overreading, i.e. if the server starts sending the next bit of data, we won't read into that.
If I send 2048 bytes, the line 'in.read(buffer)' should return -1 after reading two times.
You are mistaken on at least two counts here. If you send 2048 bytes, the line 'in.read(buffer)' should execute an indeterminate number of times, to read a total of 2048 bytes, and then block. It should only return -1 when the peer has closed the connection.
inFromClientR.readLine() never stops. any ideas? Am I forgetting something?
Server:
/*{ some code:
send a file with a dataoutputstream to client using a new port(4000) and when transfer is done i want a responce message (e.g. OK) send back to server in the old port(6000)
}*/
ServerSocket listenTransferSocket = new ServerSocket(6000);
Socket connectionTransferSocket = listenTransferSocket.accept();
BufferedReader inFromClientR =
new BufferedReader(new InputStreamReader(connectionTransferSocket.getInputStream()));
System.out.println("Client's response to Transfer: " +inFromClientR.readLine());
Client:
/*{ some code:
receive the file on port (4000) and then the responce is sent to server using the following commands
}*/
Socket fileTransferSocket = new Socket("localhost", 6000);
DataOutputStream outToServerR =
new DataOutputStream(fileTransferSocket.getOutputStream());
outToServerR.writeBytes("Transfer completed " +'\n');
BufferedReader#readLine() tries to fill its buffer with 8192 bytes, regradless of any linefeeds it find meanwhile. Since you have the connection open, the receiving side will wait until 1) you have sent 8192 bytes, or 2) closes the connection.
You would be better off using some other framing mechanism, maybe an ObjectOutputStream/ObjectInputStream.
String line = null;
while ((line = inFromClientR.readLine()) != null) {
// do sth
}
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.