I am using java sockets for communication. On the client side I have some processing and at this point I send an object to the cient. The code is as follows:
while (true) {
try {
Socket server = new Socket("localhost", 3000);
OutputStream os = server.getOutputStream();
InputStream is = server.getInputStream();
CommMessage commMessage = new CommMessageImpl();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(commMessage);
os.write(bos.toByteArray());
os.flush();
byte[] buff = new byte[512];
int bytesRead = 0;
ByteArrayOutputStream receivedObject = new ByteArrayOutputStream();
while ((bytesRead = is.read(buff)) > -1) {
receivedObject.write(buff, 0, bytesRead);
System.out.println(receivedObject);
}
os.close();
Thread.sleep(10000);
} catch (IOException e) {
} catch (InterruptedException e) {
}
}
Next on the server side I have the following code to read the object and write the response (Which is just an echo message)
public void startServer() {
Socket client = null;
try {
server = new ServerSocket(3000);
logger.log(Level.INFO, "Waiting for connections.");
client = server.accept();
logger.log(Level.INFO, "Accepted a connection from: " + client.getInetAddress());
os = new ObjectOutputStream(client.getOutputStream());
is = new ObjectInputStream(client.getInputStream());
// Read contents of the stream and store it into a byte array.
byte[] buff = new byte[512];
int bytesRead = 0;
ByteArrayOutputStream receivedObject = new ByteArrayOutputStream();
while ((bytesRead = is.read(buff)) > -1) {
receivedObject.write(buff, 0, bytesRead);
}
// Check if received stream is CommMessage or not contents.
CommMessage commMessage = getCommMessage(receivedObject);
if (commMessage != null) {
commMessage.setSessionState(this.sessionManager.getState().getState());
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(commMessage);
os.write(bos.toByteArray());
System.out.println(commMessage.getCommMessageType());
} else {
processData(receivedObject, this.sessionManager);
}
os.flush();
} catch (IOException e) {
} finally {
try {
is.close();
os.close();
client.close();
server.close();
} catch (IOException e) {
}
}
}
The above code works ok if I do not try to read data on the client side (If i exclude the code related to reading). But if I have that code, for some reason, I get some kind of deadlock when accessing input streams. Any ideas what I might have done wrong? Thanks in advance.
Both client and server are attempting to read the whole input stream (i.e. everything up to EOF) but neither is sending an EOF (by calling shutdownOutput() on the socket.)
Why do you need to store the object data temporarily in a ByteArrayOutputStream? This would probably be easier to fix if you read directly from the socket input stream.
Related
I am currently working on a file transfer program and I ran into a strange issue.
I have two classes: A sender class and a recipient class. You can read the source code along with the error message below.
Thanks for your help in advance.
Sender:
public static void sendFile(final File file, final String ip){
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
Socket s = new Socket(ip, 4816);
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
DataInputStream dis = new DataInputStream(s.getInputStream());
FileInputStream fis = new FileInputStream(file);
String filename = file.getName();
if(!dis.readUTF().equals("end")){
dos.writeUTF(filename);
dos.flush();
long size = file.length();
byte[] b = new byte[1024];
int read;
dos.writeUTF(Long.toString(size));
dos.flush();
while((read = fis.read(b)) != -1){
dos.write(b, 0, read);
dos.flush();
}
fis.close();
dos.flush();
dos.writeUTF("end");
System.out.println("Sender: Done");
dos.flush();
dis.close();
dos.close();
s.close();
}
return;
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.start();
}
Recipient:
private ServerSocket sock;
private Thread t;
public listener(){
try {
sock = new ServerSocket(4816);
listen();
} catch (IOException e) {
e.printStackTrace();
}
}
private void listen(){
t = new Thread(new Runnable() {
public void run() {
Socket s;
try {
while((s = sock.accept()) != null){
DataInputStream dis = new DataInputStream(s.getInputStream());
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
String filename = dis.readUTF();
long size = Long.valueOf(dis.readUTF());
byte[] b = new byte[1024];
FileOutputStream fos = new FileOutputStream(new File(filename), true);
long read;
do{
read = dis.read(b, 0, b.length);
fos.write(b, 0, b.length);
}while(!(read < 1024));
System.out.println("Recipient: Done");
fos.close();
dos.close();
dis.close();
s.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
t.run();
}
Error (After 1086464 out of 1513308160 bytes were transmitted. [1062 * 1024]):
java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(Unknown Source)
at java.net.SocketOutputStream.write(Unknown Source)
at java.io.DataOutputStream.write(Unknown Source)
at main.sender$1.run(sender.java:60)
at java.lang.Thread.run(Unknown Source)
I think the issue is here in the recipient..
do{
read = dis.read(b, 0, b.length);
fos.write(b, 0, b.length);
}while(!(read < 1024));
You are saying to only loop while read is not less than 1024. Any read() operation can return a value less than the maximum buffer length at any time, even if the stream is not "at the end". Especially when network sockets are involved. The number of read bytes may be greater than 0 but less than 1024 on any read because that's simply how many bytes are available to the stream at that time.
The read call is giving you all the data it has (which fits the buffer) at that time, without having to block.
Try changing it to..
int read;
while ((read = dis.read(b, 0, b.length)) != -1 ) {
fos.write(b, 0, read);
}
You had this kind of loop in the sender and it is correct (although you don't need the flush within the loop).
This kind of bug is more common than you might think. I'v seen it a lot over the years, even in "enterprise products". It doesn't get picked up and fixed because most of the time it works... until it doesn't.
Another issue above is that you were always writing b.length bytes to the file, regardless of how many bytes had actually been read into the buffer.
I'm having some difficulties trying to send file from server to the other client.
Let's say I have two clients connected to the server.
Now Server successfully sent the file to the 1st client But then when the server tries to send to the second client the client does not receive it.
Here's some code to begin with :
Server:
public synchronized void sendToAllClients() {
for (Socket z : clientSockets) {
if (z != null) {
System.out.println("TEST");
PrintWriter print = null;
try {
File myFile = new File(FILE PATH);
byte[] mybytearray = new byte[(int) myFile.length()];
FileInputStream fis = new FileInputStream(myFile);
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis);
dis.readFully(mybytearray, 0, mybytearray.length);
OutputStream os = z.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeLong(mybytearray.length);
dos.write(mybytearray, 0, mybytearray.length);
dos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Client:
public static void receiveFile(String fileName) {
try {
int bytesRead;
InputStream in = sock.getInputStream();
clientData = new DataInputStream(in);
OutputStream output = new FileOutputStream(
(FILEPATH + FILENAME);
long size = clientData.readLong();
byte[] buffer = new byte[1024];
while (size > 0
&& (bytesRead = clientData.read(buffer, 0,
(int) Math.min(buffer.length, size))) != -1) {
output.write(buffer, 0, bytesRead);
size -= bytesRead;
}
output.flush();
} catch (IOException ex) {
}
}
I am having trouble with my client/server program. The server is supposed to get a file from the client then convert it to xml, and then stream the xml file back to the client.
As of now everything works up to the point of streaming the xml file back to the client. I can successfully send my file to the server and convert it to XML.
The server does not send anything to the client. The client is stuck in the area of reading bytes in from the InputStream. On line 57 in the Client class.
public class Server {
private ServerSocket serverSocket;
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
public static void main(String[] args) {
int port = 8081;
Server srv = new Server(port);
}
public Server(int portNumber) {
try {
serverSocket = new ServerSocket(portNumber);
clientSocket = serverSocket.accept();
out = new PrintWriter(clientSocket.getOutputStream(), true);
//in = new BufferedReader(
//new InputStreamReader(clientSocket.getInputStream()));
byte[] myArray = new byte[22000]; // should be file size
InputStream is = clientSocket.getInputStream();
FileOutputStream fos = new FileOutputStream("file.csv");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int bytesRead;
int current = 0;
bytesRead = is.read(myArray, 0, myArray.length);
current = bytesRead;
do {
bytesRead = is.read(myArray, current, (myArray.length - current) );
if (bytesRead >= 0)
current += bytesRead;
} while (bytesRead > -1);
bos.write(myArray, 0, myArray.length);
bos.flush();
boolean flag = false;
System.out.println("Server: finished receiving file");
XMLWriter xmlWrite = new XMLWriter();
xmlWrite.createXmlDocument("file_copy.csv");
sendXML("server_file.XML");
clientSocket.close();
System.out.println("Server: disconnected with client");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void sendXML(String fileName) {
System.out.println("Sending file");
File file = new File(fileName);
BufferedInputStream bis;
try {
FileInputStream fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
DataInputStream dataIn = new DataInputStream( bis );
OutputStream outStream = clientSocket.getOutputStream();
int length;
try {
length = dataIn.readInt();
System.out.println("S: " + dataIn.readInt());
byte[] data = new byte[ length ];
dataIn.readFully(data, 0, data.length);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
public class Client {
private Socket mainSocket;
public static void main(String[] args) {
int port = 8081;
Client cli = new Client(port);
}
public Client(int portNumber) {
try {
mainSocket = new Socket("localhost", portNumber);
// send file to server
File file = new File("Passengers.csv");
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
OutputStream outStream = mainSocket.getOutputStream();
byte[] myBytes = new byte[(int) file.length()];
bis.read(myBytes, 0, myBytes.length);
outStream.write(myBytes, 0, myBytes.length);
outStream.flush();
System.out.println("Client: Done sending file");
byte[] fileData = new byte[30000];
InputStream is = mainSocket.getInputStream();
FileOutputStream fos = new FileOutputStream("client_file.XML");
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream dataOut = new DataOutputStream( bos );
dataOut.writeInt( fileData.length );
dataOut.write( fileData );
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
This loop will run until the stream is closed, not until the client finishes sending data.
do {
bytesRead = is.read(myArray, current, (myArray.length - current) );
if (bytesRead >= 0)
current += bytesRead;
} while (bytesRead > -1);
This is because InputStream.read will block until at least one byte of data is available or the stream is closed, causing a -1 to be returned. Since the client is done sending data but the stream is still open it will block forever.
A solution is to first send the length of the file in the first 4 bytes using something like DataOutputStream/DataInputStream, and then have the client send exactly that many bytes and the server read exactly that many bytes before moving on.
Client:
// Connect to the server and read in file data
byte[] fileData = ...;
DataOutputStream dataOut = new DataOutputStream( outStream );
dataOut.writeInt( fileData.length );
dataOut.write( fileData );
// Get reply from server
Server:
// Create ServerSocket and get Client connection
DataInputStream dataIn = new DataInputStream( is );
int length = dataIn.readInt();
byte[] data = new byte[ length ];
dataIn.readFully( data );
// Continue to process client connection
The general idea behind this kind of network communication is that every block of data, or packet, is prefixed with it's length before being send. This means that the recipient of the data can read in that number and know exactly how many bytes remain to form the complete block or packet. Communication goes along something like this for both sides of the connection. The only difference is in how you process the data.
public byte[] readPacket( DataInputStream dataIn ) throws IOException {
int length = dataIn.readInt();
byte[] packet = new byte[ length ];
dataIn.readFully( packet );
return packet;
}
public void writePacket( DataOutputStream dataOut, byte[] packet ) throws IOException {
dataOut.writeInt( packet.length );
dataOut.write( packet );
}
Only you would call these from some sort of loop preferably on background threads but it's not required for what you want to do. For your case you want to read in the file on the client side into a byte[] and then use writePacket to send it to the server. On the server side you would use readPacket to read in the file from the client. The same thing takes place when sending data back to the client but with the roles switched.
I'm trying to send my image from android client to Java server. Size of image that i'm sending is about 99kb, but server always reads a few kb less, sometimes 98, sometimes 96 and so on. I'd like to know why that data is lost and how can I send image in a proper way. Please help :)
Code:
Client(sending image):
public void sendImage(File file){
try {
out = new PrintWriter(socket.getOutputStream(),true);
out.println("Image");
out.println(file.length());
byte[] byteArray = new byte[(int) file.length()];
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read(byteArray,0,byteArray.length);
OutputStream os = socket.getOutputStream();
FilterOutputStream bos = new FilterOutputStream(os);
bos.write(byteArray,0,byteArray.length);
bos.flush();
os.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Server(receiving image):
if(input.equals("Image")){
input = in.readLine();
int fileSize = Integer.parseInt(input);
System.out.println("FILESIZE:" +fileSize);
byte[] byteArray = new byte[fileSize];
FileOutputStream fileOutputStream =
new FileOutputStream("filename.jpg");
BufferedOutputStream bos =
new BufferedOutputStream(fileOutputStream);
BufferedInputStream bis = new BufferedInputStream(in_);
int bytesRead = bis.read(byteArray, 0, byteArray.length);
int current = bytesRead;
do {
bytesRead = bis.read(byteArray, current,
(byteArray.length - current));
if (bytesRead >= 0) {
current += bytesRead;
System.out.println(current);
}
} while (bytesRead != -1);
bos.write(byteArray, 0, current);
bos.flush();
bos.close();
}
EDIT
Problem solved, working code is as follows:
Client side:
public void sendImage(File file){
try {
DataOutputStream out = new DataOutputStream(
socket.getOutputStream());
out.writeChar('I');
DataInputStream dis = new DataInputStream(new FileInputStream(file));
ByteArrayOutputStream ao = new ByteArrayOutputStream();
int read = 0;
byte[] buf = new byte[1024];
while ((read = dis.read(buf)) > -1) {
ao.write(buf, 0, read);
}
out.writeLong(ao.size());
out.write(ao.toByteArray());
out.flush();
out.close();
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Server side:
if(input =='I'){
DataInputStream dis = new DataInputStream(clientSocket.getInputStream());
long length = dis.readLong();
File to = new File("filename.jpg");
DataOutputStream dos = new DataOutputStream(
new FileOutputStream(to));
byte[] buffer = new byte[1024];
int len, current = 0;
System.out.println(length);
while ( current != length) {
len = dis.read(buffer);
dos.write(buffer, 0, len);
current += len;
System.out.println(current);
}
dis.close();
dos.close();
}
From my personal experience PrintWriter and Buffers dont work well together..
As buffers trying to read data before you tell it to it can "steal" data that it should not do. For example if you use any kind of buffered reader to read the input on the server side that buffer will steal some parts at the "start" of the incomming image becuase it think's it's just another line. You could always try using DataInputStream and DataOutputStream instead..
Client:
public void sendImage(File file) {
try {
DataOutputStream out = new DataOutputStream(
socket.getOutputStream());
out.writeChar('I'); // as image,
DataInputStream dis = new DataInputStream(new FileInputStream(file));
ByteArrayOutputStream ao = new ByteArrayOutputStream();
int read = 0;
byte[] buf = new byte[1024];
while ((read = dis.read(buf)) > -1) {
ao.write(buf, 0, read);
}
out.writeLong(ao.size());
out.write(ao.toByteArray());
out.flush();
out.close();
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Server:
// assuming folder structure exists.
public void readImage(Socket s, File to) throws IOException {
DataInputStream dis = new DataInputStream(s.getInputStream());
char c = dis.readChar();
if (c == 'I') {
long length = dis.readLong();
DataOutputStream dos = new DataOutputStream(
new FileOutputStream(to));
byte[] buffer = new byte[1024];
int len;
while ((len = dis.read(buffer)) != -1) {
dos.write(buffer, 0, len);
}
dis.close();
dos.close();
}
}
As a starting point, in the client side, you will also need a loop for reading the local image, because are you sure that...
bis.read(byteArray,0,byteArray.length);
... is really reading the whole image? So you will also need a loop as in the server side.
I am trying to build a file transfer mechanism between 2 Java socket client. The sender client would include this sorta snippet:
FileInputStream fis = null;
BufferedInputStream bis = null;
BufferedOutputStream outStream = null;
byte[] fileBytes = new byte[(int) file.length()];
int bytesRead = 0;
try {
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
outStream = new BufferedOutputStream(socket.getOutputStream());
bytesRead = bis.read(fileBytes, 0, fileBytes.length);
outStream.write(fileBytes, 0, fileBytes.length);
} catch (IOException _IOExc) {
Logger.getLogger(ChatClient.class.getName()).log(Level.SEVERE,
null, _IOExc);
//QuitConnection(QUIT_TYPE_DEFAULT);
}
The server mediator would look like:
public void run() {
assert (outSocket != null);
byte[] bytes = new byte[fileSize];
try {
System.out.println("inStream " + inStream.available());
outStream = new BufferedOutputStream(outSocket.getOutputStream());
inStream.read(bytes, 0, fileSize);
outStream.write(bytes, 0, fileSize);
outStream.flush();
} catch (IOException ex) {
Logger.getLogger(FileTransport.class.getName()).log(Level.SEVERE,
null, ex);
}
}
the destination client:
public void run() {
try {
System.out.println("Start reading...");
int len = 1024;
BufferedInputStream inStream = new BufferedInputStream
(client.user.getClientSocket().getInputStream());
while ((bytesRead = inStream.read(fileBytes, 0, len)) >
0 && current < fileSize) {
current = current + bytesRead;
System.out.println("current "+ current);
bos.write(fileBytes, 0, bytesRead < len ? bytesRead : len);
}
bos.flush();
bos.close();
} catch (IOException ex) {
Logger.getLogger(ReadFileThread.class.getName()).log(Level.SEVERE,
null, ex);
} catch (InterruptedException e) {
}
}
Both the server and destination client is passed "fileSize" in advance, the problem now is server side get slight less data and the clientB keep reading only 8192 bytes of data from server and can never get out the loop.
Many thanks
Kev
Don't ignore the result of the read() method. It returns the number of bytes that have been read, which is not necessarily the length of the file. read() must always be called in a loop, until it returns -1.
And don't, ever, use available(). It doesn't return what you think it returns. Just loop until read() returns -1 or until the number of read bytes reaches the expected count.
Read the IO tutorial.