I am having some problems with timeouts on my network application.
Using Java I have designed a client-server prrotocol that works this way:
Client uses socket tcp connection and connects to the server
Server
accept()
the connection and blocks no read waiting a requestClient requests some information or file sending a string "I need file X" and blocks until server answer (after sending the request I flush my
soketOutputStream
)Server send the information or file to the client (here the server sends the file length to client so that it can read properly)
Server blocks again until another request arrives or the client finish the connection.
I am using the same conenction socket acquired after server accept and I am using flush to end the conversation. Can I trust flush will make TCP send all data? I also tried TCP NO DELAY flag but it just make timeouts worse...thats wierd...
Server handling file request:
InputStream fin = new FileInputStream(myFile);
Packet r = new Packet("STREAMREQREPLY", (int)myFile.length(), fin);
HashMap<String, String> nh = new HashMap<String, String>();
nh.put("FILE", a);
r.setHeaders(nh);
NetworkHandler.sendPacket(r, clientSocket.getOutputStream());
fin.close();
NetworkHandler send Packet:
public static void sendPacket(Packet p, OutputStream os) throws Exception
{
synchronized(os)
{
String header = "<";
if(p.getHeaders() != null && !p.getHeaders().isEmpty())
{
Iterator<Entry<String, String>> it = p.getHeaders().entrySet().iterator();
while(it.hasNext())
{
Entry<String, String> e = it.next();
if(e.getKey() != null)
{
if(e.getKey().contains(":") || e.getKey().contains("<") || e.getKey().contains(">"))
throw new IllegalArgumentException("Parametros no header não devem conter '<' nem '>' nem ':'");
}
if(e.getValue() != null)
{
if(e.getValue().contains(":") || e.getValue().contains("<") || e.getValue().contains(">"))
throw new IllegalArgumentException("Parametros no header não devem conter '<' nem '>' nem ':'");
}
header += e.getKey()+":"+e.getValue()+":";
}
header = header.substring(0, header.length()-1)+">";
}
else
header += ">";
String cp = "TYPE: "+p.getType()+",DATASIZE: "+p.getDataSize()+header;
os.write(cp.getBytes("ISO-8859-1"));
if(p.getDataSize() > 0)
{
if(p.getInputStream() == null)
throw new IllegalArgumentException("No input Stream");
IOUtil.copyInputToOutput(p.getInputStream(), os, p.getDataSize());
}
os.flush();
}
}
IOUtil:
public static void copyInputToOutput(InputStream in, OutputStream out, int size) throws Exception
{
byte[] buf = new byte[8192];
int total = 0;
int read = 0;
while(total < size)
{
if(size-total >= buf.length)
read = in.read(buf, 0, buf.length);
else
read = in.read(buf, 0, size-total);
out.write(buf, 0, read);
total += read;
}
}
The client side just use this method to read the packet:
public static Packet readPacket(InputStream is) throws Exception
{
synchronized(is)
{
ByteArrayOutputStream ba = new ByteArrayOutputStream(1024);
int ch;
//Le o tipo do pacote
while((ch = is.read()) != 44)
{
if(ch == -1)
throw new IOException("Conecção finalizada");
ba.write(ch);
}
String type = ba.toString("ISO-8859-1");
if(!type.startsWith("TYPE: "))
throw new IllegalArgumentException("Erro ao ler tipo, esperado 'TYPE: ' lido: '"+type+"'");
Packet p = new Packet(type.replace("TYPE: ", ""));
//Le o tamanho dos dados
ba.reset();
while((ch = is.read()) != 60)
{
if(ch == -1)
throw new IOException("Conecção finalizada");
ba.write(ch);
}
String dataSize = ba.toString("ISO-8859-1");
if(!dataSize.startsWith("DATASIZE: "))
throw new IllegalArgumentException("Erro ao ler tipo, esperado 'DATASIZE: ' lido: '"+dataSize+"'");
p.setDataSize( Integer.parseInt(dataSize.replace("DATASIZE: ", "")) );
//Tenta ver se existe algum parametro no header
ba.reset();
int hRead = 0;
while((ch = is.read()) != 62)
{
if(ch == -1)
throw new IOException("Conecção finalizada");
hRead++;
ba.write(ch);
}
if(hRead > 2)
{
HashMap<String, String> map = new HashMap<String, String>();
String header = ba.toString("ISO-8859-1").replace("<", "").replace(">", "");
String[] he = header.split(":");
if(he.length%2 != 0)
throw new IllegalArgumentException("Header inválido: "+header);
for (int i = 0; i < he.length; i=i+2)
{
map.put(he[i], he[i+1]);
}
p.setHeaders(map);
}
//Seta os dados no InputStream
p.setInputStream(is);
return p;
}
}
And this one
public ByteArrayInputStream getAudioStream(String host, int port, String client, String filial, String code,
String style, String audio) throws Exception
{
Socket s = connect(host, port);
HashMap<String, String> map = new HashMap<String, String>();
//some data in the map
Packet p = new Packet("STREAMREQ");
p.setHeaders(map);
NetworkHandler.sendPacket(p, s.getOutputStream());
Packet r = NetworkHandler.readPacket(s.getInputStream());
if(r.getType().equals("ERROR"))
throw new RuntimeException("Erro: "+r.getHeaders().get("ERROR"));
ByteArrayInputStream bi = IOUtil.toByteArrayInputStream(r.getInputStream(), r.getDataSize());
return bi;
}
IOUtil
public static ByteArrayInputStream toByteArrayInputStream(InputStream is, int size) throws Exception
{
ByteArrayOutputStream bout = new ByteArrayOutputStream();
copyInputToOutput(is, bout, size);
return new ByteArrayInputStream(bout.toByteArray());
}
EDIT: Debuggin manually I realized that the server does not get out of the while loop when the error occurs
public static void copyInputToOutput(InputStream in, OutputStream out, int size) throws Exception
{
byte[] buf = new byte[8192];
int total = 0;
int read = 0;
while(total < size)
{
if(size-total >= buf.length)
read = in.read(buf, 0, buf.length);
else
read = in.read(buf, 0, size-total);
out.write(buf, 0, read);
total += read;
System.out.println("TOTAL: "+total);
System.out.println("SIZE: "+size);
}
System.out.println("FINAL COPY INPUT TO OUTPUT: "+total);
}
It never prints FINAL COPY...when the error occurs, then the client gets a timeout and disconnect and the server get a broken pipe. This could mean that the timeout is low (15 seconds) which I dont think it is or the method implementation is wrong