47150


<https://docs.python.org/ja/3.5/howto/sockets.html>  
上記のサイトにsocket.setblocking(0)は使うべきではないと表記してありますが，単一のクライアントとのみ通信したい場合も使うべきではないのでしょうか？  
別のサイトでこのようなビジーループはCPUのメモリを多く使ってしまうという記述を見かけましたが，setblocking(0)を実際にサーバで

[code]

    soc=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    soc.bind(('127.0.0.1',65535))
    soc.listen(5)
    soc.setblocking(0)
    
[/code]

のようにして単一のクライアントと通信するために使ってみたのですが，CPU使用率は1％にも満たず，動作も問題ありませんでした．  
このような場合はsetblocking(0)を使ってもよいのでしょうか？


CC BY-SA 4.0
2018-07-31T17:06:11.170
47145
2018-08-02T03:22:50.693
20217
question
python tcp socket
pythonで単一のクライアントとソケット通信する場合もsocket.setblocking(0)は使うべきではないのでしょうか？


何らかの理由でノンブロッキングソケットを使いたいのなら、使ってかまいません。

ただ、そのコードは面倒くさいものになるので、避けたほうが良いというのが、参照されたサイトの意図だと思います。

ノンブロッキングソケットを使うプログラムは、結構複雑になりますし、正しく動作することをテストするのが非常に面倒です。

ブロッキングソケットなら、コードにread()と1行書けば、データが受信するのを待って、受信されたデータを返してくれます。

それに対してノンブロッキングソケットなら、read()はすぐに返ってくるのですが、データがまだ届いていないとエラーを返してきますから、以下のようなループで回すことになります。当然、１行では済みません。

read()してみる  
IF エラーじゃなかったら、  
値を得て終わり  
ELSE エラーが返ってきたら、  
失敗回数を＋１する。  
IF 失敗回数が制限を超えたら、  
readに失敗した場合の処理をする  
ELSE  
少し待って、最初に戻る

クライアントのコードが複雑になってしまうという問題は、クライアントが単一でも、複数でも生じます。

＝  
アンブロックソケットを使うプログラムは、UDPによる通信など非同期的な要素を含むプログラムと似ています。  
そうした（複雑、デバッグしにくい、状態の再現が困難）プログラムを作ることがお好きなら、楽しんでみても良いと思います。


CC BY-SA 4.0
2018-08-01T02:21:08.053
47148
2018-08-01T02:21:08.053
217
47145
answer


socket.setblocking(0)にして、send, recv, connect,
acceptで処理をすることを、<https://docs.python.org/ja/3.5/howto/sockets.html>では、「愚かな解法」と書かれています。ノンブロッキングソケットをsoc.recv()等を使って、効率的な処理をできるようなプログラムを自分で書くのは相当な手間だと思います。単一のクライアントとのみ通信すると限定してしまえば、プログラムを書くのは簡単になりますが、ノンブロッキングソケットを使うメッリトはないと思います。それであれば、ブロッキングソケットで十分だと思います。

ネットワークでのやり取りが増加している現在では、node.jsだと最初から非同期プログラミングが前提となっているように、効率的に処理をするためにノンブロッキングソケットを使うことは多くなってきています。Pythonでは以前からselectを使えたし、最近では`asyncio`モジュールがPython3.4で追加され、その後改善されてきています。もし、必要であればそれらを使えば問題はありません。

以下が公式ドキュメントの「[19.5. asyncio — Asynchronous I/O, event loop, coroutines and
tasks](https://docs.python.org/3.7/library/asyncio-stream.html#tcp-echo-
server-using-streams)」にあるTCP echo サーバーの例です。随分楽に書けるようになっています。

[code]

    import asyncio
    
    async def handle_echo(reader, writer):
        data = await reader.read(100)
        message = data.decode()
        addr = writer.get_extra_info('peername')
        print("Received %r from %r" % (message, addr))
    
        print("Send: %r" % message)
        writer.write(data)
        await writer.drain()
    
        print("Close the client socket")
        writer.close()
    
    loop = asyncio.get_event_loop()
    coro = asyncio.start_server(handle_echo, '127.0.0.1', 8888, loop=loop)
    server = loop.run_until_complete(coro)
    
    # Serve requests until Ctrl+C is pressed
    print('Serving on {}'.format(server.sockets[0].getsockname()))
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    
    # Close the server
    server.close()
    loop.run_until_complete(server.wait_closed())
    loop.close()
    
[/code]


CC BY-SA 4.0
2018-08-01T03:07:08.453
47150
2018-08-02T03:22:50.693
2018-08-02T03:22:50.693
15171
15171
47145
answer


そのページで「愚かな解法」と書いてあるのは、ノンブロッキングについてでなく、ビジーループについてです。

質問のコードで、`soc.setblocking(0)` の後にどのようなコードが続くのか判りませんが、  
そこで、データの到着をループを繰り返す **以外** の方法(select 等)で待っているなら、  
それはビジーループではありません。

`soc` からデータが読み出せるまで何度も何度も何度も何度も `soc.recv()`
を繰り返すのがビジーループです。何度も何度も何度も何度も繰り返すので、当然 CPU 負荷は高くなります。

CPU 負荷が低いならビジーループになっていないのでしょうから、問題ありません。  
(ただし、ビジーループのはずなのに CPU 負荷が低いということなら、プログラムがあなたの意図した通りに動作していないということですが)

ノンブロッキングソケットは、select 等を使ってビジーループにならないように使えば、  
とても便利だと思います。避けないといけないようなものではありません。


CC BY-SA 4.0
2018-08-01T15:49:14.880
47165
2018-08-01T15:49:14.880
5288
47145
answer