Pythonã®AsyncioãæŽ»çšããå¹ççã§ã¹ã±ãŒã©ãã«ãªã°ããŒãã«éä¿¡ã·ã¹ãã åãã«å ç¢ãªã«ã¹ã¿ã ãããã¯ãŒã¯ãããã³ã«ãèšèšã»å®è£ ããŸãããã
Asyncioãããã³ã«å®è£ ã®ç¿åŸïŒã°ããŒãã«ã¢ããªã±ãŒã·ã§ã³åãã«ã¹ã¿ã ãããã¯ãŒã¯ãããã³ã«ã®æ§ç¯
仿¥ã®çžäºæ¥ç¶ãããäžçã§ã¯ãã¢ããªã±ãŒã·ã§ã³ã¯ãŸããŸãå¹ççã§ä¿¡é Œæ§ã®é«ããããã¯ãŒã¯éä¿¡ã«äŸåããŠããŸããHTTPãFTPãWebSocketãªã©ã®æšæºãããã³ã«ã¯å¹
åºãããŒãºã«å¯Ÿå¿ããŸãããæ¢è£œã®ãœãªã¥ãŒã·ã§ã³ã§ã¯äžååãªã·ããªãªãå€ãååšããŸãã髿§èœãªéèã·ã¹ãã ããªã¢ã«ã¿ã€ã ã²ãŒã ãµãŒããŒããªãŒããŒã¡ã€ãã®IoTããã€ã¹éä¿¡ãç¹æ®ãªç£æ¥çšå¶åŸ¡ãªã©ãæ§ç¯ããå Žåãã«ã¹ã¿ã ãããã¯ãŒã¯ãããã³ã«ãå®çŸ©ãå®è£
ããèœåã¯éåžžã«è²Žéã§ããPythonã®asyncio
ã©ã€ãã©ãªã¯ããŸãã«ãã®ç®çã®ããã«å
ç¢ã§æè»ããããŠé«æ§èœãªãã¬ãŒã ã¯ãŒã¯ãæäŸããŸãã
ãã®å
æ¬çãªã¬ã€ãã§ã¯ãasyncio
ã®ãããã³ã«å®è£
ã®è€éãã«æ·±ãèžã¿èŸŒã¿ãã°ããŒãã«ãªèŠèŽè
åãã«ã¹ã±ãŒã©ãã«ã§å埩åã®ããç¬èªã®ã«ã¹ã¿ã ãããã¯ãŒã¯ãããã³ã«ãèšèšãæ§ç¯ããããã€ããããã®åãäžããŸããå°ççãªå¢çãã€ã³ãã©ã®å€æ§æ§ã«é¢ããããçŸä»£ã®åæ£ã·ã¹ãã ã®èŠæ±ãæºããã«ã¹ã¿ã ãããã³ã«ã確å®ã«ããããã®ãã³ã¢ã³ã³ã»ãããå®çšçãªäŸãããã³ãã¹ããã©ã¯ãã£ã¹ãæ¢ããŸãã
åºç€ïŒAsyncioã®ãããã¯ãŒã¯ããªããã£ããçè§£ãã
ã«ã¹ã¿ã ãããã³ã«ã«æ·±ãå
¥ã蟌ãåã«ãasyncio
ããããã¯ãŒã¯ããã°ã©ãã³ã°ã®ããã«æäŸããåºæ¬çãªæ§æèŠçŽ ãææ¡ããããšãéèŠã§ããasyncio
ã¯ãasync
/await
æ§æã䜿çšããŠäžŠè¡ã³ãŒããèšè¿°ããããã®ã©ã€ãã©ãªã§ãããããã¯ãŒãã³ã°ã®å Žåããã©ã³ã¹ããŒããšãããã³ã«ã«åºã¥ããé«ã¬ãã«APIãéããŠãäœã¬ãã«ã®ãœã±ããæäœã®è€éããæœè±¡åããŸãã
ã€ãã³ãã«ãŒãïŒéåææäœã®ãªãŒã±ã¹ãã¬ãŒã¿ãŒ
asyncio
ã®ã€ãã³ãã«ãŒãã¯ããã¹ãŠã®éåæã¿ã¹ã¯ãšã³ãŒã«ããã¯ãå®è¡ããäžå¿çãªå®è¡è
ã§ããI/Oã€ãã³ãïŒãœã±ããã§ããŒã¿ãå°çããããæ¥ç¶ã確ç«ãããããããªã©ïŒãç£èŠããé©åãªãã³ãã©ãŒã«ãã£ã¹ãããããŸããã€ãã³ãã«ãŒããçè§£ããããšã¯ãasyncio
ãéããããã³ã°I/Oãã©ã®ããã«å®çŸããããçè§£ããäžã§éèŠã§ãã
ãã©ã³ã¹ããŒãïŒããŒã¿è»¢éã®ããã®é 管
asyncio
ã«ããããã©ã³ã¹ããŒãã¯ãå®éã®ãã€ãã¬ãã«I/Oãæ
åœããŸãããããã¯ãŒã¯æ¥ç¶ãä»ããŠããŒã¿ãéåä¿¡ããäœã¬ãã«ã®è©³çްãåŠçããŸããasyncio
ã¯ããŸããŸãªãã©ã³ã¹ããŒãã¿ã€ããæäŸããŸãã
- TCPãã©ã³ã¹ããŒãïŒã¹ããªãŒã ããŒã¹ã§ãä¿¡é Œæ§ãé«ããé åºãä¿èšŒããããšã©ãŒãã§ãã¯ãããéä¿¡çšïŒäŸïŒ
loop.create_server()
ãloop.create_connection()
ïŒã - UDPãã©ã³ã¹ããŒãïŒããŒã¿ã°ã©ã ããŒã¹ã§ãä¿¡é Œæ§ãäœããã³ãã¯ã·ã§ã³ã¬ã¹ãªéä¿¡çšïŒäŸïŒ
loop.create_datagram_endpoint()
ïŒã - SSLãã©ã³ã¹ããŒãïŒTCPäžã®æå·åãããã¬ã€ã€ãŒã§ãæ©å¯ããŒã¿ã®ã»ãã¥ãªãã£ãæäŸããŸãã
- Unixãã¡ã€ã³ãœã±ãããã©ã³ã¹ããŒãïŒåäžãã¹ãäžã§ã®ããã»ã¹ééä¿¡çšã
ãã©ã³ã¹ããŒããšå¯Ÿè©±ããŠãã€ããæžã蟌ã¿ïŒtransport.write(data)
ïŒãæ¥ç¶ãéããŸãïŒtransport.close()
ïŒããã ããéåžžããã©ã³ã¹ããŒãããçŽæ¥èªã¿åãããšã¯ãããŸãããããã¯ãããã³ã«ã®ä»äºã§ãã
ãããã³ã«ïŒããŒã¿ã®è§£éæ¹æ³ãå®çŸ©ãã
ãããã³ã«ã¯ãåä¿¡ããŒã¿ãè§£æããéä¿¡ããŒã¿ãçæããããã®ããžãã¯ã眮ãããå Žæã§ããããã¯ãç¹å®ã®ã€ãã³ãïŒäŸïŒããŒã¿åä¿¡ãæ¥ç¶ç¢ºç«ãæ¥ç¶åªå€±ïŒãçºçãããšãã«ãã©ã³ã¹ããŒãã«ãã£ãŠåŒã³åºãããäžé£ã®ã¡ãœãããå®è£
ãããªããžã§ã¯ãã§ããasyncio
ã¯ãã«ã¹ã¿ã ãããã³ã«ãå®è£
ããããã®2ã€ã®åºæ¬ã¯ã©ã¹ãæäŸããŸãã
asyncio.Protocol
ïŒã¹ããªãŒã ããŒã¹ã®ãããã³ã«ïŒTCPãªã©ïŒçšãasyncio.DatagramProtocol
ïŒããŒã¿ã°ã©ã ããŒã¹ã®ãããã³ã«ïŒUDPãªã©ïŒçšã
ãããã®ãµãã¯ã©ã¹åã«ãããã¢ããªã±ãŒã·ã§ã³ã®ããžãã¯ããããã¯ãŒã¯äžãæµããçã®ãã€ããšã©ã®ããã«çžäºäœçšããããå®çŸ©ããŸãã
asyncio.Protocol
ã«æ·±ãå
¥ã蟌ã
asyncio.Protocol
ã¯ã©ã¹ã¯ãã«ã¹ã¿ã ã¹ããªãŒã ããŒã¹ãããã¯ãŒã¯ãããã³ã«ãæ§ç¯ããããã®èŠã§ãããµãŒããŒãŸãã¯ã¯ã©ã€ã¢ã³ãæ¥ç¶ãäœæãããšãasyncio
ã¯ãããã³ã«ã¯ã©ã¹ãã€ã³ã¹ã¿ã³ã¹åããããããã©ã³ã¹ããŒãã«æ¥ç¶ããŸãããã®åŸããããã³ã«ã€ã³ã¹ã¿ã³ã¹ã¯ããŸããŸãªæ¥ç¶ã€ãã³ãã®ã³ãŒã«ããã¯ãåãåããŸãã
äž»èŠãªãããã³ã«ã¡ãœãã
asyncio.Protocol
ããµãã¯ã©ã¹åããéã«ãªãŒããŒã©ã€ãããäž»èŠãªã¡ãœãããèŠãŠã¿ãŸãããã
connection_made(self, transport)
ãã®ã¡ãœããã¯ãæ¥ç¶ãæ£åžžã«ç¢ºç«ããããšãã«asyncio
ã«ãã£ãŠåŒã³åºãããŸããåŒæ°ãšããŠtransport
ãªããžã§ã¯ããåãåããŸããããã¯éåžžãåŸã§ã¯ã©ã€ã¢ã³ã/ãµãŒããŒã«ããŒã¿ãéä¿¡ããããã«ä¿åããŸããããã¯ãåæèšå®ãå®è¡ãããããŠã§ã«ã«ã ã¡ãã»ãŒãžãéä¿¡ãããããã³ãã·ã§ã€ã¯æé ãéå§ãããããã®ã«çæ³çãªå Žæã§ãã
import asyncio
class MyCustomProtocol(asyncio.Protocol):
def connection_made(self, transport):
self.transport = transport
peername = transport.get_extra_info('peername')
print(f'Connection from {peername}')
self.transport.write(b'Hello! Ready to receive commands.\n')
self.buffer = b'' # Initialize a buffer for incoming data
data_received(self, data)
ããã¯æãéèŠãªã¡ãœããã§ãããã©ã³ã¹ããŒãããããã¯ãŒã¯ããããŒã¿ãåä¿¡ãããã³ã«åŒã³åºãããŸããdata
åŒæ°ã¯ãåä¿¡ããŒã¿ãå«ãbytes
ãªããžã§ã¯ãã§ãããã®ã¡ãœããã®å®è£
ã¯ãã«ã¹ã¿ã ãããã³ã«ã®ã«ãŒã«ã«åŸã£ãŠãããã®çãã€ããè§£æããéšåçãªã¡ãã»ãŒãžããããã¡ãªã³ã°ããé©åãªã¢ã¯ã·ã§ã³ãå®è¡ãã責任ããããŸããããã«ã«ã¹ã¿ã ãããã³ã«ã®ã³ã¢ããžãã¯ããããŸãã
def data_received(self, data):
self.buffer += data
# Our custom protocol: messages are terminated by a newline character.\n
while b'\n' in self.buffer:
message_bytes, self.buffer = self.buffer.split(b'\n', 1)
message = message_bytes.decode('utf-8').strip()
print(f'Received: {message}')
# Process the message based on your protocol's logic
if message == 'GET_TIME':
import datetime
response = f'Current time: {datetime.datetime.now().isoformat()}\n'
self.transport.write(response.encode('utf-8'))
elif message.startswith('ECHO '):
response = f'ECHOING: {message[5:]}\n'
self.transport.write(response.encode('utf-8'))
elif message == 'QUIT':
print('Client requested disconnect.')
self.transport.write(b'Goodbye!\n')
self.transport.close()
return
else:
self.transport.write(b'Unknown command.\n')
ã°ããŒãã«ãªãã¹ããã©ã¯ãã£ã¹ïŒåžžã«éšåçãªã¡ãã»ãŒãžã¯ããŒã¿ããããã¡ãªã³ã°ããå®å šãªåäœã®ã¿ãåŠçããããšã§å¯ŸåŠããŠãã ããããããã¯ãŒã¯ã®æçåãäºæããå ç¢ãªè§£ææŠç¥ã䜿çšããŠãã ããã
connection_lost(self, exc)
ãã®ã¡ãœããã¯ãæ¥ç¶ãã¯ããŒãºãããã倱ããããšãã«åŒã³åºãããŸããæ¥ç¶ãæ£åžžã«ã¯ããŒãºãããå Žåã¯exc
åŒæ°ã¯None
ã«ãªãããšã©ãŒãçºçããå Žåã¯äŸå€ãªããžã§ã¯ãã«ãªããŸããããã¯ããªãœãŒã¹ã®è§£æŸãåæã€ãã³ãã®ãã°èšé²ãªã©ãå¿
èŠãªã¯ãªãŒã³ã¢ãããå®è¡ããå Žæã§ãã
def connection_lost(self, exc):
if exc:
print(f'Connection lost with error: {exc}')
else:
print('Connection closed cleanly.')
self.transport = None # Clear reference
ãããŒå¶åŸ¡ïŒpause_writing()
ãšresume_writing()
ã¢ããªã±ãŒã·ã§ã³ãããã¯ãã¬ãã·ã£ãŒïŒäŸïŒé«éãªéä¿¡è
ãäœéãªåä¿¡è
ãå§åããïŒãåŠçããå¿
èŠãããé«åºŠãªã·ããªãªã§ã¯ãasyncio.Protocol
ã¯ãããŒå¶åŸ¡ã®ããã®ã¡ãœãããæäŸããŸãããã©ã³ã¹ããŒãã®ãããã¡ãç¹å®ã®ãã€ãŠã©ãŒã¿ãŒããŒã¯ã«éãããšããããã³ã«äžã§pause_writing()
ãåŒã³åºãããŸãããããã¡ãååã«ç©ºã«ãªããšãresume_writing()
ãåŒã³åºãããŸãããããã®ã¡ãœããããªãŒããŒã©ã€ãããŠãå¿
èŠã«å¿ããŠã¢ããªã±ãŒã·ã§ã³ã¬ãã«ã®ãããŒå¶åŸ¡ãå®è£
ã§ããŸãããå€ãã®ãŠãŒã¹ã±ãŒã¹ã§ã¯asyncio
ã®å
éšãããã¡ãªã³ã°ããããééçã«åŠçããŸãã
ã«ã¹ã¿ã ãããã³ã«ã®èšèš
广çãªã«ã¹ã¿ã ãããã³ã«ãèšèšããã«ã¯ããã®æ§é ãç¶æ 管çããšã©ãŒåŠçãã»ãã¥ãªãã£ãæ éã«æ€èšããå¿ èŠããããŸããã°ããŒãã«ã¢ããªã±ãŒã·ã§ã³ã®å Žåãåœéåã倿§ãªãããã¯ãŒã¯æ¡ä»¶ãªã©ã®è¿œå ã®åŽé¢ãéèŠã«ãªããŸãã
ãããã³ã«æ§é ïŒã¡ãã»ãŒãžã®ãã¬ãŒãã³ã°æ¹æ³
æãåºæ¬çãªåŽé¢ã¯ãã¡ãã»ãŒãžãã©ã®ããã«åºåãããè§£éããããã§ããäžè¬çãªã¢ãããŒãã«ã¯æ¬¡ã®ãã®ããããŸãã
- é·ããæ¥é èŸãšããã¡ãã»ãŒãžïŒåã¡ãã»ãŒãžã¯ããã®åŸã«ç¶ããã€ããŒãã®é·ãã瀺ãåºå®ãµã€ãºã®ããããŒã§å§ãŸããŸããããã¯ä»»æã®ããŒã¿ãéšåçãªèªã¿åãã«å¯ŸããŠå ç¢ã§ããäŸïŒãã€ããŒãã®é·ãã瀺ã4ãã€ãæŽæ°ïŒãããã¯ãŒã¯ãã€ããªãŒããŒïŒã®åŸã«ãã€ããŒããã€ããç¶ãã
- åºåãæåä»ãã¡ãã»ãŒãžïŒã¡ãã»ãŒãžã¯ç¹å®ã®ãã€ãã·ãŒã±ã³ã¹ïŒäŸïŒæ¹è¡æå
\n
ããŸãã¯NULLãã€ã\x00
ïŒã§çµäºããŸããããã¯ããåçŽã§ãããåºåãæåãã¡ãã»ãŒãžãã€ããŒãèªäœã®äžã«çŸããå¯èœæ§ãããããšã¹ã±ãŒãã·ãŒã±ã³ã¹ãå¿ èŠã«ãªãããåé¡ãšãªãããšããããŸãã - åºå®é·ã¡ãã»ãŒãžïŒãã¹ãŠã®ã¡ãã»ãŒãžã¯äºåã«å®çŸ©ãããäžå®ã®é·ããæã£ãŠããŸããåçŽã§ãããã¡ãã»ãŒãžã®å 容ãå€åãããããå®çšçã§ãªãããšãå€ãã§ãã
- ãã€ããªããã¢ãããŒãïŒããããŒã«é·ããæ¥é èŸãšããŠä»ãããã€ããŒãå ã§ãã£ãŒã«ããåºåãæ¹æ³ãçµã¿åãããŸãã
ã°ããŒãã«ãªèæ
®äºé
ïŒè€æ°ãã€ãæŽæ°ã§é·ããæ¥é èŸãšããå Žåãåžžã«ãšã³ãã£ã¢ã³ãã¹ïŒãã€ããªãŒããŒïŒãæå®ããŠãã ããããããã¯ãŒã¯ãã€ããªãŒããŒïŒããã°ãšã³ãã£ã¢ã³ïŒã¯ãäžçäžã®ç°ãªãããã»ããµã¢ãŒããã¯ãã£éã§ã®çžäºéçšæ§ã確ä¿ããããã®äžè¬çãªæ
£äŸã§ããPythonã®struct
ã¢ãžã¥ãŒã«ã¯ããã«åªããŠããŸãã
ã·ãªã¢ã©ã€ãŒãŒã·ã§ã³åœ¢åŒ
ãã¬ãŒãã³ã°ã«å ããŠãã¡ãã»ãŒãžå ã®å®éã®ããŒã¿ãã©ã®ããã«æ§é åãããã·ãªã¢ã©ã€ãºãããããæ€èšããŠãã ããã
- JSONïŒäººéãèªããåºããµããŒããããŠãããåçŽãªããŒã¿æ§é ã«é©ããŠããŸãããåé·ã«ãªãããšããããŸãã
json.dumps()
ããã³json.loads()
ã䜿çšããŸãã - Protocol Buffers (Protobuf) / FlatBuffers / MessagePackïŒéåžžã«å¹ççãªãã€ããªã·ãªã¢ã©ã€ãŒãŒã·ã§ã³åœ¢åŒã§ãããã©ãŒãã³ã¹ãéèŠãªã¢ããªã±ãŒã·ã§ã³ãå°ããªã¡ãã»ãŒãžãµã€ãºã«æé©ã§ããã¹ããŒãå®çŸ©ãå¿ èŠã§ãã
- ã«ã¹ã¿ã ãã€ããªïŒæå€§éã®å¶åŸ¡ãšå¹çã®ããã«ãPythonã®
struct
ã¢ãžã¥ãŒã«ãŸãã¯bytes
æäœã䜿çšããŠç¬èªã®ãã€ããªæ§é ãå®çŸ©ã§ããŸããããã«ã¯ã詳现ïŒãšã³ãã£ã¢ã³ãã¹ãåºå®ãµã€ãºãã£ãŒã«ãããã©ã°ïŒãžã®çްå¿ã®æ³šæãå¿ èŠã§ãã - ããã¹ãããŒã¹ (CSV, XML)ïŒå¯èœã§ã¯ãããŸãããã«ã¹ã¿ã ãããã³ã«ã§ã¯JSONãããå¹çãäœãã£ãããä¿¡é Œæ§ã®é«ãè§£æãé£ããã£ããããããšããããããŸãã
ã°ããŒãã«ãªèæ ®äºé ïŒããã¹ããæ±ãéã¯ãåžžã«UTF-8ãšã³ã³ãŒãã£ã³ã°ãããã©ã«ãã«ããŠãã ãããããã¯ãã»ãŒãã¹ãŠã®èšèªã®ãã¹ãŠã®æåããµããŒãããã°ããŒãã«ã«éä¿¡ããéã«æååããããŒã¿æå€±ãé²ããŸãã
ç¶æ 管ç
å€ãã®ãããã³ã«ã¯ã¹ããŒãã¬ã¹ã§ãããåãªã¯ãšã¹ãã«ã¯å¿ èŠãªãã¹ãŠã®æ å ±ãå«ãŸããŠããŸããä»ã®ãããã³ã«ã¯ã¹ããŒããã«ã§ãããåäžã®æ¥ç¶å ã§è€æ°ã®ã¡ãã»ãŒãžã«ããã£ãŠã³ã³ããã¹ããç¶æããŸãïŒäŸïŒãã°ã€ã³ã»ãã·ã§ã³ãé²è¡äžã®ããŒã¿è»¢éïŒããããã³ã«ãã¹ããŒããã«ã§ããå Žåããããã³ã«ã€ã³ã¹ã¿ã³ã¹å ã§ç¶æ ãã©ã®ããã«ä¿åãããæŽæ°ãããããæ éã«èšèšããŠãã ããã忥ç¶ã«ã¯ç¬èªã®ãããã³ã«ã€ã³ã¹ã¿ã³ã¹ãããããšãå¿ããªãã§ãã ããã
ãšã©ãŒåŠçãšå ç¢æ§
ãããã¯ãŒã¯ç°å¢ã¯æ¬è³ªçã«ä¿¡é Œæ§ãäœãã§ãããããã³ã«ã¯æ¬¡ã«å¯ŸåŠã§ããããã«èšèšããå¿ èŠããããŸãã
- éšåçãŸãã¯ç Žæããã¡ãã»ãŒãžïŒãã€ããªãããã³ã«ã®ã¡ãã»ãŒãžåœ¢åŒã«ãã§ãã¯ãµã ãŸãã¯CRCïŒå·¡ååé·æ€æ»ïŒãå®è£ ããŸãã
- ã¿ã€ã ã¢ãŠãïŒæšæºã®TCPã¿ã€ã ã¢ãŠããé·ãããå Žåã¯ãå¿çã®ããã«ã¢ããªã±ãŒã·ã§ã³ã¬ãã«ã®ã¿ã€ã ã¢ãŠããå®è£ ããŸãã
- åæïŒ
connection_lost()
ã§æ£åžžãªåŠçã確å®ã«ããŸãã - ç¡å¹ãªããŒã¿ïŒäžæ£ãªåœ¢åŒã®ã¡ãã»ãŒãžãæ£åžžã«æåŠã§ããå ç¢ãªè§£æããžãã¯ã
ã»ãã¥ãªãã£ã®èæ ®äºé
asyncio
ã¯SSL/TLSãã©ã³ã¹ããŒããæäŸããŸãããã«ã¹ã¿ã ãããã³ã«ãä¿è·ããã«ã¯ããã«æ€èšãå¿
èŠã§ãã
- æå·åïŒãã©ã³ã¹ããŒãã¬ãã«ã®æå·åã«ã¯ã
loop.create_server(ssl=...)
ãŸãã¯loop.create_connection(ssl=...)
ã䜿çšããŸãã - èªèšŒïŒã¯ã©ã€ã¢ã³ããšãµãŒããŒãäºãã®IDã確èªããã¡ã«ããºã ãå®è£ ããŸããããã¯ãããŒã¯ã³ããŒã¹ãèšŒææžããŒã¹ããŸãã¯ãããã³ã«ã®ãã³ãã·ã§ã€ã¯å ã®ãŠãŒã¶ãŒå/ãã¹ã¯ãŒããã£ã¬ã³ãžã§ããå¯èœæ§ããããŸãã
- èªå¯ïŒèªèšŒåŸããŠãŒã¶ãŒãŸãã¯ã·ã¹ãã ãå®è¡ãèš±å¯ãããŠããã¢ã¯ã·ã§ã³ã決å®ããŸãã
- ããŒã¿æŽåæ§ïŒè»¢éäžã«ããŒã¿ãæ¹ãããããŠããªãããšã確èªããŸãïŒããã¯å€ãã®å ŽåTLS/SSLã«ãã£ãŠåŠçãããŸãããéèŠãªããŒã¿ã«ã¯ã¢ããªã±ãŒã·ã§ã³ã¬ãã«ã®ããã·ã¥ãå¿ èŠãªå ŽåããããŸãïŒã
ã¹ããããã€ã¹ãããã®å®è£ ïŒã«ã¹ã¿ã ã®é·ãæ¥é èŸä»ãããã¹ããããã³ã«
å®è·µçãªäŸãäœæããŸããããã¡ãã»ãŒãžãé·ãæ¥é èŸä»ãã§ããã®åŸã«UTF-8ãšã³ã³ãŒããããã³ãã³ããç¶ãã«ã¹ã¿ã ãããã³ã«ã䜿çšããã·ã³ãã«ãªã¯ã©ã€ã¢ã³ãã»ãµãŒããŒã¢ããªã±ãŒã·ã§ã³ã§ãããµãŒããŒã¯'ECHO <message>'
ã'TIME'
ãªã©ã®ã³ãã³ãã«å¿çããŸãã
ãããã³ã«å®çŸ©ïŒ
ã¡ãã»ãŒãžã¯ãç¶ãUTF-8ãšã³ã³ãŒããããã³ãã³ãã®é·ãã瀺ã4ãã€ãã®ç¬Šå·ãªãæŽæ°ïŒããã°ãšã³ãã£ã¢ã³ïŒã§å§ãŸããŸããäŸïŒb'\x00\x00\x00\x04TIME'
ã
ãµãŒããŒåŽã®å®è£
# server.py
import asyncio
import struct
import datetime
class CustomServerProtocol(asyncio.Protocol):
def __init__(self):
self.transport = None
self.buffer = b''
self.message_length = 0
def connection_made(self, transport):
self.transport = transport
peername = transport.get_extra_info('peername')
print(f'Server: Connection from {peername}')
self.transport.write(b'\x00\x00\x00\x1BWelcome to CustomServer!\n') # Length-prefixed welcome
def data_received(self, data):
self.buffer += data
while True:
if self.message_length == 0: # Looking for message length header
if len(self.buffer) < 4:
break # Not enough data for length header
# Unpack the 4-byte length (big-endian, unsigned int)
self.message_length = struct.unpack('!I', self.buffer[:4])[0]
self.buffer = self.buffer[4:]
print(f'Server: Expecting message of length {self.message_length} bytes.')
if len(self.buffer) < self.message_length:
break # Not enough data for the full message payload
# Extract the full message payload
message_bytes = self.buffer[:self.message_length]
self.buffer = self.buffer[self.message_length:]
self.message_length = 0 # Reset for the next message
try:
message = message_bytes.decode('utf-8')
print(f'Server: Received command: {message}')
self.handle_command(message)
except UnicodeDecodeError:
print('Server: Received malformed UTF-8 data.')
self.send_response('ERROR: Invalid UTF-8 encoding.')
def handle_command(self, command):
response_text = ''
if command.startswith('ECHO '):
response_text = f'ECHOING: {command[5:]}'
elif command == 'TIME':
response_text = f'Current time (UTC): {datetime.datetime.utcnow().isoformat()}'
elif command == 'QUIT':
response_text = 'Goodbye!'
self.send_response(response_text)
print('Server: Client requested disconnect.')
self.transport.close()
return
else:
response_text = 'ERROR: Unknown command.'
self.send_response(response_text)
def send_response(self, text):
encoded_text = text.encode('utf-8')
length_prefix = struct.pack('!I', len(encoded_text))
self.transport.write(length_prefix + encoded_text)
def connection_lost(self, exc):
if exc:
print(f'Server: Client disconnected with error: {exc}')
else:
print('Server: Client disconnected cleanly.')
self.transport = None
async def main_server():
loop = asyncio.get_running_loop()
server = await loop.create_server(
CustomServerProtocol,
'127.0.0.1', 8888)
addr = server.sockets[0].getsockname()
print(f'Server: Serving on {addr}')
async with server:
await server.serve_forever()
if __name__ == '__main__':
try:
asyncio.run(main_server())
except KeyboardInterrupt:
print('\nServer: Shutting down.')
ã¯ã©ã€ã¢ã³ãåŽã®å®è£
# client.py
import asyncio
import struct
class CustomClientProtocol(asyncio.Protocol):
def __init__(self, message_queue, on_con_lost):
self.transport = None
self.message_queue = message_queue # To send commands to server
self.on_con_lost = on_con_lost # Future to signal connection loss
self.buffer = b''
self.message_length = 0
def connection_made(self, transport):
self.transport = transport
peername = transport.get_extra_info('peername')
print(f'Client: Connected to {peername}')
def data_received(self, data):
self.buffer += data
while True:
if self.message_length == 0: # Looking for message length header
if len(self.buffer) < 4:
break # Not enough data for length header
self.message_length = struct.unpack('!I', self.buffer[:4])[0]
self.buffer = self.buffer[4:]
print(f'Client: Expecting response of length {self.message_length} bytes.')
if len(self.buffer) < self.message_length:
break # Not enough data for the full message payload
message_bytes = self.buffer[:self.message_length]
self.buffer = self.buffer[self.message_length:]
self.message_length = 0 # Reset for the next message
try:
response = message_bytes.decode('utf-8')
print(f'Client: Received response: \"{response}\"')
except UnicodeDecodeError:
print('Client: Received malformed UTF-8 data from server.')
def connection_lost(self, exc):
if exc:
print(f'Client: Server closed connection with error: {exc}')
else:
print('Client: Server closed connection cleanly.')
self.on_con_lost.set_result(True)
def send_command(self, command_text):
encoded_command = command_text.encode('utf-8')
length_prefix = struct.pack('!I', len(encoded_command))
if self.transport:
self.transport.write(length_prefix + encoded_command)
print(f'Client: Sent command: \"{command_text}\"')
else:
print('Client: Cannot send, transport not available.')
async def client_conversation(host, port):
loop = asyncio.get_running_loop()
on_con_lost = loop.create_future()
message_queue = asyncio.Queue()
transport, protocol = await loop.create_connection(
lambda: CustomClientProtocol(message_queue, on_con_lost),
host, port)
# Give the server a moment to send its welcome message
await asyncio.sleep(0.1)
try:
protocol.send_command('TIME')
await asyncio.sleep(0.5)
protocol.send_command('ECHO Hello World from Client!')
await asyncio.sleep(0.5)
protocol.send_command('INVALID_COMMAND')
await asyncio.sleep(0.5)
protocol.send_command('QUIT')
# Wait until the connection is closed
await on_con_lost
finally:
print('Client: Closing transport.')
transport.close()
if __name__ == '__main__':
asyncio.run(client_conversation('127.0.0.1', 8888))
ãããã®äŸãå®è¡ããã«ã¯ïŒ
- ãµãŒããŒã³ãŒãã
server.py
ãšããŠãã¯ã©ã€ã¢ã³ãã³ãŒããclient.py
ãšããŠä¿åããŸãã - 2ã€ã®ã¿ãŒããã«ãŠã£ã³ããŠãéããŸãã
- æåã®ã¿ãŒããã«ã§ã
python server.py
ãå®è¡ããŸãã - 2çªç®ã®ã¿ãŒããã«ã§ã
python client.py
ãå®è¡ããŸãã
ã¯ã©ã€ã¢ã³ãããéä¿¡ãããã³ãã³ãã«ãµãŒããŒãå¿çããæ§åã芳å¯ã§ããåºæ¬çãªã«ã¹ã¿ã ãããã³ã«ã®åäœã瀺ãããŸãããã®äŸã¯ãUTF-8ãšãããã¯ãŒã¯ãã€ããªãŒããŒïŒããã°ãšã³ãã£ã¢ã³ïŒãé·ãæ¥é èŸã«äœ¿çšããããšã§ãã°ããŒãã«ãªãã¹ããã©ã¯ãã£ã¹ã«æºæ ããå¹ åºãäºææ§ã確ä¿ããŠããŸãã
é«åºŠãªãããã¯ãšèæ ®äºé
åºæ¬ãåå°ãšããŠãããã€ãã®é«åºŠãªãããã¯ããã°ããŒãã«å±éã®ããã®ã«ã¹ã¿ã ãããã³ã«ã®å ç¢æ§ãšæ©èœã匷åããŸãã
倧éããŒã¿ã¹ããªãŒã ãšãããã¡ãªã³ã°ã®åŠç
倧容éãã¡ã€ã«ãé£ç¶ããŒã¿ã¹ããªãŒã ã転éããã¢ããªã±ãŒã·ã§ã³ã«ãšã£ãŠãå¹ççãªãããã¡ãªã³ã°ã¯æ¥µããŠéèŠã§ããdata_received
ã¡ãœããã¯ãä»»æã®ããŒã¿ãã£ã³ã¯ã§åŒã³åºãããå¯èœæ§ããããŸãããããã³ã«ã¯å
éšãããã¡ãç¶æããæ°ããããŒã¿ã远å ããå®å
šãªè«çåäœã®ã¿ãåŠçããå¿
èŠããããŸããéåžžã«å€§ããªããŒã¿ã®å Žåãäžæãã¡ã€ã«ã䜿çšããããã³ã³ã·ã¥ãŒãã«çŽæ¥ã¹ããªãŒãã³ã°ããŠããã€ããŒãå
šäœãã¡ã¢ãªã«ä¿æããªãããã«ããããšãæ€èšããŠãã ããã
åæ¹åéä¿¡ãšã¡ãã»ãŒãžãã€ãã©ã€ã³åŠç
ãã®äŸã¯ã»ãšãã©ãªã¯ãšã¹ã-ã¬ã¹ãã³ã¹åã§ãããasyncio
ãããã³ã«ã¯æ¬è³ªçã«åæ¹åéä¿¡ããµããŒãããŠããŸããã¯ã©ã€ã¢ã³ããšãµãŒããŒã®äž¡æ¹ãç¬ç«ããŠã¡ãã»ãŒãžãéä¿¡ã§ããŸãããŸããã¯ã©ã€ã¢ã³ããåå¿çãåŸ
ããã«è€æ°ã®ãªã¯ãšã¹ããéä¿¡ãããµãŒããŒãããããé çªã«ïŒãŸãã¯ãããã³ã«ãèš±ãå Žåã¯é äžåã§ïŒåŠçããã³å¿çããã¡ãã»ãŒãžãã€ãã©ã€ã³åŠçãå®è£
ããããšãã§ããŸããããã¯ãã°ããŒãã«ã¢ããªã±ãŒã·ã§ã³ã§äžè¬çãªé«é
å»¶ãããã¯ãŒã¯ç°å¢ã«ãããŠãé
å»¶ã倧å¹
ã«åæžã§ããŸãã
é«ã¬ãã«ãããã³ã«ãšã®çµ±å
ã«ã¹ã¿ã ãããã³ã«ããããé«ã¬ãã«ã®å¥ã®ãããã³ã«ã®åºç€ãšããŠæ©èœããããšããããŸããããšãã°ãTCPãããã³ã«äžã«WebSocketã®ãããªãã¬ãŒãã³ã°ã¬ã€ã€ãŒãæ§ç¯ã§ããŸããasyncio
ã¯ããã©ã³ã¹ããŒããšãããã³ã«ã®é«ã¬ãã«ã®äŸ¿å©ãªã©ãããŒã§ããasyncio.StreamReader
ãšasyncio.StreamWriter
ã䜿çšããŠãããã³ã«ãé£éãããããšãå¯èœã«ããŸãããŸãã¯ãasyncio.Subprotocol
ã䜿çšããããšãã§ããŸãïŒãã ããçŽæ¥ã«ã¹ã¿ã ãããã³ã«ãé£éãããå Žåã«ã¯äžè¬çã§ã¯ãããŸããïŒã
ããã©ãŒãã³ã¹æé©å
- å¹ççãªè§£æïŒçãã€ãããŒã¿ã«å¯ŸããŠéå°ãªæååæäœãè€éãªæ£èŠè¡šçŸã䜿çšããªãããã«ããŠãã ããããã€ãã¬ãã«ã®æäœãš
struct
ã¢ãžã¥ãŒã«ããã€ããªããŒã¿ã«äœ¿çšããŸãã - ã³ããŒã®æå°åïŒäžèŠãªãã€ããããã¡ã®ã³ããŒãæžãããŸãã
- ã·ãªã¢ã©ã€ãŒãŒã·ã§ã³ã®éžæïŒé«ã¹ã«ãŒãããã§é å»¶ã«ææãªã¢ããªã±ãŒã·ã§ã³ã®å Žåããã€ããªã·ãªã¢ã©ã€ãŒãŒã·ã§ã³åœ¢åŒïŒProtobufãMessagePackïŒã¯äžè¬çã«ããã¹ãããŒã¹ã®åœ¢åŒïŒJSONãXMLïŒãããåªããããã©ãŒãã³ã¹ãçºæ®ããŸãã
- ãããåŠçïŒå€ãã®å°ããªã¡ãã»ãŒãžãéä¿¡ããå¿ èŠãããå Žåããããã¯ãŒã¯ãªãŒããŒããããåæžããããã«ããããã1ã€ã®ãã倧ããªã¡ãã»ãŒãžã«ãããåŠçããããšãæ€èšããŠãã ããã
ã«ã¹ã¿ã ãããã³ã«ã®ãã¹ã
ã«ã¹ã¿ã ãããã³ã«ã«ãšã£ãŠãå ç¢ãªãã¹ãã¯æãéèŠã§ãã
- åäœãã¹ãïŒå®å
šãªã¡ãã»ãŒãžãéšåçãªã¡ãã»ãŒãžãäžæ£ãªåœ¢åŒã®ã¡ãã»ãŒãžã倧ããªã¡ãã»ãŒãžãªã©ãããŸããŸãªå
¥åã§ãããã³ã«ã®
data_received
ããžãã¯ããã¹ãããŸãã - çµ±åãã¹ãïŒãã¹ããµãŒããŒãšã¯ã©ã€ã¢ã³ããç«ã¡äžããç¹å®ã®ã³ãã³ããéä¿¡ããå¿çãã¢ãµãŒããããã¹ããèšè¿°ããŸãã
- ã¢ãã¯ãªããžã§ã¯ãïŒå®éã®ãããã¯ãŒã¯I/Oãªãã§ãããã³ã«ããžãã¯ããã¹ãããããã«ã
transport
ãªããžã§ã¯ãã«unittest.mock.Mock
ã䜿çšããŸãã - ãã¡ãºãã¹ãïŒã©ã³ãã ãªããŒã¿ãæå³çã«äžæ£ãªåœ¢åŒã®ããŒã¿ããããã³ã«ã«éä¿¡ããŠãäºæããªãåäœãè匱æ§ãçºèŠããŸãã
ãããã€ãšç£èŠ
ã«ã¹ã¿ã ãããã³ã«ããŒã¹ã®ãµãŒãã¹ãã°ããŒãã«ã«ãããã€ããéïŒ
- ã€ã³ãã©ã¹ãã©ã¯ãã£ïŒäžçäžã®ã¯ã©ã€ã¢ã³ãã®é å»¶ãæžããããã«ãè€æ°ã®å°ççå°åã«ã€ã³ã¹ã¿ã³ã¹ããããã€ããããšãæ€èšããŠãã ããã
- ããŒããã©ã³ã·ã³ã°ïŒã°ããŒãã«ããŒããã©ã³ãµãŒã䜿çšããŠããµãŒãã¹ã€ã³ã¹ã¿ã³ã¹éã§ãã©ãã£ãã¯ã忣ããŸãã
- ç£èŠïŒæ¥ç¶ã¹ããŒã¿ã¹ãã¡ãã»ãŒãžã¬ãŒãããšã©ãŒã¬ãŒããé å»¶ã«é¢ããå æ¬çãªãã°ãšã¡ããªãã¯ãå®è£ ããŸããããã¯ã忣ã·ã¹ãã å šäœã§åé¡ã蚺æããããã«äžå¯æ¬ ã§ãã
- æå»åæïŒã°ããŒãã«ãããã€ã¡ã³ãå ã®ãã¹ãŠã®ãµãŒããŒããã¿ã€ã ã¹ã¿ã³ãã«ææãªãããã³ã«ã§ã®åé¡ã鲿¢ããããã«ãæå»åæãããŠããããšã確èªããŠãã ããïŒäŸïŒNTPçµç±ïŒã
ã«ã¹ã¿ã ãããã³ã«ã®çŸå®äžçã§ã®ãŠãŒã¹ã±ãŒã¹
ã«ã¹ã¿ã ãããã³ã«ãç¹ã«asyncio
ã®ããã©ãŒãã³ã¹ç¹æ§ãæã€ãã®ã¯ãããŸããŸãªèŠæ±ã®å³ããåéã§å¿çšãããŠããŸãã
- IoTããã€ã¹éä¿¡ïŒãªãœãŒã¹ã«å¶çŽã®ããããã€ã¹ã¯ãå¹çã®ããã«è»œéãªãã€ããªãããã³ã«ã䜿çšããããšããããããŸãã
asyncio
ãµãŒããŒã¯ãæ°åã®åæããã€ã¹æ¥ç¶ãåŠçã§ããŸãã - é«é »åºŠååŒïŒHFTïŒã·ã¹ãã ïŒæå°éã®ãªãŒããŒããããšæå€§éã®é床ãéèŠã§ããTCPäžã®ã«ã¹ã¿ã ãã€ããªãããã³ã«ãäžè¬çã§ãäœé
å»¶ã€ãã³ãåŠçã®ããã«
asyncio
ãæŽ»çšããŠããŸãã - ãã«ããã¬ã€ã€ãŒã²ãŒã ãµãŒããŒïŒãªã¢ã«ã¿ã€ã ã®æŽæ°ããã¬ã€ã€ãŒã®äœçœ®ãã²ãŒã ã®ç¶æ
ã¯ãé床ã®ããã«ã«ã¹ã¿ã ã®UDPããŒã¹ãããã³ã«ïŒ
asyncio.DatagramProtocol
ã䜿çšïŒããã䜿çšããä¿¡é Œæ§ã®é«ãã€ãã³ãã®ããã«TCPã§è£å®ãããŸãã - ãµãŒãã¹ééä¿¡ïŒé«åºŠã«æé©åããããã€ã¯ããµãŒãã¹ã¢ãŒããã¯ãã£ã§ã¯ãã«ã¹ã¿ã ãã€ããªãããã³ã«ã¯å éšéä¿¡ã«ãããŠHTTP/RESTãããããã©ãŒãã³ã¹åäžãããããããšãã§ããŸãã
- ç£æ¥çšå¶åŸ¡ã·ã¹ãã ïŒICS/SCADAïŒïŒã¬ã¬ã·ãŒãŸãã¯ç¹æ®ãªæ©åšã¯ãçŸä»£ã®çµ±åã®ããã«ã«ã¹ã¿ã å®è£ ãå¿ èŠãªç¬èªã®ãããã³ã«ã䜿çšããå ŽåããããŸãã
- ç¹æ®ããŒã¿ãã£ãŒãïŒç¹å®ã®éèããŒã¿ãã»ã³ãµãŒã®èªã¿åãããŸãã¯ãã¥ãŒã¹ã¹ããªãŒã ãæå°éã®é å»¶ã§å€æ°ã®è³Œèªè ã«ãããŒããã£ã¹ãããŸãã
課é¡ãšãã©ãã«ã·ã¥ãŒãã£ã³ã°
匷åã§ã¯ãããŸãããã«ã¹ã¿ã ãããã³ã«ã®å®è£ ã«ã¯åºæã®èª²é¡ã䌎ããŸãã
- éåæã³ãŒãã®ãããã°ïŒäžŠè¡ã·ã¹ãã ã®å¶åŸ¡ãããŒãçè§£ããããšã¯è€éã§ããããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ã«ã¯
asyncio.create_task()
ã䞊åå®è¡ã«ã¯asyncio.gather()
ããããŠæ³šææ·±ããã°èšé²ã䜿çšããŠãã ããã - ãããã³ã«ããŒãžã§ã³ç®¡çïŒãããã³ã«ãé²åããã«ã€ããŠãç°ãªãããŒãžã§ã³ã管çããåŸæ¹/åæ¹äºææ§ã確ä¿ããããšã¯å°é£ã§ããæåãããããã³ã«ããããŒã«ããŒãžã§ã³ãã£ãŒã«ããèšèšããŠãã ããã
- ãããã¡ã®ã¢ã³ããŒ/ãªãŒããŒãããŒïŒ
data_received
ã§ã®èª€ã£ããããã¡ç®¡çã¯ãã¡ãã»ãŒãžãéåãããã誀ã£ãŠé£çµããããããåå ãšãªãå¯èœæ§ããããŸããåžžã«å®å šãªã¡ãã»ãŒãžã®ã¿ãåŠçããæ®ãã®ããŒã¿ãåŠçããããã«ããŠãã ããã - ãããã¯ãŒã¯é å»¶ãšãžãã¿ãŒïŒã°ããŒãã«å±éã®å Žåããããã¯ãŒã¯æ¡ä»¶ã¯å€§ããç°ãªããŸããé å»¶ãšåéã«èæ§ãããããã«ãããã³ã«ãèšèšããŠãã ããã
- ã»ãã¥ãªãã£è匱æ§ïŒäžé©åã«èšèšãããã«ã¹ã¿ã ãããã³ã«ã¯ãäž»èŠãªæ»æãã¯ãã«ã«ãªãå¯èœæ§ããããŸããæšæºãããã³ã«ã®åºç¯ãªç²Ÿæ»ããªããããã€ã³ãžã§ã¯ã·ã§ã³æ»æããªãã¬ã€æ»æããµãŒãã¹æåŠè匱æ§ãªã©ã®åé¡ãç¹å®ãã軜æžãã責任ã¯éçºè ã«ãããŸãã
çµè«
Pythonã®asyncio
ã§ã«ã¹ã¿ã ãããã¯ãŒã¯ãããã³ã«ãå®è£
ããèœåã¯ã髿§èœããªã¢ã«ã¿ã€ã ããŸãã¯ç¹æ®ãªãããã¯ãŒã¯ã¢ããªã±ãŒã·ã§ã³ã«åãçµããã¹ãŠã®éçºè
ã«ãšã£ãŠåŒ·åãªã¹ãã«ã§ããã€ãã³ãã«ãŒãããã©ã³ã¹ããŒãããããã³ã«ã®ã³ã¢ã³ã³ã»ãããçè§£ããã¡ãã»ãŒãžåœ¢åŒãšè§£æããžãã¯ãç¶¿å¯ã«èšèšããããšã§ãéåžžã«å¹ççã§ã¹ã±ãŒã©ãã«ãªéä¿¡ã·ã¹ãã ãäœæã§ããŸãã
UTF-8ããããã¯ãŒã¯ãã€ããªãŒããŒã®ãããªæšæºãéããŠã°ããŒãã«ãªçžäºéçšæ§ã確ä¿ããããšãããå
ç¢ãªãšã©ãŒåŠçãšã»ãã¥ãªãã£å¯Ÿçãæ¡çšããããšãŸã§ããã®ã¬ã€ãã§æŠèª¬ãããŠããååã¯åŒ·åºãªåºç€ãæäŸããŸãããããã¯ãŒã¯ã®éèŠãæé·ãç¶ããäžãasyncio
ãããã³ã«å®è£
ãç¿åŸããããšã¯ã倿§ãªç£æ¥ãå°ççæ¯èŠ³ã«ãããã€ãããŒã·ã§ã³ãæšé²ãããªãŒããŒã¡ã€ãã®ãœãªã¥ãŒã·ã§ã³ãæ§ç¯ããããšãå¯èœã«ããã§ãããã仿¥ããå®éšããå埩ããæ¬¡äžä»£ã®ãããã¯ãŒã¯å¯Ÿå¿ã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ãå§ããŸãããïŒ