家・車以外で、人生で一番奮発した買い物

「「「プログラミング」JAVAのエラーが解決できません」」
http://oshiete1.goo.ne.jp/kotaeru.php3?q=1038426

の質問で作っているプログラムの続きです。
無事に、ひとつエラーが解決したので新たにこちらへ質問をさせていただきました。
ソースデータは、#5の補足をご覧ください。
今は、長時間動かしていると、
【Address already in use: connect】
というエラーがでます。100%再現性があるわけではないのですが時々出ます。
どのようにすると解決するのか分かる方折られないでしょうか?なにとぞ、宜しくお願い致します。

A 回答 (8件)

話が見えてきたので、参加させてもらいます。




#3の補足で「0.1秒毎ではなく、1秒毎や5秒毎でも一応大丈夫です」とあるので、
次のような構造(概略)で設計してみたらどうでしょうか?


〇URL管理クラス
 ダウンロード対象とたるURLを管理する。
 getNextUrl()メソッドで次々とURLを返す。
 最後まで返したら、初めに戻る。

 ただし、getNextUrl()メソッドでURLを返す時に、その時刻をURL毎に保存しておき、
 前回返した時刻からn秒以内の場合は、返さずにスレッドをsleepさせる。

 getNextUrl()メソッドはsynchronizedメソッドであること。

〇ダウンロードクラス
 指定されたURLのファイルをダウンロードする。

〇コントロールクラス
 全体の流れを管理・制御する。
 n個のダウンロードスレッドを起動する。

〇ダウンロードスレッドクラス
 ダウンロード、解析、表示を行う。
 ダウンロードするURLはURL管理クラスから取得する。


要するに、n個のスレッドがダウンロードを実行します。
それぞれのスレッドは、同一のURLに対しアクセスするのではなく、
1つのURLリストをそれぞれのスレッドで順番にダウンロードしていきます。

また、ダウンロード速度が速かった、あるいは登録件数が少なかったなどの理由で、
同じURLに対し、短時間でアクセスしようとした場合には、予め設定してある時間だけ
待ち時間をいれます。(サーバーに優しい)


また、ダウンロードも毎回本当にダウンロードするのではなく、
更新されたかチェックするなどの配慮をしてもいいと思います。
    • good
    • 0
この回答へのお礼

回答いただきありがとうございます。
ぜひとも参考にさせていただきます。
また、

また、ダウンロード速度が速かった、あるいは登録件数が少なかったなどの理由で、
同じURLに対し、短時間でアクセスしようとした場合には、予め設定してある時間だけ
待ち時間をいれます。(サーバーに優しい)

については、確かに良い方法ですね。ありがとうございます。

お礼日時:2004/11/03 18:40

#2です。

たびたびすいません。
思いっきり「間違い」を書いたので訂正。

#5で、僕が
>それから、シングルスレッドのほうが高速のような気がします
と書いてしまいましたが、
これ(たぶん)間違いです。
(本当に申し訳ありません。はずかしい…)

---
ただ、
「複数スレッドを使えば、とりもなおさず(問答無用で)高速になる」
と(質問者さんが)思われているようであれば、
それはそうじゃないよ、
適切に使わなければ意味がないよ、
ということが言いたかったのは事実。それを強調しすぎた?
(ちょっと言い訳がましくてゴメン)

同じく#5で
>「マルチスレッド」にする利点は「ない」と思う
と書いた真意は、
「複数スレッドに本当に利点があるかどうかを、もう一度、自身の目で疑え
(質問者さん自身が「利点がある」と判断した場合に限り、使ってイイデスよ)」
ということです。(これは本当)
    • good
    • 0
この回答へのお礼

回答頂ありがとうございます。
いろいろありがとうございます。とても参考になります。
せっかく、今は、ブロードバンドの時代で、より効率的に早く、サイトを調べることができればいいなぁというのが事の発端です。定額時代で、同じお金を払っているなら沢山使わないと損な気がします。(かといって、ひとつのサイトを攻撃するのではなく、複数サイトへの分散アクセスを考えています。)
たとえば、RegetやIriaなどのダウンロード支援ソフトは回線幅を最大限利用してダウンロードできるためとても便利だと思っています。

お礼日時:2004/10/19 15:28

勘違いをされているようですが・・・


この現象はJAVAの限界ではありません。
Apacheのabツールなどで同時接続の試験などを行うとよくわかると思うのですが、WindowsとLinuxでの試験結果は全然違います。
JAVAといえどWindows上で走っている時はWindowsのAPIを呼び出しているので、もっと低レベルでの処理の問題でしょう。いわゆるそのマシンの限界です。
マシンスペックなどにも限界はありますので、どうしてもそれだけの要求をするならば一台につきいくつの処理というようにして、複数台のマシンを同時に走らせるなどの考慮が必要です。
開発全般に言えることなのですが、要件によって環境が決まり、その中でコーディングを行うのです。
要件に対しての環境のミスマッチが大きすぎます。数千のスレッドを(しかもコストのかかるソケット処理を)Windowsマシンでやるなんて誰が考えても無謀すぎます。
経験が浅いためか、ちょっとmizuki_ffさんは感覚がずれているような感じですが、切り分けや経験ある人の意見は素直に聞くほうがよいでしょう。

この回答への補足

回答頂ありがとうございます。

>要件に対しての環境のミスマッチが大きすぎます。数千のスレッドを(しかもコストのかかるソケット処理を)Windowsマシンでやるなんて誰が考えても無謀すぎます。

に関してですが、開くべきソケットの数を指定するとか、開いて、閉じたら、また開くという風にすれば可能だと思うのですがいかがでしょうか?
また、こういったことは、JAVAで可能なのでしょうか?
宜しくお願い致します。

>切り分けや経験ある人の意見は素直に聞くほうがよいでしょう。
そうですね。そうします。

補足日時:2004/10/19 15:14
    • good
    • 0

#2です



>残念ながら、こうしてしまうと、
>1つ目のURLを1000個読み込んで次のを1000個読み込んで次のを・・・となり

>できるだけリアルタイムの情報を早く欲しいと考えているため、
>いくつかの違うURLを同時に解析したいのですが
>ないか良い方法はないでしょうか?


すいません。前回
http://oshiete1.goo.ne.jp/kotaeru.php3?q=1038426
の#5の補足欄のソースコードに於いて
「1000回ループさせる意図」
がつかめなかったので、
間違えました。
(今やその意図がわかったような気がする。これは単に
「アプリケーション起動期間」を表すんですよね?)

前回#5の補足欄のソースコード(マルチスレッド版)を
「シングルスレッド版」に置き換えれば、
下記ソースコードのようになります。

ちなみに、
質問者さんの考えるようなWeb巡回アプリケーションを作る場合、
「マルチスレッド」にする利点は「ない」と思うのですが、
どうでしょうか。
(それから、シングルスレッドのほうが高速のような気がします)


class Test {
 public static void main(String[] args) { 
  String[] list
   = {"http://127.0.0.1/baa/",
    "http://127.0.0.1/bee/",
    "http://127.0.0.1/buu/"};
  
  Th th = new Th(list);
  th.start();
 }
}
 
class Th extends Thread {
 String[] list;
 final int kTERM=1000;//処理する「期間」
 
 Th(String[] list) {
  this.list = list;
 }
 
 
 public void run() {
  //一定期間の間、処理
  for (int i = 0; i < kTERM; i++) {
   _run1();//ウェブ1めぐり
   
   //適当にウェイト
   try {Thread.sleep(15000);}catch(InterruptedException e){}
  }
 }
 
 
 void _run1() {
  //Web巡回ループ
  for (int i = 0; i <list.length; i++ ) {
    _run2(list[i]);//1サイトを調査
  }
 }
 
 
 void _run2(String address) {
  try {
   InputStream is=null;
   URL url= new URL(address);
   try {
     is = url.openStream(); 
   }catch(BindException e){
    System.err.println("connections have run out!!");
    System.exit(0);//終了
   }catch(Exception e) {return;}
   
   
   is.close();
  } catch(Exception e){}
 }
}
    • good
    • 0

エラーの発生原因についてははっきりしませんが、対処方法について。



>もしそうであれば、同一ポートにアクセスしようとしたら、そこで待つような指示は
>できないのでしょうか?
>また、多重アクセスがサーバー負荷のため危険であればアクセスポート数や
>スレッド数を制限して(スレッドの上限を設定する)サーバーにやさしいプログラムは
>作成できないものでしょうか?
>それとも、#1様のおっしゃるように、JAVAという言語の限界なのでしょうか?

接続設定で同期/非同期の設定は、ネットワークにアクセスする機能として
持っていると思います。
また、例外がスローされる場合に、エラーコードなどでリトライ可能という
判断が付けば、ループを作って自動リトライすることもできると思います。

ベタなコードですが・・・
--------
static final int LOOP_MAX = 10
int loopcnt = 0;
while(loopcnt < LOOP_MAX){
 try{
  // ネットワーク処理
  break;
 }catch(Exception e){
  if(isCritical(e)){// リトライ不可かどうかの判定(メソッドを作成)
   throw e;// リトライ不可
  }
  loopcnt ++;
  // ウェイト処理
 }
}
--------

また、javaで出来るネットワークアクセスは、どちらかというと
ある程度の処理を経た、扱いやすいデータが対象ですので、
ハードウェアに直接アクセスすることが出来るC/C++でのプログラムとは
若干できることに制限があると思います。
ただ、この制限は通常気にしなくても良いものですので、
プログラマという立場からすれば制限を理解したうえで適宜作成すれば、
必要な機能は十分に満たせると考えています。
(実際にどうやれば十分かは、その時々で違いますので割愛します)

それから、もし時間的に余裕があるようでしたら、シングルスレッドで
作成して、コマンドプロンプトを複数起動する形で検証していってはどうでしょうか。


(ここまでは機能的な話題、ここからは機能外的な話題です)


>情報というのは、ネット限定商品、限定3名様プレミア商品などのように
>告知を早く知った人の勝ち、という情報の収集もあります。
>調べていると、光回線且つ自動クリックソフトで人ではない速さで調べている方もいるとのことでした。

ネット限定であれば、情報の入手方法はインターネット限定になりますね(^_^;
それから、間隔については、やはり常識(=モラル)の問題だと思います。
特に人間には無理は速度の場合は。
接続先が利用規約などで制限していても、やる人はやるでしょうし、また本当にプログラムで
自動的に行っているという検証が出来ないので、取り締まりにくいことも事実でしょう。
#環境にも依るので、それらしいというだけで、確実に検出できるわけでは無いですから。

まぁ、プル型の送信方法では仕方ないことだと考えています。


私自身は、あまり限定、確率でというのに左右されたくないので、
限定情報にはあまり執着していないつもりです。
ですので、あまり頻繁に同じアクセスをするとどうかと思ってしまうので、
この話題では mizuki_ff さんの考えと違うところに論点がいってしまっている気がしています。

あと、だいたい30分程度、短くて5分くらいまでが限界じゃないかと考えています。
#もっともこれは、メールサーバへのアクセス間隔ですが(^_^;;
    • good
    • 0
この回答へのお礼

回答頂ありがとうございます。
とても参考になりました。


>因みに、私の場合、メールサーバーへのアクセスは1分毎です。ダイヤルアップ時代からそうしていますがまずいんでしょうかね?

お礼日時:2004/10/19 15:21

#2の方への補足について



>残念ながら、こうしてしまうと、1つ目のURLを1000個読み込んで
>次のを1000個読み込んで次のを・・・

パソコンの性能もあるでしょうけれど、理想的な起動の仕方であれば
(スレッドの起動タイミングがURL接続よりもかなり早いのであれば)、
一つ目のURLが読み込まれている間に次々とスレッドが立ち上がり、
全部のスレッドで同時にURLを開いてくれるハズです。
(スレッドとはそういうものです)
#ただ、スレッド数は多くても数十程度で、百を超えるとリソース量の
#残りが心配になります。
#(といっても、やったことはありませんが)


エラーに関しては、googleで検索し、ざっとピックアップしてみましたが
「使用しようとしているアドレス(ポート)は既に使われている」と
いう意味のようです。
そのページでは、アンチウィルスソフトがブロックしている
アドレス(ポート)を使用しようとして同エラーが出ているようですので、
恐らく一つのスレッドが終了する前に次のスレッドが同一のIP/ポートから
アクセスしようとするため、どこかで競合が起きているのでしょう。
#上のスレッドの話と矛盾する(=エラー発生)気もしますが・・・


(ここからはおせっかいです。考え方の相違ともいいますが・・・)

話は変わりますが、前回のQAをみてあまり良い印象を受けません。
#テストのために、LAN内にとどまらず、インターネットに接続しているからでしょう。

>できるだけリアルタイムの情報を早く欲しいと考えているため

「情報が発信され次第すぐに得たい」という気持ちは分かるのですが、
そのために0.1秒毎に同じアクセスを繰り返すことは、やはり「常識外」だと思います。
(3000回ですから5分間ですが、多分バージョンアップして無制限に、だと思いますので)
#それに機械的なアクセスと判断された場合は、接続拒否を受ける可能性もあります。

自分のやりたいこと(リアルタイムな情報取得)のために相手に要求(接続を
捌くためのキャパシティや設定)するのは自分のエゴでしかありません。
#むろん、先方と合意があれば構いません。
#常識的な範囲=手動での一般的な操作の程度、と考えていますので。


それから、本当に、0.1秒刻みで得なければいけない情報なのでしょうか?
もしそうだとすると、0.1秒刻みで得なければいけない情報を、
場合によっては繋がらないこともある、不安定なインターネットで得るですか?
また、情報の再発信のためだとして、編集に時間がかかったり、
再アクセスするまでに時間がかかるのであれば、0.1秒という間隔に
意味が無いのではないでしょうか?

本当にシビアにリアルタイム性が求められる情報、それから本当に確実性の
求められる情報も含めて、それらはインターネットで得るべきではなく、
新聞・TV・ラジオなど他の情報ソースを使用するべきです。
#チェックのために時間が取られるとしても・・・その価値があるのですから。

この回答への補足

回答いただきありがとうございます。

>つまり、スレッドの数がどんどん増えていって、ある時点で、使用中のポートへ再度アクセスすることで、同一ポートを使っているので接続できなかったというエラーがでるということでしょうか。もしそうであれば、同一ポートにアクセスしようとしたら、そこで待つような指示はできないのでしょうか?
また、多重アクセスがサーバー負荷のため危険であればアクセスポート数やスレッド数を制限して(スレッドの上限を設定する)サーバーにやさしいプログラムは作成できないものでしょうか?それとも、#1様のおっしゃるように、JAVAという言語の限界なのでしょうか?

>手動でできる速さで・・・
0.1秒毎ではなく、1秒毎や5秒毎でも一応大丈夫です。情報というのは、ネット限定商品、限定3名様プレミア商品などのように告知を早く知った人の勝ち、という情報の収集もあります。調べていると、光回線且つ自動クリックソフトで人ではない速さで調べている方もいるとのことでした。

なお、前回の回答の#5の補足で、全てヤフーになっていますが、ここのURLはヤフーが2つ、別のサイトが2つ、さらに全く別のサイトが2つというような感じで全然違うサイトに分散させることも考えています。

以上、なんとか、サーバーにやさしくて且つ、最大限すばやく情報を入手する方法を模索しています。もしよろしければ、解決する方法を教えていただけないでしょうか?
宜しくお願い致します。

補足日時:2004/10/17 12:40
    • good
    • 0

単一スレッド構造にしましょう






//-----------------------------------
import java.net.*;
import java.io.*;

class Test {
 public static void main(String[] args) {
  String[] list = {
   "http://127.0.0.1/a/"
   ,"http://127.0.0.1/b/"
   ,"http://127.0.0.1/c/"};
  
  Th th = new Th(list);
  th.start();
 }
}
 
class Th extends Thread {
 String[] list;
 final int kREPEAT=1000;
 
 
 Th(String[] list) {
  this.list = list;
 }
 
 
 public void run() {
  for (int i = 0; i < list.length; i++) {
   _run1(list[i]);
  } 
 }
 
 
 void _run1(String addr) {
  for (int i = 0; i <kREPEAT; i++ ) {
    _run2(addr);
  }
 }
 
 
 void _run2(String addr) {
  try {
   InputStream is=null;
   URL url= new URL(addr);
   try {
     is = url.openStream();   
   }catch(BindException e){
    err("BindException occurred");
    System.exit(0);//アプリケーション終了
   }catch(Exception e) {
     err("failed to open stream");
     e.printStackTrace();
     return;//メソッド終了
   }
   
   is.close();
  } catch(Exception e){
   err("unexpected error");
   e.printStackTrace(); 
  }
 }
 
 
 void err(String s) {
  System.err.println(s);
 }
}

この回答への補足

回答いただきありがとうございます。
残念ながら、こうしてしまうと、1つ目のURLを1000個読み込んで次のを1000個読み込んで次のを・・・となります。できるだけリアルタイムの情報を早く欲しいと考えているため、いくつかの違うURLを同時に解析したいのですがないか良い方法はないでしょうか?
もし、私の理解不足で実はそうなっていたらご指摘ください。
また、どうして、Address already in use: connectというエラーが出るのか分かりますでしょうか?
宜しくお願い致します。

補足日時:2004/10/17 06:15
    • good
    • 0

JAVAとインターネットとの親和性が高いと云われていますが実は、そこまで高くはありません。

こういったエラーは正直、JAVAでは制御のできない次元です。

自信なしで、あくまで聞いたことがある程度なので、誰も答えなかったら参考にして下さいね。
    • good
    • 0
この回答へのお礼

回答いただきありがとうございます。
もし、有効な回答がなかったらぜひとも参考にさせていただきます。ありがとうございます。

お礼日時:2004/10/17 06:20

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


おすすめ情報