我的网络应用程序出现超时问题。
使用Java我设计了一个以这种方式工作的客户端 - 服务器prrotocol:
客户端使用套接字tcp连接并连接到服务器
服务器accept()
连接并阻止没有等待请求的读取
客户端请求发送字符串的一些信息或文件"我需要文件X"并阻止直到服务器回答(在发送请求后我刷新我的soketOutputStream
)
服务器将信息或文件发送到客户端(此处服务器将文件长度发送给客户端,以便它可以正常读取)
服务器再次阻止,直到另一个请求到达或客户端完成连接。
我正在使用服务器接受后获得的相同的conenction套接字,我正在使用flush来结束对话。 我能相信flush会让TCP发送所有数据吗?我也尝试过TCP NO DELAY标志,但它只会让时间更糟......那太奇怪......
服务器处理文件请求:
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发送数据包:
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;
}
}
客户端只使用此方法读取数据包:
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;
}
}
这一个
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());
}
修改 手动调试我意识到发生错误时服务器没有退出while循环
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);
}
它永远不会打印FINAL COPY ...当错误发生时,客户端会超时并断开连接并且服务器会损坏管道。 这可能意味着超时很短(15秒),我认为它不是或方法实现错误