簡體   English   中英

Java線程套接字連接超時

[英]Java threaded socket connection timeouts

我必須每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映射包含(主機名,hostip)值,並且為每個條目創建一個ClientConnect線程對象。

我的問題是,當我的計算機列表中包含10台計算機(其中大多數都不處於運行狀態)時,即使我的超時限制設置為10秒,我也會收到很多超時異常(處於運行狀態的計算機)。

如果我強制列表包含單個工作PC,則沒有問題。 超時是非常隨機的,不知道是什么原因造成的。

所有機器都在本地網絡中,遠程服務器也由我編寫(使用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線程上的工作量不是很多。

因此,我所有舊服務器的服務器都出現了某種問題。 不知道那是什么,因為我的新服務器是從同一OS映像創建的。

其次,盡管超時百分比已大大降低,但我仍然認為在像我們這樣的小型LAN中甚至一次超時也不常見。 但這可能是服務器的應用程序部分的問題。

最后,我的觀點是,除了舊服務器的問題(我仍然無法相信我花了那么多時間!)之外,肯定還有服務器應用程序錯誤或與JDK相關的錯誤(因為我經歷過JRE崩潰) )。

ps我使用Eclipse作為IDE,而我的JRE是最新的。

如果以上任何一個給您敲響了鍾聲,請發表評論。 謝謝。

- - -編輯 - - -

難道是PrintWriter和/或BufferedReader實際上不是線程安全的??????

---- 2013年9月9日的新編輯----

重新閱讀所有評論並感謝@Gray和他的評論后:

當您運行多個服務器時,前幾個服務器是否工作,其余服務器是否超時? 在fork循環中睡一小段時間(例如10或100ms)可能會很有趣,看看它是否可以那樣工作。

我重新整理了主機/ 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);

jayunit100

由於ping是一個常見程序,因此它應該可以在UNIX和Windows上運行。

我的問題是,當我的計算機列表中包含10台計算機(其中大多數都不處於運行狀態)時,即使我的超時限制設置為10秒,我也會收到很多超時異常(處於運行狀態的計算機)。

因此,據我所知,如果(例如)您的地圖中有10台PC,並且1台處於活動狀態,而另外9台未處於聯機狀態,則所有10個連接都將超時。 如果您僅將1個處於活動狀態的PC放置在地圖中,它將顯示為正常。

這指向某種並發問題,但我看不到。 我本以為存在某種未鎖定的共享數據或其他東西。 我看到您的測試代碼正在使用StatementResultSet 也許有一個沒有鎖定就可以共享的數據庫連接? 您可以嘗試僅返回結果字符串並打印出來嗎?

不太可能進行某種類型的網絡或防火牆配置,但是一個連接失敗會導致另一個連接失敗的想法只是一個奇怪的想法。 也許嘗試在其中一台服務器上或從另一台計算機上運行程序?

如果我嘗試您的測試代碼,它似乎可以正常工作。 這是我的測試類源代碼 與聯機和脫機主機的組合聯系沒有問題。

最后,簡要介紹一下您的代碼:

  • 您應該在finally塊中關閉流,讀取器和套接字。 檢查我的測試班級那里是否有更好的模式。
  • 您應該返回一個小的Result類,而不是傳回必須對其進行解析的String

希望這可以幫助。

經過大量閱讀和實驗之后,我將不得不回答自己的問題(如果可以的話,我當然可以這樣做)。

Java不能在不增加大量性能開銷的情況下處理並發多個套接字連接。 至少在Core2Duo / 4GB RAM / Windows XP計算機中。

創建多個到遠程主機的並發套接字連接(當然使用我發布的代碼)會造成某種資源瓶頸或阻塞情況,盡管我仍然不知道。

如果您嘗試同時連接到20台主機,並且其中許多主機已斷開連接,那么您將無法保證與有生命的主機的“快速”連接。 您將建立連接,但可能需要20-25秒。 這意味着您必須將套接字超時設置為60秒左右。 (我的申請不接受)

如果有生命的主機很幸運可以先嘗試建立連接(請注意並發不是絕對的。for循環仍具有順序性),那么他可能會很快連接並獲得響應。

如果不是很幸運,socket.connect()方法將阻塞一段時間,具體取決於它之前最終將超時的主機數量。

在pool.submit(worker)方法調用之間添加了一個小小的睡眠(100毫秒)之后,我意識到它會有所作為。 我可以更快地連接到“不幸的”主機。 但是,如果增加死主機的數量,結果幾乎相同。

如果我編輯主機列表,並將以前“不走運”的主機放在頂部(在死主機之前),所有問題都會消失...

因此,由於某些原因,當要連接的主機很多且沒有運行時,socket.connect()方法會形成瓶頸形式。 無論是JVM問題,操作系統限制還是我的編碼不好,我都不知道。

我將嘗試其他編碼方法,希望明天我會發表一些反饋。

ps這個答案讓我想到了我的問題: https : //stackoverflow.com/a/4351360/2025271

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM