質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.49%
Windows 7

Microsoft Windows 7は過去にリリースされたMicrosoft WindowsのOSであり、Windows8の1代前です。2009年の7月にリリースされ販売されました。Windows7の前はWindowsVistaで、その更に3年前にリリースされました。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

5677閲覧

Windows環境のPythonでurllib使用時のエラー

nekomata913

総合スコア24

Windows 7

Microsoft Windows 7は過去にリリースされたMicrosoft WindowsのOSであり、Windows8の1代前です。2009年の7月にリリースされ販売されました。Windows7の前はWindowsVistaで、その更に3年前にリリースされました。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

2クリップ

投稿2018/02/12 18:46

前提・実現したいこと

PythonでHTTPを使用して、WEBサイトの簡易的な死活監視を行いたいです。
以下のコードをpyファイルに保存してbatファイルから5分おきに実行したところ、
約18時間後以降は下記のエラーを出力し続け、システムを再起動するまでインターネット接続が不可になりました。
メッセージからするとリソース不足のようですが、何か明示的な解放操作が必要なのでしょうか?
なお、batファイルを使用せず、python中のwhileでループするコードでも同様の症状が発生しました。
ご教示お願いします。

該当のソースコード

Python

1import urllib.request 2 3def check_status(url): 4 try: 5 with urllib.request.urlopen(url, timeout=10) as response: 6 return response.code 7 except urllib.error.HTTPError as e: 8 return e.code 9 except Exception as e: 10 print(str(e)) 11 return -1 12 13status = check_status('https://www.hoge.com/') 14print("access status:", status) 15if status != 200: 16 print("access error.") 17 # 疎通失敗時のアクション

発生している問題・エラーメッセージ

access status:-1 <urlopen error [WinError 10055] システムのバッファー領域が不足しているか、またはキューがいっぱいなため、ソケット操作を実行できませんでした。>

補足情報(FW/ツールのバージョンなど)

環境 Windows 7 professional 32bit, Python 3.6.4 32bit

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

コード上は問題が無いように見受けられます。
例外発生時に問題があるみたいなので、訂正線を追加

urlopen error [WinError 10055]

WinSockエラー 10055なので考えられる原因は3点あります。

1,MaxUserPortの設定を行っていなく、使用可能なポートが枯渇した。
■参考情報
5000 を超える番号の TCP ポートから接続しようとすると 'WSAENOBUFS (10055)' エラーが表示される

2,ファイアウォールやアンチウィルスソフトのバグによるポート枯渇。
OFFにしてもよいなら、危険ですが一度試してみてくださいな。

3,32bit OSなためシステムのリソース不足
64bit OSで試してみてくださいな。

■参考情報
Windows ソケットのエラー コード、値、および意味]

あとはnetstat -anop tcp 3 でポートがCLOSE_WAIT状態になっているのかの確認ぐらいでしょうか。


2018/02/14追記
少し気になって調べてみましたが、urllib.request.urlopenはHTTP 1.1のKeep-Alive接続をサポートしないみたいですね。

urllib.request モジュールは HTTP/1.1 を使用し、その HTTP リクエストに Connection:close ヘッダーを含みます。

よって接続するたびに新規ソケットが生成されるとのこと。

Python

1with urllib.request.urlopen(url, timeout=10) as response: 2 # response ヘッダーを表示 3 # ('Connection', 'close')が表示されると思います。 4 print(response.getheaders()) 5 return response.code

投稿2018/02/12 20:27

編集2018/02/14 08:09
umyu

総合スコア5846

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nekomata913

2018/02/13 14:54

回答ありがとうございます。2,3は環境の都合上試せていませんが、1の対処を行い、実行間隔を5秒に縮めて確認したところ、12時間経過時点で問題なく動作しています。このまま様子を見たいと思います。
umyu

2018/02/13 15:34

ダウンタイム許容時間が何分で何時間以上継続稼働する予定のプログラムか分かりませんが、 時間がある時にProcess Exploperやgc.set_debug(gc.DEBUG_LEAK) などで、本質的な原因調査をされたほうが良いと思います。
nekomata913

2018/02/13 18:10

ありがとうございます。上記のプログラムは補助的かつ短期間の用途ですので、現状は許容範囲内ですが、気にはなりますので調べたいと思います。
nekomata913

2018/02/14 14:42

なるほど、urllib.request.urlopenの仕様として新規ソケットを生成し続けた結果、空きローカルポートが枯渇した、ということでしょうか。対処について調べたところ、MaxUserPortとTcpTimedWaitDelayの設定により改善したケースが多いようですね。現在はMaxUserPort=65534,TcpTimedWaitDelay=60に変更し、とくに問題なく動作しているようです。
umyu

2018/02/14 15:50 編集

最初の回答でMaxUserPortの事を「原因」という言葉を使ってしまったのが良くなかったのですが。 本来は「対処法」というのが正しい表現です。 新規ソケットを生成し続けたのは質問文の症状発生の「起因」だと思いますが。 質問文の「5分おきに実行」という点が気になってます。 TcpTimedWaitDelay(240秒=4分)が経過したら、ポート番号がいつかは再利用されるため。 もう一箇所どこかに「原因」があるのではないかと推測しています。 netstat で接続先のHOSTがESTABLISHED状態のポートが複数無いことを念のため確認してみてくださいな。
nekomata913

2018/02/15 15:33

確認してみましたが、それらしい接続は見当たらないですね…。当初とまったく同じ条件下、というわけではないですが。せめてMaxUserPortとTcpTimedWaitDelayを戻せば、もう少し実のある調査ができそうですが、すみません、ちょっとそこまで手が回ってないですね。
umyu

2018/02/16 06:17

なるほど、調査ありがとうございました。
guest

0

withステートメントは__enter__関数と__exit__関数のラッパーになっていますが、この内__enter__関数内でエラーが発生した場合に__exit__関数は呼ばれません(python3.6.3で確認)。
この場合は安全のために、withを使わずに明示的にオープン・クローズをした方がいいかと思います。
修正例としてはこのようなコードになります。

python

1import urllib.request 2 3def check_status(url): 4 try: 5 response = urllib.request.urlopen(url, timeout=10) 6 return response.code 7 except urllib.error.HTTPError as e: 8 return e.code 9 except Exception as e: 10 print(str(e)) 11 return -1 12 finally: 13 response.close() 14 15status = check_status('https://www.hoge.com/') 16print("access status:", status) 17if status != 200: 18 print("access error.") 19 # 疎通失敗時のアクション

urlopen関数の怖いところは、標準のopensocket.socket.connectなどと違って名前解決が出来なかった、そもそもソケットが作れなかった等の場合を除きエラーが発生するときには既に接続が確立していることです。
もし、標準以外のライブラリを使うことに抵抗が無いならurllibではなくrequests等の高水準ライブラリを使うとよいと思います。
一応サンプルコードもおいときますね。

python

1from requests import get 2 3def check_status(url): 4 #hostが見つからない場合、404が返ります 5 return get(url, timeout=10).status_code 6 7status = check_status('https://www.hoge.com/') 8print("access status:", status) 9if status != 200: 10 print("access error.") 11 # 疎通失敗時のアクション

投稿2018/02/12 23:34

編集2018/02/13 00:47
frodo821

総合スコア322

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nekomata913

2018/02/13 14:58

回答ありがとうございます。withのくだりは知りませんでした。勉強になります。requestsは環境制約上使用を見送りましたが、HTTPを扱う際には使用を検討したいと思います。
frodo821

2018/02/13 15:35

自分も実際に遭遇するまで知りませんでした^^);
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問