esp-open-rtosを使ってみる

UDPのアドレス指定方法


esp-open-rtosから少し脱線しますが、UDPのAddress指定方法には以下の4つの指定方法が有ります。
・Limited broadcast address
・Directed broadcast address
・Multicast address
・Unicast address

これ以外にPython独自のアドレス指定方法として空白(長さ=0の文字列)とオール0が有ります。

この違いについては、色々なところに解説が有りますが、違いを理解しておかないと思わぬところでハマります。(メッチャハマりました)
例えば送信側をLimited broadcast address、受信側をDirected broadcast addressで指定すると、データの送受信ができません。


Limited broadcast address

255.255.255.255、あるいは<broadcast>で表すアドレスで、ルータを超えることができません。
送信側も受信側もLimited broadcast addressを指定する必要が有ります。
Pythonのコー ドは以下の様になります。

送信側

#!/usr/bin/python
#-*- encoding: utf-8 -*-
# Limited broadcast addressでの送信
# 自分が今いるネットワークに対するブロードキャストアドレス
# ルータ越えが制限されている

import socket
import datetime
import time

ADDRESS = "255.255.255.255" # limited broadcast address
#ADDRESS = "<broadcast>" # limited broadcast address
PORT = 9876

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

while True:
    msg = str(datetime.datetime.now())
    s.sendto(msg.encode('utf-8'), (ADDRESS, PORT))
    time.sleep(1)

s.close()

255.255.255.255、あるいは<broadcast>のどちらでも同じ動きをします。

受信側

#!/usr/bin/python
#-*- encoding: utf-8 -*-
# Limited broadcast addressでの受信
# 自分が今いるネットワークに対するブロードキャストアドレス
# ルータ越えが制限されている

import socket
import select

ADDRESS = '<broadcast>'
PORT = 9876

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((ADDRESS, PORT)) # limited broadcast address
s.setblocking(0)

while True:
    result = select.select([s],[],[])
    msg = result[0][0].recv(1024)
    print(msg.strip())


Directed broadcast address

192.168.10.255など、最後のオクテットだけを255で表すアドレスで、ルータを超えることが可能です。
送信側も受信側もDirected broadcast addressを指定する必要が有ります。

192.168.10.255 をブロードキャストアドレスとして紹介しているページと、
255.255.255.255 をブロードキャストアドレスとして紹介しているページが有り、これが混乱のもとになっています。
また、単に「ブロードキャストアドレスで受信」と言われても、LimitedなのかDirectedなのか、区別できません。

Pythonのコードは以下の様になります。

送信側

#!/usr/bin/python
#-*- encoding: utf-8 -*-
# Directed broadcast addressでの送信
# ネットワーク部で指定されたネットワークに対するブロードキャストアドレス
# ルータ越えが可能

import socket
import datetime
import time

ADDRESS = '192.168.10.255' # directed broadcast address
PORT = 9876

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

while True:
    msg = str(datetime.datetime.now())
    s.sendto(msg.encode('utf-8'), (ADDRESS, PORT))
    time.sleep(1)

s.close()

受信側

#!/usr/bin/python
#-*- encoding: utf-8 -*-
# Directed broadcast addressでの受信
# ネットワーク部で指定されたネットワークに対するブロードキャストアドレス
# ルータ越えが可能

import socket
import select

ADDRESS = "192.168.10.255" # directed broadcast address
#ADDRESS = ''
PORT = 9876

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((ADDRESS, PORT)) # directed broadcast address
s.setblocking(0)

while True:
    result = select.select([s],[],[])
    msg = result[0][0].recv(1024)
    print(msg.strip())


Multicast address

マルチキャストアドレスという特殊なアドレス(224.0.0.0〜239.255.255.255)を使用して、特定のグループに所属する全て のPCにデータを送ります。
使ったことが無いので、これ以上のことは分かりません。


Unicast address

192.168.10.41など、全てのオクテットを指定するアドレスで、ルータを超えることが可能です。
送信側も受信側もUnicast addressを指定する必要が有ります。
Pythonのコードは以下の様になります。

送信側

#!/usr/bin/python
#-*- encoding: utf-8 -*-
# Unicast addressでの送信
# ルータ越えが可能

import socket
import datetime
import time

ADDRESS = "192.168.10.41" # unicast address
PORT = 9876

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

while True:
    msg = str(datetime.datetime.now())
    s.sendto(msg.encode('utf-8'), (ADDRESS, PORT))
    time.sleep(1)

s.close()

受信側

#!/usr/bin/python
#-*- encoding: utf-8 -*-
# Unicast addressでの受信
# ルータ越えが可能

import socket
import select

ADDRESS = '192.168.10.41'
PORT = 9876

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((ADDRESS, PORT)) # directed broadcast address
s.setblocking(0)

while True:
    result = select.select([s],[],[])
    msg = result[0][0].recv(1024)
    print(msg.strip())


空白(長さ=0の文字列)

python独自の指定方法ですが、受信側に限りアドレスを空白(長さ=0の文字列)で指定することができます。
ブロードキャストの受信コードのサンプルに、このアドレスが使われていることが有り、これも混乱する元になっています。

受信側でこのアドレスを指定した場合、送信側のアドレス指定は何でも構いません。
送信側がLimited Broadcast/Directed Broadcast/Unicastのいずれでも、受信することができます。

受信側

#!/usr/bin/python
#-*- encoding: utf-8 -*-
# 空白アドレスでの受信

import socket
import select

#ADDRESS = "192.168.10.255" # directed broadcast address
ADDRESS = ''
PORT = 9876

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((ADDRESS, PORT)) # directed broadcast address
s.setblocking(0)

while True:
    result = select.select([s],[],[])
    msg = result[0][0].recv(1024)
    print(msg.strip())


0.0.0.0のアドレス

これもpython独自の指定方法ですが、受信側に限りアドレスを0.0.0.0で指定することができます。
ブロードキャストの受信コードのサンプルに、このアドレスが使われていることが有り、これも混乱する元になっています。

受信側でこのアドレスを指定した場合、送信側のアドレス指定は何でも構いません。
送信側がLimited Broadcast/Directed Broadcast/Unicastのいずれでも、受信することができます。

受信側

#!/usr/bin/python
#-*- encoding: utf-8 -*-
# 空白アドレスでの受信

import socket
import select

#ADDRESS = "192.168.10.255" # directed broadcast address
ADDRESS = '0.0.0.0'
PORT = 9876

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((ADDRESS, PORT)) # directed broadcast address
s.setblocking(0)

while True:
    result = select.select([s],[],[])
    msg = result[0][0].recv(1024)
    print(msg.strip())



家庭内LANでルーターが1つしかなければ、Limited BroadcastでもDirected Broadcastでも、あまり変わりは有りませんが、
どちらかに統一しておかないと面倒です。
また、pythonのrecv()はpython2とpython3で、受信データの型が変わっているので注意が必要です。
python2では受信データは文字列です。
recv type=<type 'str'>

一方、python3では受信データはbytes配列となります。
recv type=<class 'bytes'>

bytes配列の場合、以下のコードでstr型に変換することができます。
import select, socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('<broadcast>', 9876))
s.setblocking(0)

while True:
    result = select.select([s],[],[])
    msg = result[0][0].recv(1024)
    print("recv type={}".format(type(msg)))
    if (type(msg) is bytes):
        #print("bytes")
        msg=msg.decode('utf-8')
        print("type(msg)={}".format(type(msg)))
    print(msg)


続く....