Java线程套接字连接超时

时间:2013-09-04 13:20:35

标签: java multithreading sockets timeout

我必须每隔x秒与多台计算机同时建立tcp套接字连接,以便获得状态更新数据包。

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

我的套接字连接类是:

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

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

如果我强制列表包含一个工作电脑,我没有问题。 超时是相当随机的,不知道是什么导致了它们。

所有计算机都在本地网络中,远程服务器也由我(在C / C ++中)编写,并且在其他设置中工作超过2年没有任何问题。

我错过了什么或者它是否是os网络限制问题? 我在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线程上没有那么多。

所以,我所有旧服务器的fisrt都有某种问题。不知道那是什么,因为我的新服务器是从相同的操作系统映像创建的。

其次,尽管超时百分比已经大幅下降,但我仍然认为在像我们这样的小型局域网中获得一次超时是不常见的。但这可能是服务器的应用程序部分问题。

最后我的观点是,除了旧服务器的问题(我仍然无法相信,我失去了那么多时间!),必须有服务器应用程序错误或JDK相关的错误(因为我经历过那个JRE崩溃了。)

P.S。我使用Eclipse作为IDE,而我的JRE是最新的。

如果上述任何一个响铃,请发表评论。 谢谢。

- - - - - - - - 编辑

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

---- NEW EDIT 2013年9月9日----

重新阅读所有评论后,感谢@Gray及其评论:

  

当您运行多个服务器时,前几个服务器是否正常工作,其余服务器是否超时?在你的fork循环(例如10或100ms)中放一个小睡眠可能会很有趣,看它是否有效。

我重新排列了hosts / ip的树列表,得到了一些非常奇怪的结果。 似乎如果将一个活动主机放在树列表的顶部,从而首先启动套接字连接,则在没有任何延迟或超时的情况下连接和接收数据包没有问题。

相反,如果一个活动主机位于列表底部,并且前面有几个死主机,则连接时间太长,而我之前的10秒超时则无法连接。但是在将超时更改为60秒后(感谢@EJP)我意识到没有超时发生!

连接需要很长时间(在某些情况下超过20秒)。 有些事情正在破坏新的套接字连接,并不是主机或网络忙于响应。

我在这里有一些调试数据,如果你想看看: http://pastebin.com/2m8jDwKL

3 个答案:

答案 0 :(得分:1)

您可以在连接到套接字之前检查可用性。有一个答案提供某种hackish解决方法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是一个常见的程序。

答案 1 :(得分:0)

  

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

据我了解这个问题,如果您的地图中有10台PC,其中1台处于活动状态,而其他9台不在线,则所有10个连接都会超时。如果您只是将1台活着的PC放在地图中,它就会显示为正常。

这指出了某种并发问题,但我看不到它。我原本以为有某种共享数据没有被锁定或什么的。我看到您的测试代码正在使用StatementResultSet。也许有一个数据库连接正在共享而没有锁定或什么?您可以尝试返回结果字符串并将其打印出来吗?

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

如果我尝试你的测试代码,它似乎工作正常。这是source code for my test class。联系在线和离线主机组合没有问题。

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

  • 您应该关闭finally块中的流,阅读器和套接字。检查我的test class以获得更好的模式。
  • 您应该返回一个小Result类,而不是传回一个必须要解析它们的String

希望这有帮助。

答案 2 :(得分:0)

经过大量的阅读和实验,我将不得不回答我自己的问题(如果我当然可以这样做)。

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

创建到远程主机的多个并发套接字连接(当然使用我发布的代码)会产生某种资源瓶颈或阻塞情况,我仍然不知道。

如果您尝试同时连接20台主机,并且其中很多都已断开连接,那么您无法保证与活动主机的“快速”连接。 您将连接,但可能会在20-25秒后。这意味着您必须将套接字超时设置为60秒。 (我的申请不能接受)

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

如果运气不好,socket.connect()方法会阻塞一段时间,具体取决于最终超时的主机数量。

在pool.submit(worker)方法调用(100毫秒)之间添加一个小睡眠后,我意识到它有所不同。我可以更快地连接到“不幸”的主机。但是,如果死宿主列表增加,结果几乎相同。

如果我编辑我的主机列表并将一个以前“不幸”的主机放在顶部(在死去的主机之前),所有问题都会消失......

因此,由于某种原因,当连接的主机很多且不活动时,socket.connect()方法会创建一种瓶颈形式。无论是JVM问题,操作系统限制还是来自我身边的错误编码,我都不知道......

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

P.S。这个答案让我想起了我的问题: https://stackoverflow.com/a/4351360/2025271