Reputation: 413
I write client-server application. Client will run on android and server on plain java. I need to exchange data from client to server and then from server to client. My proble is that when I send file from client to server the server stuck on receiving data from socket. Here is my code:
client send file:
Log.i("======", "sendToServer==============");
OutputStream output = sk.getOutputStream();
String pathToOurFile = directory + File.separator + file;
FileInputStream fileInputStream = new FileInputStream(pathToOurFile);
byte[] buffer = new byte[sk.getSendBufferSize()];
int bytesRead = 0;
while((bytesRead = fileInputStream.read(buffer))>0)
{
output.write(buffer,0,bytesRead);
}
fileInputStream.close();
server get the data:
System.out.println("I get data");
File file=null;
InputStream input = sk.getInputStream();
file = new File("C://protocolFIle/" + "temp.xml");
FileOutputStream out = new FileOutputStream(file);
byte[] buffer = new byte[sk.getReceiveBufferSize()];
int bytesReceived = 0;
while((bytesReceived = input.read(buffer))>0) {
out.write(buffer,0,bytesReceived);
}
return file;
server stuck on line
while((bytesReceived = input.read(buffer))>0) {
out.write(buffer,0,bytesReceived);
}
on server side. I think I need to signal somehow in client side when I finish send data. I know I can write "output.close()" on client side and then server get file and break while loop but I need this output in future. Do anyone know how can I send this file that server dont stuck? Maybe I need to send this file in another way. Thanks for any help.
Upvotes: 1
Views: 251
Reputation: 10993
Using close()
on the client side won't actually be much help, because the TCP/IP API methods such as close()
are pretty much useless as a means of signalling an end of message at application layer. The reason for this is that closing the TCP stream at the client end does not immediately close down the entire TCP connection right through to the opposite client. This is all to do with the underlying architecture of TCP.
What you need to do is implement an application protocol level means of identifying the length of the message, or otherwise some other means of message 'framing'. The easiest thing for you to do might be to implement a basic header at the start of your message, which could simply consist of nothing more than four bytes that represent an U32 message length (just for example).
A massively important thing that I would stress again about TCP is that the TCP protocol itself, and notably calls like close()
, do not provide means of reliably signalling the start / stop of your application message that you're sending over TCP. TCP must be thought of as a stream protocol, because that's exactly what it is; therefore, you must also place into that stream some special header with a length field, or framing characters, or some other means of indicating when the receive end has read enough out of the .read()
call such that it has a complete message.
I have recently created an Android application that talks to a server C# application using TCP sockets, and even though I am only sending simple and relatively short messages over the stream, I found that implementing very basic message framing and message header was absolutely essential.
Signalling the length or end of a message should be done at application protocol level.
Also, signalling that a TCP connection should be closed by both ends should also be done at application protocol level. When you search StackOverflow you often come across many people experiencing problems with detecting whether a TCP connection 'has been closed'. The problem is that this is an inherently wrong way to go about things; a TCP sockets API in any language simply cannot reliably tell you that a stream has been closed.
If you look at HTTP, which is an example of a popular protocol using TCP sockets, you will see that this protocol makes use of headers that indicate length, and flags that indicate a connection shall close, and whether a message is fragmented, and so on.
Going back to your particular problem, what you should do in your first write()
at the Android side is construct and write a simple message header that contains a length field. At the server side, the first read()
should expect to see this header prior to the start of the file payload. The length provided in that header is then used to determine when you have read()
all of the file.
You could later expand your header to contain things like a CRC value.
Upvotes: 2