12、Python-ネットワークプログラミング

17959 ワード

1、ソケット1.1 socketモジュールソケットはネットワークプログラミングの基本コンポーネントであり、一般的にサーバ側ソケットとクライアントソケットを含む.
サーバ側の作成手順は次のとおりです.
 1 import socket
 2 
 3 s = socket.socket()
 4 
 5 host = socket.gethostname()
 6 port = 1234
 7 try:
 8     s.bind((host, port))
 9 except Exception, e:
10     print e
11     s.close()
12 
13 s.listen(5)
14 while True:
15     c, addr = s.accept()
16     print 'Got connection from', addr
17     c.send('Thank you for connecting')
18 c.close()

クライアントの作成手順は次のとおりです.
 1 import socket, time
 2 
 3 s = socket.socket()
 4 
 5 host = socket.gethostname()
 6 port = 1234
 7 
 8 s.connect((host, port))
 9 print s.recv(1024)
10 while True:
11     command = 'hello
' 12 s.send(command) 13 time.sleep(1)

このプロセスの通信方式は一問一答の形式であるため,ブロックまたは同期ネットワークプログラミングとも呼ばれる.1.2 SocketServerとその友人SocketServerモジュールは、BaseHTTPServer、SimpleHTTPServer、CGIHTTPServer、SimpleXML RPCServer、DocXML RPCServerを含む標準ライブラリ内の多くのサーバフレームワークの基礎であり、これらのサーバフレームワークはすべてベースサーバに特定の機能を追加しています.SocketServerは4つの基本クラスを含む:TCPソケットストリームのTCPServer;UDPデータに対してソケットのUDPServerを報告します;および対象性の弱いUnixStreamServerとUnixDatagramServerです.SocketServerサーバフレームワークを使用すると、サーバが要求を受信するたびに、要求ハンドラがインスタンス化され、その様々な処理方法(handler methods)が要求を処理するときに呼び出されます.基本的なBaseRequestHandlerクラスは、すべての操作をプロセッサというhandleというメソッドに配置し、このメソッドはサーバによって呼び出されます.その後、この方法で属性selfにアクセスすることができる.requestのクライアントソケット.ストリーム(TCPServer)を使用する場合、StreamRequestHandlerクラスを使用して、2つのプロパティ、selfを作成できます.rfile(読み取り用)とself.wfile(書き込み用).
 1 from SocketServer import TCPServer, StreamRequestHandler
 2 
 3 class Handler(StreamRequestHandler):
 4 
 5     def handle(self):
 6         addr = self.request.getpeername()
 7        
 8         print 'Got connection from', addr
 9         self.wfile.write('Thank you for connecting')
10 server = TCPServer(('', 1234), Handler)
11 server.serve_forever()

2、複数の接続は同時に複数のクライアントがサービス側に接続して要求処理を行うことができる.この目的を達成するには、フォーク(forking)、スレッド(threading)、および非同期I/O(asynchronous I/O)の3つの主要な方法があります.それぞれの欠点があります.分岐はリソースを占め、クライアントが多すぎると、分岐はうまく分岐できません.スレッド処理は同期の問題を引き起こす可能性があります.分岐とは:1つのプロセス(実行中のプログラム)を分岐すると、基本的にコピーされ、分岐後の2つのプロセスは現在の実行ポイントから実行され続け、各プロセスには独自のメモリコピー(変数など)があります.1つのプロセス(元のプロセス)が親プロセスになり、もう1つのプロセス(コピーされた)が子プロセスになります.分岐プロセスは並列に実行されるため、クライアント間で待つ必要はありません.しかし、分岐はリソースを消費します(各分岐のプロセスには独自のメモリが必要です).これにはもう一つの選択肢があります.スレッドです.スレッドとは:スレッドは軽量レベルのプロセスまたはサブプロセスであり、すべてのスレッドは同じ(本物の)プロセスに存在し、メモリを共有します.リソース消費の低下には、スレッドがメモリを共有しているため、変数が競合しないことを確認したり、同じ時間に同じ内容を変更したりする必要があります.これは混乱をもたらします.SocketServerフレームワークを使用してフォークまたはスレッドサーバを作成するのは簡単すぎて、ほとんど説明する必要はありません.Windowsでは分岐はサポートされていません.分岐テクノロジーを使用したサーバ:
 1 from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler
 2 
 3 class Server(ForkingMixIn, TCPServer): pass
 4 
 5 class Handler(StreamRequestHandler):
 6 
 7     def handle(self):
 8         addr = self.request.getpeername()
 9         print 'Got connection from', addr
10         self.wfile.write('Thank you for connecting')
11 
12 server = Server(('', 1234), Handler)
13 server.serve_forever()

スレッド処理を使用するサーバ:
from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler

class Server(ThreadingMixIn, TCPServer): pass

class Handler(StreamRequestHandler):

    def handle(self):
        addr = self.request.getpeername()
        print 'Got connection from', addr
        self.wfile.write('Thank you for connecting')
      
server = Server(('', 1234), Handler)
server.serve_forever()

非同期I/Oとは:サーバがクライアントと通信する場合、クライアントからのデータが不連続である可能性があります.分岐またはスレッド処理を使用する場合は、問題ではありません.1つのプログラムがデータを待っている場合、別の並列プログラムは、独自のクライアントを処理し続けることができます.他の処理方法は、所与の時間内に本当に通信を行うクライアントのみを処理することである.これはasyncore/asynchatフレームワークで採用されている方法で、この機能の基礎はselect関数であり、poll関数が利用可能であれば、両方の関数はselectモジュールから来ています.ここでpoll関数の伸縮性はより良いが,UNIXシステムでのみ使用できる.Select関数には必須パラメータとして3つのシーケンスがあり、4番目のパラメータとしてオプションのタイムアウト時間があります.この3つのシーケンスは、入力、出力、および例外に使用されるソケットファイル記述子です.タイムアウト時間が指定されていない場合、selectはブロックされ、ファイル記述子が行動の準備ができているまで待機状態になります.所定のタイムアウト時間が与えられた場合、selectは最大で所定のタイムアウト時間をブロックする.指定されたタイムアウト時間が0の場合、selectはブロックされません.Selectの戻り値は、対応するパラメータを表す各アクティブなサブセットの長さ3のメタグループの3つのシーケンスです.
 1 import socket, select
 2 
 3 s = socket.socket()
 4 
 5 host = socket.gethostname()
 6 port = 1234
 7 s.bind((host, port))
 8 
 9 s.listen(5)
10 inputs = [s]
11 while True:
12     rs, ws, es = select.select(inputs, [], [])
13     for r in rs:
14         if r is s:
15             c, addr = s.accept()
16             print 'Got connection from', addr
17             inputs.append(c)
18         else:
19             try:
20                 data = r.recv(1024)
21                 disconnected = not data
22             except socket.error:
23                 disconnected = True
24             if disconnected:
25                 print r.getpeername(), 'disconnected'
26                 inputs.remove(r)
27             else:
28                 print data

Pollメソッドはselectより簡単に使用できます.pollを呼び出すと、pollオブジェクトが得られます.次にpollオブジェクトのregisterメソッドを使用してファイル記述子(またはfilenoメソッド付きオブジェクト)を登録します.登録後、unregisterメソッドを使用して登録されたオブジェクトを削除できます.ソケットオブジェクトを登録した後、pollメソッド(オプションのタイムアウト時間パラメータを含む)を呼び出し、fdがファイル記述子であり、eventが何が起こったかを示す(fd,event)フォーマットリスト(空の可能性がある)を得ることができます.イベントオブジェクトはビットマスクである、ビットと操作によってイベントのタイプを判断することができる.selectモジュールにおけるpollingイベント定数イベント名記述POLLINファイル記述子からのデータを読み込むPOLLPRIファイル記述子からの緊急データを読み込むPOLLOUTファイル記述子はデータを準備しており、書き込み中にPOLLERRのファイル記述子に関するエラーが発生しないPOLLHUPが保留中であり、接続が失われたPOLLNVALは無効な要求であり、接続が開かれていない
 1 import socket, select
 2 
 3 s = socket.socket()
 4 
 5 host = socket.gethostname()
 6 port = 1234
 7 s.bind((host, port))
 8 
 9 fdmap = {s.fileno(): s}
10 
11 s.listen(5)
12 p = select.poll()
13 p.register(s)
14 while True:
15     events = p.poll()
16     for fd, event in events:
17         if fdmap[fd] is s:
18             c, addr = s.accept()
19             print 'Got connection from', addr
20             p.register(c)
21             fdmap[c.fileno()] = c
22         elif event & select.POLLIN:
23             data = fdmap[fd].recv(1024)
24             if not data: # No data -- connection closed
25                 print fdmap[fd].getpeername(), 'disconnected'
26                 p.unregister(fd)
27                 del fdmap[fd]
28             else:
29                 print data