Aiohttp: Websocket ํด๋ผ์ด์–ธํŠธ ์ค‘๋‹จ / ์ฐจ๋‹จ?

์— ๋งŒ๋“  2018๋…„ 09์›” 26์ผ  ยท  12์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: aio-libs/aiohttp

์งง์€ ์ด์•ผ๊ธฐ

ws.receive() ์€ (๋Š”) websocket์—์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ด๋™ ํ•จ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ค‘๋‹จ ๋œ ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

์˜ˆ์ƒ๋˜๋Š” ํ–‰๋™

ws.receive ๋Š” ๋ฌด์–ธ๊ฐ€๋ฅผ ์ถœ๋ ฅํ•˜๊ณ ๋ณด๊ณ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ํ–‰๋™

์›น ์†Œ์ผ“์—์„œ ์ค‘๋‹จ / ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค.

์žฌํ˜„ ๋‹จ๊ณ„

์•„๋ž˜ ์ฝ”๋“œ๋Š” ํ˜ธ์ถœ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

    async def _ws_subscribe(self,
                            session: ClientSession,
                            url: str
                            ) -> Dict:
        """ Fetch data from web socket asynchronously.

        This helper method fetches data from a web socket asynchronously. It is used to
        fetch subscribe to websockets of the SkyQ box.

        Args:
            session (aiohttp.ClientSession): Session to use when fetching the data.
            url (str): WebSocket URL to fetch.

        Returns:
            dict: The body of data returned.

        """
        LOGGER.debug(f"Inside _ws_subscribe()...")

        async with session.ws_connect(url, autoclose=False) as ws:
            while True:
                LOGGER.debug(f"Inside _ws_subscribe() infinite loop pre ws.receive.")
                payload = await ws.receive()
                LOGGER.debug(f"READ SOMETHING! _ws_subscribe() infinite loop post ws.receive")
                LOGGER.debug(f'type = {payload.type}')
                LOGGER.debug(f'payload data = {payload.data}')
                LOGGER.debug(f'payload exception = {ws.exception()}')
                if payload.type == aiohttp.WSMsgType.TEXT:
                    LOGGER.debug('Web-socket data received.')
                    asyncio.create_task(self._handle(payload))
                    # asyncio.ensure_future(self._handle(payload))
                elif payload.type == aiohttp.WSMsgType.BINARY:
                    LOGGER.debug('Web-socket binary data received.')
                elif payload.type == aiohttp.WSMsgType.PING:
                    LOGGER.debug('Web-socket ping received')
                    ws.pong()
                elif payload.type == aiohttp.WSMsgType.PONG:
                    LOGGER.debug('Web-socket pong received')
                else:
                    if payload.type == aiohttp.WSMsgType.CLOSE:
                        await ws.close()
                    elif payload.type == aiohttp.WSMsgType.ERROR:
                        LOGGER.info(f'Error during receive {ws.exception()}')
                    elif payload.type == aiohttp.WSMsgType.CLOSED:
                        pass

                    break

๊ฒฐ๊ณผ ๋กœ๊ทธ :

(pyskyq-4vSEKDfZ) โœ” [brad<strong i="6">@bradmac</strong>:~/Code/pyskyq] [16-create-status-end-point-property|โœš 5โ€ฆ2] $ pyskyq -vv green
[2018-09-26 09:12:14] DEBUG:pyskyq.cli:Starting SkyQ...
[2018-09-26 09:12:14] DEBUG:pyskyq.skyremote:Initialised SkyRemote object with host=skyq, port=49160
[2018-09-26 09:12:14] DEBUG:pyskyq.status:Initialised Status object object with host=skyq, port=9006
[2018-09-26 09:12:14] DEBUG:asyncio:Using selector: KqueueSelector
[2018-09-26 09:12:14] DEBUG:pyskyq.status:Asyncio event loop thread running...
[2018-09-26 09:12:14] DEBUG:pyskyq.skyq:Initialised SkyQ object with host=skyq.
[2018-09-26 09:12:14] INFO:pyskyq.cli:Script ends here
[2018-09-26 09:12:14] DEBUG:pyskyq.status:Setting up web socket listener on ws://skyq:9006/as/system/status.
[2018-09-26 09:12:14] DEBUG:pyskyq.status:Inside _ws_subscribe()...
[2018-09-26 09:12:14] DEBUG:asyncio:Get address info skyq:9006, type=<SocketKind.SOCK_STREAM: 1>
[2018-09-26 09:12:14] DEBUG:asyncio:Getting address info skyq:9006, type=<SocketKind.SOCK_STREAM: 1> took 15.352ms: [(<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('10.0.1.6', 9006))]
[2018-09-26 09:12:14] DEBUG:asyncio:poll 60478.847 ms took 14.299 ms: 1 events
[2018-09-26 09:12:14] DEBUG:asyncio:connect <socket.socket fd=8, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('0.0.0.0', 0)> to ('10.0.1.6', 9006)
[2018-09-26 09:12:14] DEBUG:asyncio:poll 60461.657 ms took 21.045 ms: 1 events
[2018-09-26 09:12:14] DEBUG:asyncio:<socket.socket fd=8, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('10.0.1.30', 50141), raddr=('10.0.1.6', 9006)> connected to 10.0.1.6:9006: (<_SelectorSocketTransport fd=8 read=polling write=<idle, bufsize=0>>, <aiohttp.client_proto.ResponseHandler object at 0x1033c8ac8>)
[2018-09-26 09:12:14] DEBUG:asyncio:poll 60429.970 ms took 7.192 ms: 1 events
[2018-09-26 09:12:14] DEBUG:pyskyq.status:Inside _ws_subscribe() infinite loop pre ws.receive.

๊ทธ๋Ÿฌ๋‚˜ ์†Œ์ผ“์€ ์—ด๋ ค ์žˆ๊ณ  netcat์—์„œ ๋ณผ ์ˆ˜์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

(pyskyq-4vSEKDfZ) โœ” [brad<strong i="6">@bradmac</strong>:~/Code/pyskyq] [16-create-status-end-point-property|โœš 5โ€ฆ2] $ nc skyq 9006
GET /as/system/status HTTP/1.1
Host: skyq:9006
Accept: */*
Connection: Upgrade
Upgrade: websocket

๏ฟฝ~๏ฟฝ{
   "camessage" : {
      "reason" : "no message",
      "state" : "unavailable"
   },
   "drmstatus" : {
      "state" : "available"
   },
   "entitlements" : [
      "ANALYTICS",
      "BIGBASIC",
      "ETHAN_APP_1",
      "HD",
      "PDL",
      "SKY_DRM_CE",
      "SKY_DRM_MR",
      "SKY_IPPV",
      "ULTRA+",
      "SKY+",
      "GATEWAYENABLER",
      "SIDELOAD"
   ],
   "epginfobits" : {
      "epginfobits" : "0xFDE5FFC0",
      "mask" : "0x5FFA003F",
      "state" : "available"
   },
   "gatewayservices" : {
      "state" : "available"
   },
   "hdmi" : {
      "2160p10bitCapable" : false,
      "authenticatedHDCP" : "1.x",
      "sinkHDCP" : "1.x",
      "sinkHLG" : false,
      "sinkUHD" : false,
      "state" : "available",
      "uhdConfigured" : false
   },
   "network" : {
      "state" : "available"
   },
   "nssplayback" : {
      "state" : "available"
   },
   "pvr" : {
      "state" : "available"
   },
   "schedule" : {
      "lastdate" : "20181003",
      "state" : "available"
   },
   "servicelist" : {
      "state" : "available"
   },
   "smartcard" : {
      "active" : true,
      "bouquet" : "4101",
      "countryCode" : "GBR",
      "currency" : "GBP",
      "cwe" : true,
      "householdid" : "10947783",
      "paired" : true,
      "state" : "available",
      "subbouquet" : "1",
      "transactionlimit" : 65535,
      "viewingCardNumber" : "725 325 260"
   },
   "swupdate" : {
      "reason" : "IDLE",
      "state" : "unavailable"
   },
   "systemupdates" : {
      "entitlements" : 2,
      "install" : 1,
      "servicegenres" : 1,
      "smartcard" : 1
   },
   "updatetask" : {
      "reason" : "no update",
      "state" : "unavailable"
   }
}
^C(pyskyq-4vSEKDfZ) โœ” [brad<strong i="7">@bradmac</strong>:~/Code/pyskyq] [16-create-status-end-point-property|โœš 5โ€ฆ2] $

๋‹น์‹ ์˜ ํ™˜๊ฒฝ

(pyskyq-4vSEKDfZ) โœ” [brad<strong i="6">@bradmac</strong>:~/Code/pyskyq] [16-create-status-end-point-property|โœš 5โ€ฆ2] $ uname -a
Darwin bradmac 17.7.0 Darwin Kernel Version 17.7.0: Thu Jun 21 22:53:14 PDT 2018; root:xnu-4570.71.2~1/RELEASE_X86_64 x86_64
(pyskyq-4vSEKDfZ) โœ” [brad<strong i="7">@bradmac</strong>:~/Code/pyskyq] [16-create-status-end-point-property|โœš 5โ€ฆ2] $ pip freeze
aiohttp==3.4.4
appnope==0.1.0
arrow==0.12.1
async-timeout==3.0.0
async-upnp-client==0.12.4
attrs==18.2.0
backcall==0.1.0
binaryornot==0.4.4
bleach==2.1.3
certifi==2018.4.16
chardet==3.0.4
click==6.7
cookiecutter==1.6.0
cycler==0.10.0
decorator==4.3.0
Django==2.1.1
entrypoints==0.2.3
ez-setup==0.9
future==0.16.0
html5lib==1.0.1
idna==2.7
idna-ssl==1.1.0
ipykernel==4.8.2
ipython==6.4.0
ipython-genutils==0.2.0
ipywidgets==7.2.1
jedi==0.12.0
Jinja2==2.10
jinja2-time==0.2.0
jsonschema==2.6.0
jupyter==1.0.0
jupyter-client==5.2.3
jupyter-console==5.2.0
jupyter-core==4.4.0
kaggle==1.4.2
kiwisolver==1.0.1
MarkupSafe==1.0
matplotlib==2.2.2
mistune==0.8.3
multidict==4.4.2
nbconvert==5.3.1
nbformat==4.4.0
notebook==5.5.0
numpy==1.14.4
pandocfilters==1.4.2
parso==0.2.1
pexpect==4.6.0
pickleshare==0.7.4
pipenv==2018.7.1
poyo==0.4.1
prompt-toolkit==1.0.15
ptyprocess==0.6.0
Pygments==2.2.0
pyparsing==2.2.0
PyScaffold==3.0.3
python-dateutil==2.7.3
python-didl-lite==1.1.0
pytz==2018.4
pyzmq==17.0.0
qtconsole==4.3.1
requests==2.19.1
Send2Trash==1.5.0
simplegeneric==0.8.1
six==1.11.0
terminado==0.8.1
testpath==0.3.1
tornado==5.0.2
tqdm==4.24.0
traitlets==4.3.2
urllib3==1.22
virtualenv==16.0.0
virtualenv-clone==0.3.0
voluptuous==0.11.5
wcwidth==0.1.7
webencodings==0.5.1
whichcraft==0.4.1
widgetsnbextension==3.2.1
yarl==1.2.6
(pyskyq-4vSEKDfZ) โœ” [brad<strong i="8">@bradmac</strong>:~/Code/pyskyq] [16-create-status-end-point-property|โœš 5โ€ฆ2] $

์ตœ์‹  ์ •๋ณด...

Python ๋ฒ„์ „์€ 3.7.0์ž…๋‹ˆ๋‹ค.

๋ชจ๋“  12 ๋Œ“๊ธ€

GitMate.io ๋Š” ๊ด€๋ จ ๋ฌธ์ œ๊ฐ€ https://github.com/aio-libs/aiohttp/issues/2200 (๋ฐฑ๊ทธ๋ผ์šด๋“œ ํด๋ผ์ด์–ธํŠธ ์›น ์†Œ์ผ“์ด SIGINT์—์„œ ์ค‘๋‹จ๋จ), https://github.com/aio-libs/aiohttp/issues/ ๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. https://github.com/aio-libs/aiohttp/issues/376(ProactorEventLoop ์ค‘๋‹จ๋จ), https://github.com/aio-libs/aiohttp/issues / 3027 (ws_connect ์ค‘๋‹จ) ๋ฐ https://github.com/aio-libs/aiohttp/issues/265 (ํด๋ผ์ด์–ธํŠธ ์ธก ์›น ์†Œ์ผ“ ๊ตฌํ˜„).

client_ws.py # L204 ๊ฐ€ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค ... ์–ด๋–ค ์•„์ด๋””์–ด๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

์„œ๋ฒ„๋Š” SkyQ ์…‹ํ†ฑ ๋ฐ•์Šค์ด๋ฏ€๋กœ WS์˜ ์ „์ฒด RFC ์ค€์ˆ˜ ๊ตฌํ˜„์ด ์•„๋‹ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ping / poings๋ฅผ ์กด์ค‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค ... ๊ทธ๋ฆฌ๊ณ  ํŽ˜์ด๋กœ๋“œ์— ์ด์ƒํ•œ ๏ฟฝ~๏ฟฝ ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค ... ๊ทธ๊ฒƒ์ด opcode์ธ์ง€ ๋˜๋Š” ๋‹ค๋ฅธ ํŠน์ดํ•œ ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค ...

Websocket์€ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค. ๏ฟฝ~๏ฟฝ ๋Š” ๋ฉ”์‹œ์ง€ ์œ ํ˜•๊ณผ ๊ธธ์ด๋ฅผ ์ธ์ฝ”๋”ฉํ•ฉ๋‹ˆ๋‹ค.
์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋””์ฝ”๋”ฉ์— ์ฐธ์—ฌํ•˜๊ณ  ์‹ค์ œ ํŽ˜์ด๋กœ๋“œ ํฌ๊ธฐ์™€ ๋น„๊ตํ•˜์ง€ ์•Š์œผ๋ฉด ๋„์™€ ๋“œ๋ฆด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๏ฟฝ~๏ฟฝ ์ด๋‹ค 001111110111111000111111 : ์ธ ์ด์ง„ 3F7E3F ์ง„์ˆ˜์žˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฐ”์ดํŠธ๋Š”์ด ์—”๋“œ ํˆฌ ์—”๋“œ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค.
00111111
01111110
00111111

๋‚˜๋Š” RFC์˜ ์ „๋ฌธ๊ฐ€๋Š” ์•„๋‹ˆ์ง€๋งŒ ์œ ํ˜•๊ณผ ๊ธธ์ด ์ธก๋ฉด์—์„œ ๋ฌด์—‡์„ ๋งํ•˜๋Š”๊ฐ€?

๊ทธ๊ฒŒ ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐํ•˜์„ธ์š”? ์ฆ‰, ์‹ค์ œ ํŽ˜์ด๋กœ๋“œ ๊ธธ์ด์— ๋™์˜ํ•˜์ง€ ์•Š๊ณ  ์ถ”๊ฐ€ ํ”„๋ ˆ์ž„์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ฒƒ์ž…๋‹ˆ๊นŒ?

ํฅ๋ฏธ๋กญ๊ฒŒ๋„ go๋กœ ์ž‘์„ฑ๋œ ์ด ์›น ์†Œ์ผ“ cli ํด๋ผ์ด์–ธํŠธ ๋Š” ์ƒ๋‹จ์—์žˆ๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ํฌํ•จํ•˜์—ฌ ์ถœ๋ ฅ์„ ์ž˜ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

```
ws ws : // skyq : 9006 / as / system / status
<{
"camessage": {
"reason": "๋ฉ”์‹œ์ง€ ์—†์Œ",
"์ƒํƒœ": "์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ"
},
"drmstatus": {
"์ƒํƒœ": "์‚ฌ์šฉ ๊ฐ€๋Šฅ"
},
"entitlements": [
"ํ•ด์„ํ•™",
"BIGBASIC",
"ETHAN_APP_1",
"HD",
"PDL",
"SKY_DRM_CE",
"SKY_DRM_MR",
"SKY_IPPV",
"ULTRA +",
"SKY +",
"GATEWAYENABLER",
"์‚ฌ์ด๋“œ๋กœ๋“œ"
],
"epginfobits": {
"epginfobits": "0xFDE5FFC0",
"๋งˆ์Šคํฌ": "0x5FFA003F",
"์ƒํƒœ": "์‚ฌ์šฉ ๊ฐ€๋Šฅ"
},
"gatewayservices": {
"์ƒํƒœ": "์‚ฌ์šฉ ๊ฐ€๋Šฅ"
},
"hdmi": {
"2160p10bitCapable": ๊ฑฐ์ง“,
"authenticatedHDCP": "์—†์Œ",
"reason": "HDMI ์ถœ๋ ฅ ํฌํŠธ ๋น„ํ™œ์„ฑํ™” ๋จ",
"sinkHDCP": "์—†์Œ",
"sinkHLG": ๊ฑฐ์ง“,
"sinkUHD": ๊ฑฐ์ง“,
"์ƒํƒœ": "์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ",
"uhdConfigured": false
},
"๋„คํŠธ์›Œํฌ": {
"์ƒํƒœ": "์‚ฌ์šฉ ๊ฐ€๋Šฅ"
},
"nssplayback": {
"์ƒํƒœ": "์‚ฌ์šฉ ๊ฐ€๋Šฅ"
},
"pvr": {
"์ƒํƒœ": "์‚ฌ์šฉ ๊ฐ€๋Šฅ"
},
"์ผ์ •": {
"lastdate": "20181003",
"์ƒํƒœ": "์‚ฌ์šฉ ๊ฐ€๋Šฅ"
},
"servicelist": {
"์ƒํƒœ": "์‚ฌ์šฉ ๊ฐ€๋Šฅ"
},
"์Šค๋งˆํŠธ ์นด๋“œ" : {
"ํ™œ์„ฑ": ์ฐธ,
"๊ฝƒ๋‹ค๋ฐœ": "4101",
"countryCode": "GBR",
"ํ†ตํ™”": "GBP",
"cwe": ์ฐธ,
"๊ฐ€์ •์šฉ": "10947783",
"paired": ์ฐธ,
"์ƒํƒœ": "์‚ฌ์šฉ ๊ฐ€๋Šฅ",
"subbouquet": "1",
"transactionlimit": 65535,
"viewingCardNumber": "725325260"
},
"swupdate": {
"reason": "IDLE",
"์ƒํƒœ": "์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ"
},
"์‹œ์Šคํ…œ ์—…๋ฐ์ดํŠธ" : {
"entitlements": 2,
"์„ค์น˜": 1,
"servicegenres": 1,
"์Šค๋งˆํŠธ ์นด๋“œ": 1
},
"updatetask": {
"reason": "์—…๋ฐ์ดํŠธ ์—†์Œ",
"์ƒํƒœ": "์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ"
}
}

^ C
์ผ์‹œ ์ •์ง€
โœ” [ brad @ bradmac : ~] $```

ํ”„๋กœํ† ์ฝœ์— ๋”ฐ๋ฅด๋ฉด WS ๋ฉ”์‹œ์ง€์˜ ์ฒซ ๋ฒˆ์งธ 3F ๋ฐ”์ดํŠธ๋Š” ๊ธˆ์ง€๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค (ํ–ฅํ›„ ์‚ฌ์šฉ์„ ์œ„ํ•ด ์˜ˆ์•ฝ ๋จ).
๋‹น์‹ ์€ ๋ญ”๊ฐ€๋ฅผ ๋†“์นœ ๊ฒƒ์ด ํ™•์‹คํ•ฉ๋‹ˆ๊นŒ?

๊ธฐ๋ณธ ํ”„๋ ˆ์ด๋ฐ ํ”„๋กœํ† ์ฝœ์—์„œ 2d, 3rd ๋ฐ 4th ๋น„ํŠธ๋ฅผ ์ฐธ์กฐํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ค ๊ฒฝ์šฐ์—๋Š” ์˜ˆ, ์•„๋งˆ๋„ ๋‚ด๊ฐ€ ์ž˜๋ชปํ–ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์œ ๋‹ˆ ์ฝ”๋“œ / ASCII ๋ฌธ์ œ ์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ํ•œ๋ฒˆ ์‚ดํŽด ๋ณผ๊ฒŒ์š”.

๋ช‡ ๊ฐ€์ง€ ๋‹ค๋ฅธ ์˜จ๋ผ์ธ ๋ณ€ํ™˜๊ธฐ๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค ... ์ง€๊ธˆ ๋ฐ›๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
16 ์ง„์ˆ˜ = ef bf bd 7e ef bf bd
๋ฐ”์ด๋„ˆ๋ฆฌ = 11101111 10111111 10111101 01111110 11101111 10111111 10111101

RFC 6455์— ๋”ฐ๋ฅธ ๊ฒฝ์šฐ

5.2.  Base Framing Protocol

   This wire format for the data transfer part is described by the ABNF
   [RFC5234] given in detail in this section.  (Note that, unlike in
   other sections of this document, the ABNF in this section is
   operating on groups of bits.  The length of each group of bits is
   indicated in a comment.  When encoded on the wire, the most
   significant bit is the leftmost in the ABNF).  A high-level overview
   of the framing is given in the following figure.  In a case of
   conflict between the figure below and the ABNF specified later in
   this section, the figure is authoritative.

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+

   FIN:  1 bit

      Indicates that this is the final fragment in a message.  The first
      fragment MAY also be the final fragment.

   RSV1, RSV2, RSV3:  1 bit each

      MUST be 0 unless an extension is negotiated that defines meanings
      for non-zero values.  If a nonzero value is received and none of
      the negotiated extensions defines the meaning of such a nonzero
      value, the receiving endpoint MUST _Fail the WebSocket
      Connection_.

๊ทธ๋Ÿฌ๋ฉด RSV1 ๋ฐ RSV2 ๊ฐ€ 1๋กœ ์„ค์ •๋˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ์ผ๋ถ€ etxension์ด ํ˜‘์ƒ๋˜์ง€ ์•Š๋Š” ํ•œ ์ œ์•ˆํ•œ๋Œ€๋กœ? ๋‚˜๋Š” ๋ชจ๋ฅธ๋‹ค.

JSON ๋ฌธ์„œ์˜ ๋ฌธ์ž ์ˆ˜๋Š” 1703

RFC๋Š” ํŽ˜์ด๋กœ๋“œ ๊ธธ์ด์— ๋Œ€ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งํ•ฉ๋‹ˆ๋‹ค.

Payload length:  7 bits, 7+16 bits, or 7+64 bits

      The length of the "Payload data", in bytes: if 0-125, that is the
      payload length.  If 126, the following 2 bytes interpreted as a
      16-bit unsigned integer are the payload length.  If 127, the
      following 8 bytes interpreted as a 64-bit unsigned integer (the
      most significant bit MUST be 0) are the payload length.  Multibyte
      length quantities are expressed in network byte order.  Note that
      in all cases, the minimal number of bytes MUST be used to encode
      the length, for example, the length of a 124-byte-long string
      can't be encoded as the sequence 126, 0, 124.  The payload length
      is the length of the "Extension data" + the length of the
      "Application data".  The length of the "Extension data" may be
      zero, in which case the payload length is the length of the
      "Application data".

๊ทธ๋ž˜์„œ, ๊ทธ ๋ชจ๋“  ๊ฒƒ์„ ๊ฐ์•ˆํ•  ๋•Œ ์šฐ๋ฆฌ๋Š” ๋‹ค์Œ์— ์–ด๋””๋กœ ๊ฐ€์•ผํ•ฉ๋‹ˆ๊นŒ? ์„œ๋ฒ„๊ฐ€ ๋‡Œ์‚ฌ ์ƒํƒœ์ด๊ณ  RFC๋ฅผ ์ค€์ˆ˜ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ๋‹ค๋ฅธ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ถ„๋ช…ํžˆ ํ•  ์ˆ˜์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ œ์•ˆ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์—ฌ์ „ํžˆ ์˜์‹ฌ์Šค๋Ÿฌ์›Œ ๋ณด์ž…๋‹ˆ๋‹ค.
์•Œ๋ ค์ง„ ์œ ์ผํ•œ ํ™•์žฅ์€ RSV1์„ ์‚ฌ์šฉํ•˜๋Š” WebSocket ์••์ถ•์ž…๋‹ˆ๋‹ค. ์–ด๋–ค ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์ด RSV2๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.
๋˜ํ•œ opcode FF๋„ ์˜ˆ์•ฝ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์‚ฌ์šฉํ•ด์„œ๋Š” ์•ˆ๋ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„๊ฐ€ ๋ฌด์—‡์„ ์ „์†กํ•˜๋Š”์ง€ ๋ชจ๋ฅด์ง€ ์•Š๋Š” ํ•œ ํด๋ผ์ด์–ธํŠธ๋ฅผ "๊ณ ์น "๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค.

@asvetlov ์ €๋Š” websockets ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด์•˜๊ณ  ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค ... ๊ทธ๋ž˜์„œ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋””๋ฒ„๊น…ํ•˜๋Š” ๋ฐ ๋” ๋งŽ์€ ์‹œ๊ฐ„์„ ํ• ์•  ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ํ•จ๊ป˜ ๊ฐˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค ...

aoihttp -์ž‘๋™ํ•˜์ง€ ์•Š์Œ

import asyncio
import logging
import sys

import aiohttp

LOGGER = logging.getLogger(__name__)
logformat = "[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout,
                    format=logformat, datefmt="%Y-%m-%d %H:%M:%S")


async def main():
    async with aiohttp.ClientSession() as session:
        async with session.ws_connect('http://skyq:9006/as/system/status') as ws:
            payload = await ws.receive()
            print(payload.text)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

websockets -์ž‘๋™

import asyncio
import websockets
import logging
import sys

LOGGER = logging.getLogger(__name__)
logformat = "[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout,
                    format=logformat, datefmt="%Y-%m-%d %H:%M:%S")


async def hello():
    async with websockets.connect(
            'ws://skyq:9006/as/system/status') as websocket:

        payload = await websocket.recv()
        print(f"{payload}")

asyncio.get_event_loop().run_until_complete(hello())

์›ํ•˜๋Š” ๊ฒฝ์šฐ์ด ํ‹ฐ์ผ“์„ ๋ฌด๋ฃŒ๋กœ ๋‹ซ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ๋ฒ„๊ทธ๋กœ ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์— ์—ด์–ด ๋‘๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋˜๋Š” ์ ์–ด๋„ Postel์˜ ๋ฒ•์„ ์ค€์ˆ˜ํ•˜์ง€ ์•Š๋Š” ์ƒํ™ฉ.

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์—ฌ์ „ํžˆ ์˜์‹ฌ์Šค๋Ÿฌ์›Œ ๋ณด์ž…๋‹ˆ๋‹ค.
์•Œ๋ ค์ง„ ์œ ์ผํ•œ ํ™•์žฅ์€ RSV1์„ ์‚ฌ์šฉํ•˜๋Š” WebSocket ์••์ถ•์ž…๋‹ˆ๋‹ค. ์–ด๋–ค ํ™•์žฅ ํ”„๋กœ๊ทธ๋žจ์ด RSV2๋ฅผ ์‚ฌ์šฉํ•˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.
๋˜ํ•œ opcode FF๋„ ์˜ˆ์•ฝ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์‚ฌ์šฉํ•ด์„œ๋Š” ์•ˆ๋ฉ๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š” @asvetlov

์„œ๋ฒ„๊ฐ€ ๋ฌด์—‡์„ ์ „์†กํ•˜๋Š”์ง€ ๋ชจ๋ฅด์ง€ ์•Š๋Š” ํ•œ ํด๋ผ์ด์–ธํŠธ๋ฅผ "๊ณ ์น "๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋‹น์‹ ์ด ์˜๋ฏธ ์ƒ๊ฐ ", ๋‹น์‹ ์€ ์„œ๋ฒ„๊ฐ€ ๋ณด๋‚ด๋Š” ์•Œ๊ณ ํ•˜์ง€ ์•Š๋Š” ํ•œ, ๋‹น์‹ ์€ ํด๋ผ์ด์–ธํŠธ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."

์–ด๋–ค ๊ฒฝ์šฐ์—๋Š”์ด ์ ์— ์™„์ „ํžˆ ๋™์˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ฒฌ๊ณ ์„ฑ ์›์น™์— ๋Œ€ํ•ด ์ž˜ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

"๋ณด๋‚ด๋Š” ๊ฒƒ์€ ๋ณด์ˆ˜์ ์ด๋ฉฐ๋ฐ›๋Š” ๊ฒƒ์€ ์ž์œ ๋กœ ์›Œ์•ผํ•ฉ๋‹ˆ๋‹ค."

๋ฐ˜๋“œ์‹œํ•ด์•ผ ํ•  ๊ฒƒ,ํ•ด์•ผํ•˜๋Š” ๊ฒƒ,ํ•˜์ง€ ๋ง์•„์•ผ ํ•  ๊ฒƒ์— ๊ด€๊ณ„์—†์ด, RFC์—์„œ ์—”ํ‹ฐํ‹ฐ๊ฐ€ RFC _completely _...๋ฅผ ์ค€์ˆ˜ ํ•  ๊ฒƒ์œผ๋กœ ํ•ด์„ํ•˜๊ณ  ๊ธฐ๋Œ€ํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋ถ„๋ช…ํžˆ ๊ทธ๋“ค์€ ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉฐ ๋‹ค์–‘ํ•œ ์œ ํ˜•์˜ ๋งŽ์€ ์ธํ„ฐ๋„ท ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋”์ฐํ•œ ๋ฉ์ฒญํ•œ ํ”„๋กœํ† ์ฝœ์˜ ์ ˆ๋ฐ˜ ๊ตฌํ˜„.

์ข‹์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ / ํ”„๋กœํ† ์ฝœ ๊ตฌํ˜„์€ ๊ฐ€๋Šฅํ•œ ํ•œ์ด๋ฅผ ์šฉ์„œํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. RFC1122๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. ํ†ต์‹ ์—์„œ ๋‹ค๋ฅธ ์ฐธ๊ฐ€์ž์—๊ฒŒ ์•ก์„ธ์Šคํ•˜์ง€ ์•Š๊ณ ๋„ (์ผ๋ถ€) ํ”„๋กœํ† ์ฝœ ์œ„๋ฐ˜์„ ์šฉ์„œํ•˜๋„๋ก ํ”„๋กœํ† ์ฝœ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ˜ธ์ŠคํŠธ๊ฐ€ 100 % ๊ทœ์ •์„ ์ค€์ˆ˜ํ•˜์ง€ ์•Š์œผ๋ฉด ๋„ˆ๋ฌด ๋‚˜์˜๋ฉด ์ด์•ผ๊ธฐํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ๋Š” ๊ฐ•๊ฒฝ ํ•œ ๊ฒฌํ•ด๋ฅผ ๊ฐ€์ง€๊ณ ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋„ˆ๋ฌด ๋‚˜์˜๋‹ค. ๊ทธ๊ฒƒ์€ ๋งˆ์น˜ '๋‚˜๋Š” ์—ฌ์™•์˜ ์˜์–ด ๋งŒ ํ•  ์ค„ ์••๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ์ด์ƒํ•œ ์˜์–ด ๋ฐฉ์–ธ์œผ๋กœ ๋‚˜์—๊ฒŒ ๋งํ•˜๋ฉด ๋‚˜๋Š” ๋‹น์‹ ์—๊ฒŒ ๋งํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.'

๋„์„œ๊ด€์˜ ์ „์ฒด ์š”์ ์ด ์˜์‚ฌ ์†Œํ†ต์„ ์šฉ์ดํ•˜๊ฒŒํ•˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด ๋ถ„๋ช…ํžˆ ์ด๊ฒƒ์€ ์ข‹์€ ์ ‘๊ทผ ๋ฐฉ์‹์ด ์•„๋‹™๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋‚ด ์ œ์•ˆ์€ ํ”„๋กœํ† ์ฝœ์˜ ์—„๊ฒฉํ•œ ์š”๊ตฌ ์‚ฌํ•ญ์„ ์™„ํ™” ํ•  ์ˆ˜์žˆ๋Š” ๊ณณ์„ ๊ณ ๋ คํ•˜๊ณ  (์•„๋งˆ๋„ "์™„์ „ํ•œ"์ฒ˜๋ฆฌ๋ฅผ ์ผœ๋Š” ๋ฐฉ๋ฒ• kwargs๋ฅผ ํ†ตํ•ด) ์ƒ๋Œ€๋ฐฉ์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ๋•Œ๋„ ๋ง์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. .

์ด ๋น„ํ‰ ์™ธ์—๋„ ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ์œ„ํ•œ ๋ชจ๋“  ์ž‘์—…์— ๋Œ€ํ•ด aio-libs ์˜ ๊ธฐ์—ฌ์ž ๋ฐ ๊ท€ํ•˜์—๊ฒŒ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๐Ÿ‘ ๐Ÿฅ‡

@asvetlov ์ด๊ฒƒ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ์ž…๋‹ˆ๋‹ค ... ์ œ (๋น„ ๊ทœ๊ฒฉ) ์„œ๋ฒ„๊ฐ€ ์ž‘๋™ํ•˜๊ธฐ ์ „์— ๋Œ€๋ฌธ์ž Upgrade: ํ—ค๋”๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„ ๋ƒˆ์Šต๋‹ˆ๋‹ค. ์ œ๋ชฉ ์ผ€์ด์Šค ํ—ค๋” ๋ฌธ์ œ๋ฅผ ๊ณ ๋ คํ•˜๋Š” ๊ฒฝ์šฐ์— ์–ธ๊ธ‰ ํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ œ๋Œ€๋กœ ๊ตฌํ˜„๋˜์ง€ ์•Š์€ ์„œ๋ฒ„์˜ ํ”„๋กœํ† ์ฝœ ์œ„๋ฐ˜์œผ๋กœ ์ธํ•ด ์›น ์†Œ์ผ“ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ์‹ค์ œ ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค ..

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰