ホーム >
Pythonでネットワークプログラミング
TCP通信
サーバ側
サーバ側のプログラムは基本的に、
- socketでソケットを作成
- bindでアドレスとポート番号を指定
- listenでクライアントの接続を待つ
- acceptでクライアントの接続を受け付ける
- sendやrecvを使ってクライアントのデータの送受信を行う
- closeでソケットを閉じる
の流れで行う。
- tcp_server1.py
from __future__ import print_function
import socket
from contextlib import closing
def main():
host = '127.0.0.1'
port = 4000
backlog = 10
bufsize = 4096
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
with closing(sock):
sock.bind((host, port))
sock.listen(backlog)
while True:
conn, address = sock.accept()
with closing(conn):
msg = conn.recv(bufsize)
print(msg)
conn.send(msg)
return
if __name__ == '__main__':
main()
クライアント側
クライアント側では、ソケットを作成した後にconnectを使ってサーバに接続し、通信完了後にcloseを実行する。
- tcp_client1.py
from __future__ import print_function
import socket
from contextlib import closing
def main():
host = '127.0.0.1'
port = 4000
bufsize = 4096
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
with closing(sock):
sock.connect((host, port))
sock.send(b'Hello world')
print(sock.recv(bufsize))
return
if __name__ == '__main__':
main()
selectを使用する
上のサーバ側のプログラムでは、acceptでクライアントの接続を待っている間にプログラムがブロックするため、同時に複数のクライアントと通信を行うことができない。 selectを使用すると複数のソケットを同時に監視することができるので、複数のクライアントと同時に通信を行うことができるようになる。
- tcp_server2.py
from __future__ import print_function
import socket
import select
def main():
host = '127.0.0.1'
port = 4000
backlog = 10
bufsize = 4096
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
readfds = set([server_sock])
try:
server_sock.bind((host, port))
server_sock.listen(backlog)
while True:
rready, wready, xready = select.select(readfds, [], [])
for sock in rready:
if sock is server_sock:
conn, address = server_sock.accept()
readfds.add(conn)
else:
msg = sock.recv(bufsize)
if len(msg) == 0:
sock.close()
readfds.remove(sock)
else:
print(msg)
sock.send(msg)
finally:
for sock in readfds:
sock.close()
return
if __name__ == '__main__':
main()
- tcp_client2.py
from __future__ import print_function
import sys
import socket
from contextlib import closing
def main():
host = '127.0.0.1'
port = 4000
bufsize = 4096
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
with closing(sock):
sock.connect((host, port))
while True:
line = sys.stdin.readline().rstrip()
if len(line) == 0:
break
sock.send(line.encode('utf-8'))
print(sock.recv(bufsize))
return
if __name__ == '__main__':
main()
UDP通信
送信側
UDPソケットを作成する場合、socket関数の引数にSOCK_DGRAMを指定する。 送信側はsendの代わりにsendtoで送信先を指定する必要がある。
- send_udp.py
from __future__ import print_function
import socket
import time
from contextlib import closing
def main():
host = '127.0.0.1'
port = 4000
count = 0
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
with closing(sock):
while True:
message = 'Hello world : {0}'.format(count).encode('utf-8')
print(message)
sock.sendto(message, (host, port))
count += 1
time.sleep(1)
return
if __name__ == '__main__':
main()
受信側
受信側はbindで受信先を指定してからrecvでデータを受信する。
- recv_udp.py
from __future__ import print_function
import socket
from contextlib import closing
def main():
host = '127.0.0.1'
port = 4000
bufsize = 4096
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
with closing(sock):
sock.bind((host, port))
while True:
print(sock.recv(bufsize))
return
if __name__ == '__main__':
main()
マルチキャスト通信
マルチキャスト通信の送信側のプログラムでは、setsockoptを使ってIP_MULTICAST_IFソケットオプションを設定する。 また、IP_MULTICAST_LOOPを0(false)に設定すると、マルチキャストパケットがローカルなソケットにループバックされなくなるので、自分が送信したパケットを受け取る際のオーバーヘッドを軽減させることができる。
- send_udp_multicast.py
from __future__ import print_function
import socket
import time
from contextlib import closing
def main():
local_address = '192.168.0.1' # 送信側のPCのIPアドレス
multicast_group = '239.255.0.1' # マルチキャストアドレス
port = 4000
with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as sock:
# sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 0)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(local_address))
count = 0
while True:
message = 'Hello world : {0}'.format(count).encode('utf-8')
print(message)
sock.sendto(message, (multicast_group, port))
count += 1
time.sleep(0.5)
return
if __name__ == '__main__':
main()
受信側のプログラムでは、IP_ADD_MEMBERSHIPソケットオプションを設定し、マルチキャストグループに参加する必要がある。 また、bindの前にSO_REUSEADDRを1(true)に設定しておくことで、複数のプロセスからマルチキャストを受信できるようになる。
なお、ファイアウォールが有効になっていると通信できないことがあるので注意。
- recv_udp_multicast.py
from __future__ import print_function
import socket
from contextlib import closing
def main():
local_address = '192.168.0.2' # 受信側のPCのIPアドレス
multicast_group = '239.255.0.1' # マルチキャストアドレス
port = 4000
bufsize = 4096
with closing(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', port))
sock.setsockopt(socket.IPPROTO_IP,
socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(multicast_group) + socket.inet_aton(local_address))
while True:
print(sock.recv(bufsize))
return
if __name__ == '__main__':
main()