# サーバーに接続(サンプルフロー実行後)
nc localhost 9000
# メッセージを入力してEnter
Hello Server!
# サーバーからのレスポンスが表示される
📥 サンプルフローJSON(クリックで展開)
[
{
"id": "tcp_sample_tab",
"type": "tab",
"label": "TCP サンプル",
"disabled": false,
"info": ""
},
{
"id": "tcp_comment1",
"type": "comment",
"z": "tcp_sample_tab",
"name": "━━━ TCPサーバー(エコー) ━━━",
"info": "ポート9000で待ち受け、受信データをそのまま返す",
"x": 200,
"y": 40,
"wires": []
},
{
"id": "tcp_in_server",
"type": "tcp in",
"z": "tcp_sample_tab",
"name": "TCP Server :9000",
"server": "server",
"host": "",
"port": "9000",
"datamode": "stream",
"datatype": "utf8",
"newline": "",
"topic": "",
"trim": false,
"base64": false,
"tls": "",
"x": 180,
"y": 100,
"wires": [["tcp_func_echo", "tcp_debug_received"]]
},
{
"id": "tcp_func_echo",
"type": "function",
"z": "tcp_sample_tab",
"name": "エコー処理",
"func": "// 受信データに情報を追加して返信\nvar received = msg.payload;\nvar clientInfo = msg.ip + \":\" + msg.port;\n\nnode.status({fill: \"green\", shape: \"dot\", text: clientInfo});\n\nmsg.payload = \"[Echo] \" + received + \" (from \" + clientInfo + \")\\n\";\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 390,
"y": 100,
"wires": [["tcp_out_reply"]]
},
{
"id": "tcp_out_reply",
"type": "tcp out",
"z": "tcp_sample_tab",
"name": "Reply to Client",
"host": "",
"port": "",
"beserver": "reply",
"base64": false,
"end": false,
"tls": "",
"x": 600,
"y": 100,
"wires": []
},
{
"id": "tcp_debug_received",
"type": "debug",
"z": "tcp_sample_tab",
"name": "受信データ",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "ip",
"statusType": "msg",
"x": 390,
"y": 140,
"wires": []
},
{
"id": "tcp_comment2",
"type": "comment",
"z": "tcp_sample_tab",
"name": "━━━ TCP Request(クライアント) ━━━",
"info": "上記サーバーにリクエストを送信",
"x": 210,
"y": 220,
"wires": []
},
{
"id": "tcp_inject_request",
"type": "inject",
"z": "tcp_sample_tab",
"name": "リクエスト送信",
"props": [{"p": "payload"}],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "Hello TCP Server!",
"payloadType": "str",
"x": 180,
"y": 280,
"wires": [["tcp_request"]]
},
{
"id": "tcp_request",
"type": "tcp request",
"z": "tcp_sample_tab",
"name": "TCP Request",
"server": "localhost",
"port": "9000",
"out": "sit",
"ret": "string",
"splitc": " ",
"newline": "",
"trim": false,
"tls": "",
"x": 390,
"y": 280,
"wires": [["tcp_debug_response"]]
},
{
"id": "tcp_debug_response",
"type": "debug",
"z": "tcp_sample_tab",
"name": "レスポンス",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "\"受信\"",
"statusType": "str",
"x": 590,
"y": 280,
"wires": []
},
{
"id": "tcp_comment3",
"type": "comment",
"z": "tcp_sample_tab",
"name": "━━━ JSONデータの送受信 ━━━",
"info": "",
"x": 190,
"y": 360,
"wires": []
},
{
"id": "tcp_in_json_server",
"type": "tcp in",
"z": "tcp_sample_tab",
"name": "TCP Server :9001",
"server": "server",
"host": "",
"port": "9001",
"datamode": "stream",
"datatype": "utf8",
"newline": "\\n",
"topic": "",
"trim": false,
"base64": false,
"tls": "",
"x": 180,
"y": 420,
"wires": [["tcp_func_json_process"]]
},
{
"id": "tcp_func_json_process",
"type": "function",
"z": "tcp_sample_tab",
"name": "JSON処理",
"func": "try {\n // 受信データをJSONとしてパース\n var data = JSON.parse(msg.payload);\n \n // 処理結果を作成\n var response = {\n status: \"success\",\n received: data,\n processed: true,\n timestamp: new Date().toISOString()\n };\n \n msg.payload = JSON.stringify(response) + \"\\n\";\n} catch(e) {\n msg.payload = JSON.stringify({status: \"error\", message: \"Invalid JSON\"}) + \"\\n\";\n}\n\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 390,
"y": 420,
"wires": [["tcp_out_json_reply", "tcp_debug_json"]]
},
{
"id": "tcp_out_json_reply",
"type": "tcp out",
"z": "tcp_sample_tab",
"name": "Reply JSON",
"host": "",
"port": "",
"beserver": "reply",
"base64": false,
"end": false,
"tls": "",
"x": 590,
"y": 420,
"wires": []
},
{
"id": "tcp_debug_json",
"type": "debug",
"z": "tcp_sample_tab",
"name": "JSON処理結果",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"x": 600,
"y": 460,
"wires": []
},
{
"id": "tcp_inject_json",
"type": "inject",
"z": "tcp_sample_tab",
"name": "JSONリクエスト",
"props": [{"p": "payload"}],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "{\"command\":\"getData\",\"id\":123}",
"payloadType": "str",
"x": 180,
"y": 500,
"wires": [["tcp_request_json"]]
},
{
"id": "tcp_request_json",
"type": "tcp request",
"z": "tcp_sample_tab",
"name": "TCP Request JSON",
"server": "localhost",
"port": "9001",
"out": "char",
"ret": "string",
"splitc": "\\n",
"newline": "",
"trim": false,
"tls": "",
"x": 410,
"y": 500,
"wires": [["tcp_debug_json_response"]]
},
{
"id": "tcp_debug_json_response",
"type": "debug",
"z": "tcp_sample_tab",
"name": "JSONレスポンス",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "payload",
"targetType": "msg",
"statusVal": "\"JSON受信\"",
"statusType": "str",
"x": 620,
"y": 500,
"wires": []
},
{
"id": "tcp_comment4",
"type": "comment",
"z": "tcp_sample_tab",
"name": "━━━ 接続状態の監視 ━━━",
"info": "",
"x": 180,
"y": 580,
"wires": []
},
{
"id": "tcp_in_monitor",
"type": "tcp in",
"z": "tcp_sample_tab",
"name": "TCP Server :9002",
"server": "server",
"host": "",
"port": "9002",
"datamode": "stream",
"datatype": "utf8",
"newline": "",
"topic": "",
"trim": false,
"base64": false,
"tls": "",
"x": 180,
"y": 640,
"wires": [["tcp_func_monitor"]]
},
{
"id": "tcp_func_monitor",
"type": "function",
"z": "tcp_sample_tab",
"name": "接続管理",
"func": "// クライアント情報を取得\nvar clientId = msg.ip + \":\" + msg.port;\nvar session = msg._session;\n\n// 接続リストを管理\nvar clients = flow.get('tcpClients') || {};\n\nif (msg.payload === '') {\n // 空データは切断を意味することがある\n delete clients[session];\n node.warn(\"Client disconnected: \" + clientId);\n} else {\n // 接続中のクライアントを記録\n clients[session] = {\n ip: msg.ip,\n port: msg.port,\n lastSeen: new Date().toISOString()\n };\n}\n\nflow.set('tcpClients', clients);\nnode.status({fill: \"blue\", shape: \"dot\", text: Object.keys(clients).length + \" clients\"});\n\nmsg.payload = \"Welcome! You are: \" + clientId + \"\\n\";\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 390,
"y": 640,
"wires": [["tcp_out_monitor"]]
},
{
"id": "tcp_out_monitor",
"type": "tcp out",
"z": "tcp_sample_tab",
"name": "Reply",
"host": "",
"port": "",
"beserver": "reply",
"base64": false,
"end": false,
"tls": "",
"x": 570,
"y": 640,
"wires": []
}
]
演習4: 2台のNode-RED間通信 上級
📋 課題: センサーデータを送信するクライアントと、受信して処理するサーバーを同一フロー内に作成してください。
🎯 要求仕様:
- サーバー: ポート9103で待ち受け、JSONデータを受信して処理
- クライアント: 5秒ごとにランダムなセンサーデータを送信
- 送信データ:
{"sensor":"temp","value":25.5}
✅ 成功の条件:
- デプロイ後、5秒ごとにクライアント側のデバッグパネルにサーバーからの応答が表示される
- サーバー側のデバッグパネルに受信した温度データが表示される
- サーバーノードのステータスに「sensor: XX.X」形式で最新値が表示される
- クライアントのデバッグパネルに「送受信完了」ステータスが表示される
💡 ヒント
クライアント側: Inject(5秒repeat)→ Function(JSON生成)→ TCP Request
サーバー側: TCP In → Function(JSON処理)→ TCP Out
改行文字(\n)を区切りとして使うと、JSONデータの境界が分かりやすくなります。
✅ 解答例フロー
[
{"id": "ex4_tab", "type": "tab", "label": "演習4"},
{"id": "ex4_comment1", "type": "comment", "z": "ex4_tab", "name": "【サーバー】", "info": "", "x": 130, "y": 40, "wires": []},
{"id": "ex4_tcp_in", "type": "tcp in", "z": "ex4_tab", "name": "TCP Server :9103", "server": "server", "host": "", "port": "9103", "datamode": "stream", "datatype": "utf8", "newline": "\\n", "topic": "", "trim": false, "base64": false, "tls": "", "x": 180, "y": 100, "wires": [["ex4_func_server"]]},
{"id": "ex4_func_server", "type": "function", "z": "ex4_tab", "name": "JSONパース", "func": "try {\n var data = JSON.parse(msg.payload);\n node.status({fill: \"green\", shape: \"dot\", text: data.sensor + \": \" + data.value});\n msg.payload = JSON.stringify({status: \"received\", data: data}) + \"\\n\";\n} catch(e) {\n msg.payload = JSON.stringify({status: \"error\"}) + \"\\n\";\n}\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 400, "y": 100, "wires": [["ex4_tcp_out", "ex4_debug_server"]]},
{"id": "ex4_tcp_out", "type": "tcp out", "z": "ex4_tab", "name": "Reply", "host": "", "port": "", "beserver": "reply", "base64": false, "end": false, "tls": "", "x": 590, "y": 100, "wires": []},
{"id": "ex4_debug_server", "type": "debug", "z": "ex4_tab", "name": "サーバー処理", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "payload", "targetType": "msg", "x": 610, "y": 140, "wires": []},
{"id": "ex4_comment2", "type": "comment", "z": "ex4_tab", "name": "【クライアント】", "info": "", "x": 140, "y": 200, "wires": []},
{"id": "ex4_inject", "type": "inject", "z": "ex4_tab", "name": "5秒ごと", "props": [{"p": "payload"}], "repeat": "5", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 150, "y": 260, "wires": [["ex4_func_client"]]},
{"id": "ex4_func_client", "type": "function", "z": "ex4_tab", "name": "センサーデータ生成", "func": "var temp = (20 + Math.random() * 10).toFixed(1);\nmsg.payload = JSON.stringify({\n sensor: \"temperature\",\n value: parseFloat(temp),\n timestamp: new Date().toISOString()\n}) + \"\\n\";\nreturn msg;", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 380, "y": 260, "wires": [["ex4_tcp_request"]]},
{"id": "ex4_tcp_request", "type": "tcp request", "z": "ex4_tab", "name": "TCP Request", "server": "localhost", "port": "9103", "out": "char", "ret": "string", "splitc": "\\n", "newline": "", "trim": false, "tls": "", "x": 590, "y": 260, "wires": [["ex4_debug_client"]]},
{"id": "ex4_debug_client", "type": "debug", "z": "ex4_tab", "name": "クライアント受信", "active": true, "tosidebar": true, "console": false, "tostatus": true, "complete": "payload", "targetType": "msg", "statusVal": "\"送受信完了\"", "statusType": "str", "x": 790, "y": 260, "wires": []}
]