Java线程套接字连接超时



为了获得类似状态更新数据包的东西,我必须每x秒向多台机器同时进行一次tcp套接字连接。

我使用一个可调用线程类,它创建一个连接到每台机器的未来任务,发送一个查询包,并接收一个返回到创建所有可调用对象的主线程的回复。

我的套接字连接类别是:

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_UPDATE1");
            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映射包含(hostname,hostip)值,我为每个条目创建一个ClientConnect线程对象。

我的问题是,当我的机器列表包含10台电脑(其中大多数都不活动)时,即使我的超时限制设置为10秒,我也会收到很多超时异常(在活动电脑中)。

如果我强制列表包含一台正在工作的电脑,我没有问题。超时是随机的,不知道是什么原因造成的。

所有机器都在本地网络中,远程服务器由我自己编写(用C/C++编写),并在另一个设置中工作了2年多,没有任何问题。

我是遗漏了什么,还是可能是操作系统网络限制问题?我正在windowsxpsp3上测试这段代码。提前感谢!



更新:

在创建了两台新的服务器机器,并保留了一台经常超时的服务器机器之后,我得到了以下结果:

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)),不得不重新启动应用程序。-我注意到,当应用程序运行时,当我浏览互联网时,我的网络连接很困难。我不知道这是否是意料之中的事,但我认为我在MAX15线程上的表现并不多。

所以,我的一些旧服务器出现了某种问题。不知道那是什么,因为我的新服务器是从同一个操作系统映像创建的。

其次,尽管超时百分比急剧下降,但我仍然认为,在像我们这样的小型局域网中,即使一次超时也很少见。但这可能是服务器应用程序部分的问题。

最后,我的观点是,除了旧服务器的问题(我仍然无法相信我在这方面浪费了这么多时间!)之外,肯定存在服务器应用程序错误或JDK相关错误(因为我经历了JRE崩溃)。

p.s.我使用Eclipse作为IDE,我的JRE是最新的。

如果以上任何一个给你敲响了警钟,请发表评论。非常感谢。

-----编辑-----

是否可能PrintWriter和/或BufferedReader实际上不是线程安全的

----新版2013年9月9日----

重读所有评论后,感谢@Gray和他的评论:

当您运行多个服务器时,前两个是否工作,其余的是否超时?在你的叉子循环中睡一个小觉(比如10或100毫秒),看看是否能起作用,这可能很有趣。

我重新排列了主机/ip的树列表,得到了一些非常奇怪的结果。看起来,如果一个活动主机被放在树列表的顶部,从而成为第一个启动套接字连接的主机,那么在没有任何延迟或超时的情况下连接和接收数据包就没有问题。

相反,如果一个活动主机被放在列表的底部,前面有几个死主机,那么连接会花费太长时间,而我之前的超时时间是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);

由jayinit100

它应该在unix和windows上运行,因为ping是一个常见的程序。

我的问题是,当我的机器列表包含10台电脑(其中大多数都不活动)时,即使我的超时限制设置为10秒,我也会收到很多超时异常(在活动电脑中)。

因此,据我所知,如果你的地图上有(例如)10台电脑,其中1台还活着,另外9台不在线,那么所有10个连接都会超时。如果你只是把1台活着的电脑放在地图上,它会显示得很好。

这指向某种并发问题,但我看不出来。我本以为有某种共享数据没有被锁定或其他什么。我看到您的测试代码正在使用StatementResultSet。也许有一个数据库连接是在没有锁定的情况下共享的?你能试着只返回结果字符串并打印出来吗?

不太可能是某种网络或防火墙配置,但一个失败的连接会导致另一个失败,这种想法很奇怪。也许可以尝试在其中一台服务器上或从另一台计算机上运行程序?

如果我尝试你的测试代码,它似乎工作得很好。这是我的测试类的源代码。它在联系联机和脱机主机的组合时没有问题。

最后,一些关于你的代码的快速评论:

  • 您应该关闭finally块中的流、读卡器和套接字。查看我的测试类,了解更好的模式
  • 您应该返回一个小的Result类,而不是返回一个必须解析它们的String

希望这能有所帮助。

经过大量的阅读和实验,我将不得不回答我自己的问题(当然,如果允许的话)。

Java无法在不增加大量性能开销的情况下处理并发的多套接字连接。至少在Core2Duo/4GB RAM/Windows XP机器中。

创建到远程主机的多个并发套接字连接(当然使用我发布的代码)会造成某种资源瓶颈或阻塞情况,我仍然没有意识到这一点。

如果你试图同时连接到20台主机,但其中很多主机都断开了连接,那么你就无法保证与活跃主机的"快速"连接。您将接通,但可能在20-25秒后接通。这意味着您必须将套接字超时设置为大约60秒。(我的申请不可接受)

如果一个活跃的主机幸运地首先开始连接尝试(记住并发性不是绝对的。for循环仍然具有顺序性),那么他可能会很快连接并得到响应。

如果运气不好,socket.connect()方法将阻塞一段时间,这取决于它之前有多少主机最终会超时。

在pool.submit(worker)方法调用(100ms)之间添加了一个小睡眠后,我意识到这会有所不同。我可以更快地连接到"倒霉"的主机。但是,如果死亡主机的列表增加,结果几乎是一样的。

如果我编辑我的主机列表,并将以前"不幸"的主机放在顶部(在死亡主机之前),所有问题都会消失。。。

因此,由于某种原因,socket.connect()方法在要连接的主机很多且不活动时会产生某种形式的瓶颈。无论是JVM问题、操作系统限制还是我这边的糟糕编码,我都不知道。。。

我会尝试一种不同的编码方法,希望明天我会发布一些反馈。

这个答案让我想到了自己的问题:https://stackoverflow.com/a/4351360/2025271

相关内容

  • 没有找到相关文章

最新更新