我必须每隔 x 秒与多台机器建立同步 TCP 套接字连接,以获得状态更新数据包之类的内容。
我使用一个 Callable 线程类,它创建一个连接到每台机器的 future 任务,发送一个查询数据包,并接收一个回复,该回复返回到创建所有可调用对象的主线程。
我的套接字连接类是:
public class ClientConnect implements Callable<String> {
Connection con = null;
Statement st = null;
ResultSet rs = null;
String hostipp, hostnamee;
ClientConnect(String hostname, String hostip) {
hostnamee=hostname;
hostipp = hostip;
}
@Override
public String call() throws Exception {
return GetData();
}
private String GetData() {
Socket so = new Socket();
SocketAddress sa = null;
PrintWriter out = null;
BufferedReader in = null;
try {
sa = new InetSocketAddress(InetAddress.getByName(hostipp), 2223);
} catch (UnknownHostException e1) {
e1.printStackTrace();
}
try {
so.connect(sa, 10000);
out = new PrintWriter(so.getOutputStream(), true);
out.println("\1IDC_UPDATE\1");
in = new BufferedReader(new InputStreamReader(so.getInputStream()));
String [] response = in.readLine().split("\1");
out.close();in.close();so.close(); so = null;
try{
Integer.parseInt(response[2]);
} catch(NumberFormatException e) {
System.out.println("Number format exception");
return hostnamee + "|-1" ;
}
return hostnamee + "|" + response[2];
} catch (IOException e) {
try {
if(out!=null)out.close();
if(in!=null)in.close();
so.close();so = null;
return hostnamee + "|-1" ;
} catch (IOException e1) {
// TODO Auto-generated catch block
return hostnamee + "|-1" ;
}
}
}
}
这就是我在主类中创建线程池的方式:
private void StartThreadPool()
{
ExecutorService pool = Executors.newFixedThreadPool(30);
List<Future<String>> list = new ArrayList<Future<String>>();
for (Map.Entry<String, String> entry : pc_nameip.entrySet())
{
Callable<String> worker = new ClientConnect(entry.getKey(),entry.getValue());
Future<String> submit = pool.submit(worker);
list.add(submit);
}
for (Future<String> future : list) {
try {
String threadresult;
threadresult = future.get();
//........ PROCESS DATA HERE!..........//
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
pc_nameip 映射包含(主机名,hostip)值,并且对于每个条目,我创建一个 ClientConnect 线程对象。
我的问题是,当我的机器列表包含 10 台计算机(其中大多数都不是 Activity 的)时,即使我的超时限制设置为 10 秒,我也会遇到很多超时异常(在 Activity 的计算机中)。
如果我强制列表包含一台正在运行的电脑,我没有问题。 超时是相当随机的,不知道是什么原因造成的。
所有机器都在本地网络中,远程服务器也是由我编写的(用 C/C++),并且在另一个设置中工作了 2 年多,没有任何问题。
我是否遗漏了什么或者可能是操作系统网络限制问题? 我正在 windows xp sp3 上测试此代码。提前致谢!
<小时/> <小时/>更新:
创建两台新的服务器计算机并保留一台出现大量超时的服务器后,我得到以下结果:
For 100 thread runs over 20 minutes :
NEW_SERVER1 : 99 successful connections/ 1 timeouts
NEW_SERVER2 : 94 successful connections/ 6 timeouts
OLD_SERVER : 57 successful connections/ 43 timeouts
其他信息: - 我经历过一次 JRE 崩溃(EXCEPTION_ACCESS_VIOLATION (0xc0000005)),并且必须重新启动应用程序。 - 我注意到,当应用程序运行时,当我浏览互联网时,我的网络连接很困难。我不知道这是否符合预期,但我认为我拥有的 MAX 15 线程并没有那么多。
所以,我所有的旧服务器首先出现了某种问题。不知道那是什么,因为我的新服务器是从相同的操作系统镜像创建的。
其次,虽然超时百分比已经大幅下降,但我仍然认为在像我们这样的小型局域网中出现一次超时是不常见的。但这可能是服务器的应用程序部分问题。
最后,我的观点是,除了旧服务器的问题(我仍然不敢相信我在这方面浪费了这么多时间!),肯定存在服务器应用程序错误或 JDK 相关错误(因为我经历了 JRE 崩溃)。
附:我使用 Eclipse 作为 IDE,我的 JRE 是最新的。
如果以上任何内容给您带来任何启发,请发表评论。 谢谢。
-----编辑-----
难道 PrintWriter 和/或 BufferedReader 实际上并不是线程安全的吗???!!!?
----2013 年 9 月 9 日新编辑----
重新阅读所有评论并感谢@Gray 和他的评论后:
When you run multiple servers does the first couple work and the rest of them timeout? Might be interesting to put a small sleep in your fork loop (like 10 or 100ms) to see if it works that way.
我重新排列了主机/IP 的树列表,并得到了一些非常奇怪的结果。 看起来,如果一个 Activity 的主机被放置在树列表的顶部,从而第一个启动套接字连接,则连接和接收数据包没有任何问题,没有任何延迟或超时。
相反,如果一个 Activity 的主机被放置在列表的底部,前面有几个死亡的主机,那么连接需要很长时间,并且我之前的超时为 10 秒,它无法连接。但在将超时更改为 60 秒后(感谢@EJP),我意识到没有发生超时!
连接时间太长(某些情况下超过 20 秒)。 有什么东西阻塞了新的套接字连接,并不是主机或网络太忙而无法响应。
我这里有一些调试数据,如果你想看一下: http://pastebin.com/2m8jDwKL
最佳答案
您可以在连接到套接字之前简单地检查可用性。有一个答案提供了某种黑客解决方法 https://stackoverflow.com/a/10145643/1809463
Process p1 = java.lang.Runtime.getRuntime().exec("ping -c 1 " + ip);
int returnVal = p1.waitFor();
boolean reachable = (returnVal==0);
作者:jayunit100
它应该可以在 unix 和 windows 上运行,因为 ping 是一个通用程序。
关于Java 线程套接字连接超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18615007/