{"title":"John Engineering Stuff","link":[{"@attributes":{"href":"https:\/\/johnliu55.tw\/","rel":"alternate"}},{"@attributes":{"href":"https:\/\/johnliu55.tw\/feeds\/all.atom.xml","rel":"self"}}],"id":"https:\/\/johnliu55.tw\/","updated":"2021-06-25T00:00:00+08:00","entry":[{"title":"3D\u5217\u5370\u4e8c\u4e09\u4e8b","link":{"@attributes":{"href":"https:\/\/johnliu55.tw\/3d-printing-stuff.html","rel":"alternate"}},"published":"2021-06-25T00:00:00+08:00","updated":"2021-06-25T00:00:00+08:00","author":{"name":"John Liu"},"id":"tag:johnliu55.tw,2021-06-25:\/3d-printing-stuff.html","summary":"<p class=\"first last\">\u95dc\u65bc\u6211\u73a93D\u5217\u5370\u7684\u4e8b<\/p>\n","content":"<div class=\"admonition admonition-\">\n<p class=\"first admonition-title\">\u8b8a\u66f4\u8a18\u9304<\/p>\n<dl class=\"last docutils\">\n<dt>2020-06-27<\/dt>\n<dd>\u52a0\u5165\u300c <a class=\"reference external\" href=\"https:\/\/octoprint.org\/\">OctoPrint<\/a> \u300d<\/dd>\n<\/dl>\n<\/div>\n<p>\u9019\u7bc7\u7121\u95dc\u6280\u8853\uff0c\u55ae\u7d14\u60f3\u8b1b\u4e00\u4e0b\u6211\u73a9 3D \u5217\u5370\u4e00\u5e74\u591a\u4f86\u7684\u6545\u4e8b\u5475\u5475\u3002<\/p>\n<p>\u6211\u4e00\u76f4\u90fd\u5f88\u559c\u6b61 DIY\u3001\u81ea\u5df1\u5f04\u6771\u5f04\u897f\u7684\u611f\u89ba\u3002\u5538\u5927\u5b78\u7684\u6642\u5019\u628a KTR \u7d66\u5927\u6539\u6210 Cafe Racer\uff1b\u79df\u7a7a\u623f\u8cb7\u4e86\u5e7e\u842c\u584a\u7684 Ikea \u50a2\u4ff1\u5168\u90e8\u81ea\u5df1\u7d44\uff1b\u9078\u7528 ArchLinux \u628a\u81ea\u5df1\u7684 Linux \u7d66\u62fc\u6e4a\u8d77\u4f86\u3002\u96d6\u7136\u5f88\u7d2f\uff0c\u4f46\u6211\u5f88\u4eab\u53d7\u9019\u4e9b\u904e\u7a0b\u3002<\/p>\n<p>\u4e5f\u5fd8\u4e86\u662f\u70ba\u4ec0\u9ebc\uff0c2019 \u5e74\u6642\u6211\u5076\u7136\u5f97\u77e5\u4e86\u300c3D \u5370\u8868\u6a5f\u300d\u9019\u500b\u6771\u897f\u3002\u9019\u73a9\u610f\u5152\u592a\u8b9a\u4e86\u5427\uff01\u53ea\u8981\u96fb\u8166\u756b\u7684\u51fa\u4f86\uff0c\u6211\u5c31\u53ef\u4ee5\u628a\u4ed6\u8b8a\u6210\u5be6\u9ad4\uff0c\u60f3\u5230\u5c31\u597d\u723d\u554a\uff01<\/p>\n<p>\u6240\u4ee5\u5728 2019 \u5e74\u5e95\uff0c\u6211\u4e0b\u5b9a\u6c7a\u5fc3\u8cb7\u4e86\u7b2c\u4e00\u53f0 3D \u5370\u8868\u6a5f\uff1a\n<a class=\"reference external\" href=\"https:\/\/www.anycubic.com\/products\/anycubic-i3-mega-s\">Anycubic i3 Mega S<\/a> \u3002<\/p>\n<p>\u9760\u8457\u9019\u53f0\u6a5f\u5668\u6211\u7406\u89e3\u4e86 3D \u5217\u5370\u7684\u6982\u5ff5\uff0c\u5370\u4e86\u4e0d\u5c11\u6771\u897f\u5f8c\u79c9\u6301\u8457 DIY \u7cbe\u795e\u4e5f\u6539\u88dd\u4e86\u4e0d\u5c11\u6771\u897f\u4e0a\u53bb\uff0c\u5f9e\u52a0\u71c8\u689d\u3001\u71d2\u5ba2\u88fd Firmware\u3001\u5230\u81ea\u5df1\u8a2d\u8a08 Direct Extruder\uff0c\u73a9\u7684\u4e0d\u4ea6\u6a02\u4e4e\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"Anycubic i3 Mega S \u6700\u7d42\u578b\u614b \" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/anycubic_i3.jpg\" style=\"width: 600px;\" \/>\n<div class=\"legend\">\nAnycubic i3 Mega S \u6700\u7d42\u578b\u614b<\/div>\n<\/div>\n<p>\u5230\u4e86 2020 \u5e74\u5e95\uff0c\u6211\u767c\u73fe\u5927\u90e8\u5206\u7684\u6642\u9593\u6211\u90fd\u662f\u5728\u300c\u6539\u9032\u5b83\u300d\u800c\u4e0d\u662f\u300c\u7528\u5b83\u300d\uff0c\u89ba\u5f97\u9019\u6a23\u4e0b\u53bb\u597d\u50cf\u4e0d\u592a\u5c0d\u52c1\uff0c\u6211\u61c9\u8a72\u82b1\u591a\u9ede\u6642\u9593\u5728 3D \u5217\u5370\u672c\u8eab\u3002\u65bc\u662f\u54a7\uff0c\u8d81\u8457 Cyber Monday \u514d\u904b\u6d3b\u52d5\u8a02\u4e86\u4e00\u53f0\n<a class=\"reference external\" href=\"https:\/\/www.prusa3d.com\/original-prusa-i3-mk3\/\">Prusa i3 MK3S+<\/a> \u3002<\/p>\n<p>Prusa \u662f\u4e00\u9593\u4f4d\u65bc\u6377\u514b\u7684\u516c\u53f8\uff0c\u4ed6\u5011\u7684 Prusa i3 \u7cfb\u5217\u662f\u4e00\u53f0\u9700\u8981\u81ea\u5df1\u5f9e\u96f6\u4ef6\u958b\u59cb\u7d44\u88dd\uff0c\u800c\u4e14\u865f\u7a31\u540c\u50f9\u683c\u5e36\u4e2d\u5217\u5370\u54c1\u8cea\u6700\u597d\u7684\u6a5f\u5668\uff0c\u4f46\u6bd4\u8d77\u4e2d\u570b\u724c\u5b50\u7684\u6a5f\u5668\u9084\u662f\u8cb4\u4e0a\u4e0d\u5c11\uff08749 \u9382\u4e0d\u542b\u904b\uff09\uff0c\u6240\u4ee5\u7576\u521d\u7336\u8c6b\u8d85\u7d1a\u4e45\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"Prusa i3 assembling\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/prusa_assembling.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\nPrusa i3 MK3S+ \u7d44\u88dd\u904e\u7a0b<\/div>\n<\/div>\n<p>\u7d44\u88dd\u82b1\u4e86\u6211\u5169\u5929\u7684\u6642\u9593\uff0c\u4e0d\u904e\u4ed6\u958b\u59cb\u5370\u7684\u6642\u5019\u771f\u7684\u8d85\u6709\u6210\u5c31\u611f\uff0c\u5370\u51fa\u4f86\u7684\u54c1\u8cea\u4e5f\u4e0d\u662f\u958b\u73a9\u7b11\uff0c\u8ddf Anycubic \u771f\u7684\u6709\u5dee\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"Prusa Benchy\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/prusa_benchy.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\nPrusa i3 MK3S+ \u5370\u51fa\u4f86\u7684 Benchy<\/div>\n<\/div>\n<p><strong>\u300c\u4f60\u7d42\u7a76\u8981\u958b\u6b50\u6d32\u8eca\u7684\uff0c\u90a3\u70ba\u4ec0\u9ebc\u4e0d\u4e00\u958b\u59cb\u5c31\u958b\u5462\uff1f\u300d<\/strong><\/p>\n<p>\u6b38\uff0c\u4e0d\u904e\u4f5c\u70ba\u5165\u9580\uff0cAnycubic \u9084\u662f\u76f8\u7576\u7a31\u8077\u7684\uff0c\u800c\u4e14\u62c6\u62c6\u88dd\u88dd\u6539\u4f86\u6539\u53bb\u6bd4\u8f03\u4e0d\u6703\u5fc3\u75db\u3002<\/p>\n<div class=\"section\" id=\"id1\">\n<h2>\u7dda\u6750\u9632\u6f6e\u7bb1<\/h2>\n<p>\u65e2\u7136\u5347\u7d1a\u4e86\u5370\u8868\u6a5f\uff0c\u9031\u908a\u4e5f\u8981\u8ddf\u8457\u5347\u7d1a\u4e00\u4e0b\u3002<\/p>\n<p>Prusa i3 \u548c Anycubic i3 \u9019\u500b\u578b\u5f0f\uff08FDM\uff0c\u5176\u4ed6\u9084\u6709 SLS\u3001SLA \u7b49\uff09\u7684 3D \u5370\u8868\u6a5f\u9700\u8981\u300c\u7dda\u6750\u300d\uff08Filament\uff09\u7576\u539f\u6599\u4f86\u5370\u51fa\u6210\u54c1\uff0c \u800c\u7dda\u6750\u53c8\u6709\u5206\u4e0d\u540c\u7684\u5851\u81a0\u6750\u8cea\uff0c\u50cf\u662f PLA\u3001PETG\u3001ABS \u7b49\u7b49\u3002\u800c\u67d0\u4e9b\u6750\u8cea\u5bb9\u6613\u53d7\u6f6e\uff0c\u53d7\u6f6e\u5f8c\u6703\u9020\u6210\u5370\u51fa\u6210\u54c1\u54c1\u8cea\u8b8a\u5dee\uff0c\u5f37\u5ea6\u8b8a\u4f4e\uff0c\u751a\u81f3\u5370\u5230\u4e00\u534a\u5c31\u5931\u6557\u3002<\/p>\n<div class=\"figure\">\n<img alt=\" \u53d7\u6f6e\u5f8c\u5370\u51fa\u4f86\u7684\u6210\u54c1 \" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/wet_filament.jpg\" style=\"width: 800px;\" \/>\n<p class=\"caption\">Source: <a class=\"reference external\" href=\"https:\/\/www.matterhackers.com\/news\/filament-and-water\">MatterHacker<\/a><\/p>\n<div class=\"legend\">\n\u4e7e\u71e5\u548c\u53d7\u6f6e\u7dda\u6750\u5370\u51fa\u6210\u54c1\u6bd4\u8f03\uff0c\u53ef\u4ee5\u770b\u5230\u53d7\u6f6e\u7dda\u6750\u7684\u8868\u9762\u76f8\u7576\u7c97\u7cd9<\/div>\n<\/div>\n<p>\u5176\u4e2d\u4e00\u500b\u89e3\u6c7a\u65b9\u6cd5\u662f<strong>\u8b93\u7dda\u6750\u5728\u4f7f\u7528\u524d\u8b8a\u4e7e\u71e5<\/strong>\u3002\u6211\u4e00\u958b\u59cb\u662f\u7528\u9019\u500b\u65b9\u6cd5\uff0c\u56e0\u70ba\u53ea\u8981\u62ff\u4e00\u53f0\u98df\u7269\u4e7e\u71e5\u6a5f\u4f86\u6539\u88dd\u4e00\u4e0b\u5c31\u884c\u4e86\u3002\u751a\u81f3\u5982\u679c\u6709\u6eab\u63a7\u70e4\u7bb1\uff0c\u4f7f\u7528\u4e4b\u524d\u62ff\u7dda\u6750\u4f4e\u6eab\u70d8\u70e4\u4e00\u4e0b\u4e5f\u53ef\u4ee5\uff0c\u4fbf\u5b9c\u53c8\u7c21\u55ae\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"DIY \u7dda\u6750\u4e7e\u71e5\u6a5f \" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/diy_filament_dryer.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\nDIY \u7dda\u6750\u4e7e\u71e5\u6a5f<\/div>\n<\/div>\n<p>\u4e0d\u904e\u6bcf\u6b21\u4f7f\u7528\u524d\u90fd\u8981\u70d8\u5b83\u500b\u5169\u4e09\u5c0f\u6642\uff0c\u4e45\u4e86\u4e5f\u662f\u6709\u9ede\u7169\u3002\u6240\u4ee5\u4e4b\u5f8c\u6c7a\u5b9a\u63a1\u7528\u53e6\u4e00\u500b\u65b9\u6cd5\uff0c<strong>\u8b93\u7dda\u6750\u7531\u59cb\u81f3\u7d42\u90fd\u5f85\u5728\u4e7e\u71e5\u7684\u74b0\u5883\u4e2d<\/strong>\uff0c\u9023\u53d7\u6f6e\u7684\u6a5f\u6703\u90fd\u4e0d\u8b93\u5b83\u6709\u3002\u800c\u95dc\u65bc\u9019\u7a2e\u6771\u897f\uff0c\u7db2\u8def\u4e0a\u6709\u76f8\u7576\u591a\u7684\u8cc7\u6e90\u53ef\u4ee5\u53c3\u8003\uff0c\u4f86\u81ea\u5df2 DIY\u3002\u9019\u908a\u8cbc\u4e00\u500b\u6211\u5f88\u559c\u6b61\u7684\u983b\u9053 <strong>CNC Kitchen<\/strong> \u5be6\u4f5c\u7dda\u6750\u9632\u6f6e\u7bb1\u7684\u5f71\u7247\uff1a<\/p>\n<div class=\"youtube youtube-16x9\"><iframe src=\"https:\/\/www.youtube.com\/embed\/WEFtUKGAd7k\" width=\"800\" height=\"450\" allowfullscreen seamless frameBorder=\"0\"><\/iframe><\/div><p>\u9019\u7bb1\u5b50\u7684\u57fa\u672c\u6982\u5ff5\u5f88\u7c21\u55ae\uff1a<\/p>\n<ol class=\"arabic simple\">\n<li>\u627e\u4e00\u500b\u5920\u5927\u7684\u5bc6\u5c01\u5f0f\u9632\u6f6e\u7bb1<\/li>\n<li>\u88e1\u9762\u52a0\u500b\u652f\u67b6\u653e\u7dda\u6750\u5377\uff08Filament Spool\uff09<\/li>\n<li>\u947d\u5e7e\u500b\u6d1e\u8b93\u7dda\u6750\u80fd\u51fa\u5165<\/li>\n<li>\u628a\u9664\u6fd5\u5291\u5012\u9032\u53bb\uff0c\u518d\u653e\u4e00\u53f0\u6fd5\u5ea6\u8a08\u76e3\u63a7\u6fd5\u5ea6<\/li>\n<li>\u84cb\u5b50\u84cb\u8d77\u4f86<\/li>\n<\/ol>\n<p>\u807d\u8d77\u4f86\u5f88\u7c21\u55ae\u561b\uff0c\u6240\u4ee5\u6211\u5c31\u505a\u4e86\u4e00\u500b\uff1a<\/p>\n<div class=\"figure\">\n<img alt=\"DIY Dry Box\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/dry_box.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\n\u6211\u7684 DIY \u7dda\u6750\u9632\u6f6e\u7bb1<\/div>\n<\/div>\n<p>\u8ddf\u5f71\u7247\u88e1\u7684\u6bd4\u8d77\u4f86\uff0c\u6211\u53e6\u5916\u8a2d\u8a08\u4e86<strong>\u5916\u90e8\u7dda\u6750\u67b6<\/strong>\u3002\u56e0\u70ba\u67d0\u4e9b\u7dda\u6750\u4e0d\u6613\u53d7\u6f6e\uff0c\u6240\u4ee5\u53ef\u4ee5\u653e\u5728\u4e00\u822c\u74b0\u5883\u4e2d\u4e5f\u4e0d\u6703\u5f71\u97ff\u54c1\u8cea\u3002\u6240\u4ee5\u6211\u5728\u7bb1\u84cb\u4e0a\u53e6\u5916\u8a2d\u8a08\u4e86\u652f\u67b6\u4f86\u653e\u9019\u4e9b\u7dda\u6750\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"DIY Dry Box Top Rack\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/dry_box_top_rack_3.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\n\u5916\u90e8\u7dda\u6750\u67b6<\/div>\n<\/div>\n<p>\u653e\u7dda\u6750\u7684\u687f\u5b50\u90fd\u662f 8mm \u7684\u78b3\u7e96\u7dad\u68d2\uff0c\u800c\u4e14\u5169\u5074\u90fd\u6709\u8ef8\u627f\u4f86\u8b93\u5b83\u53ef\u4ee5\u6ed1\u9806\u7684\u6efe\u52d5\u3002\u7528\u78b3\u7e96\u7dad\u68d2\u53ea\u662f\u56e0\u70ba\u627e\u4e0d\u5230\u540c\u898f\u683c\u7684\u91d1\u5c6c\u68d2\uff0c\u7d55\u5c0d\u4e0d\u662f\u56e0\u70ba\u78b3\u7e96\u7dad\u6bd4\u8f03\u6f6e\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"DIY Dry Box Bearing Rod\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/dry_box_bearing.jpg\" style=\"width: 800px;\" \/>\n<\/div>\n<div class=\"figure\">\n<img alt=\"DIY Dry Box Top Rack Bearing\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/dry_box_top_rack_2.jpg\" style=\"width: 800px;\" \/>\n<\/div>\n<div class=\"figure\">\n<img alt=\"DIY Dry Box Inside Rack Bearing\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/dry_box_inside_rack.jpg\" style=\"width: 800px;\" \/>\n<\/div>\n<p>\u5229\u7528 PTFE Tube \u52a0\u4e0a\u6211\u81ea\u5df1\u8a2d\u8a08\u7684\u652f\u67b6\u8b93\u7dda\u6750\u80fd\u9806\u5229\u7684\u8d70\u5230\u5370\u8868\u6a5f\uff1a<\/p>\n<div class=\"figure\">\n<img alt=\" \u7dda\u6750\u8d70\u7dda\u652f\u67b6 \" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/ptfe_rack.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\nPTFE Tube + \u652f\u67b6<\/div>\n<\/div>\n<p>\u6211\u8a2d\u8a08\u4e86\u4e00\u500b\u96f6\u4ef6\u8b93 PTFE Tube \u53ef\u4ee5\u56fa\u5b9a\u5728 Extruder \u4e0a\uff1a<\/p>\n<div class=\"figure\">\n<img alt=\"PTFE Tube Cap\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/ptfe_tube_cap.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\nPTFE Tube Cap<\/div>\n<\/div>\n<p>\u8a2d\u8a08\u9019\u500b\u96f6\u4ef6\u5f88\u7c21\u55ae\uff0c\u56e0\u70ba Prusa i3 \u7cfb\u5217\u5370\u8868\u6a5f\u662f open-sourced\uff01\u6240\u4ee5\u53ef\u4ee5\u76f4\u63a5\u62ff\u5230\u9019\u500b\u96f6\u4ef6\u7684 STL \u6a94\u9032\u884c\u4fee\u6539\u3002<\/p>\n<p>\u628a\u7bb1\u5b50\u8ddf\u5370\u8868\u6a5f\u7d44\u5408\u5728\u4e00\u8d77\u6700\u5f8c\u9577\u9019\u6a23\uff1a<\/p>\n<div class=\"figure\">\n<img alt=\"DIY Dry Box with Prusa i3\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/dry_box_all.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\n\u6307\u63ee\u8247\u7d44\u5408<\/div>\n<\/div>\n<p><strong>\u9019\u6771\u897f\u597d\u7528\u5230\u4e0d\u884c\u554a\uff01<\/strong>\u4e0d\u4f46\u80fd\u4fdd\u6301\u7dda\u6750\u4e7e\u71e5\uff0c\u9592\u7f6e\u7684\u7dda\u6750\u73fe\u5728\u4e5f\u6709\u5730\u65b9\u653e\u4e86\uff0c\u4e0d\u6703\u6efe\u4f86\u6efe\u53bb\u7684\uff0c\u8d85\u65b9\u4fbf\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"octoprint\">\n<h2>OctoPrint<\/h2>\n<p>\u4e0d\u7ba1\u662f Anycubic i3 Mega S \u6216\u662f Prusa i3 MK3S+\uff0c\u8981\u958b\u59cb\u5217\u5370\u90fd\u6709\u500b\u7e41\u96dc\u7684\u6d41\u7a0b\uff1a<\/p>\n<ol class=\"arabic simple\">\n<li>\u628a SD \u5361\u63d2\u5230\u96fb\u8166\u4e0a<\/li>\n<li>\u628a Slicer \u7522\u751f\u51fa\u4f86\u7684 <tt class=\"docutils literal\">.gcode<\/tt> \u6a94\u5f9e\u96fb\u8166\u653e\u5230 SD \u5361\u4e0a<\/li>\n<li>\u628a SD \u5361\u5f9e\u96fb\u8166\u62d4\u51fa\u4f86\uff0c\u63d2\u5230\u5370\u8868\u6a5f\u4e0a<\/li>\n<li>\u5f9e\u5370\u8868\u6a5f\u9078\u64c7 SD \u5361\u4e0a\u7684\u6a94\u6848\uff0c\u958b\u59cb\u5217\u5370<\/li>\n<\/ol>\n<div class=\"figure\">\n<img alt=\"Prusa i3 SD card\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/prusa_sdcard.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\nPrusa i3 MK3S+ SD \u5361\u5217\u5370\u4ecb\u9762<\/div>\n<\/div>\n<p>\u6709\u4e9b\u8fd1\u671f\u63a8\u51fa\u7684 3D \u5370\u8868\u6a5f\u5df2\u7d93\u5167\u5efa WiFi \u529f\u80fd\uff0c\u63db\u53e5\u8a71\u8aaa\u5c31\u662f\u80fd\u5920\u900f\u904e\u7db2\u8def\u4f86\u50b3\u8f38\n<tt class=\"docutils literal\">.gocde<\/tt> \u6a94\uff0c\u4e0d\u7528\u518d\u63d2\u63d2\u62d4\u62d4\u7684\u4e86\u3002\u800c\u70ba\u4e86\u8b93 Prusa i3 MK3S+ \u80fd\u5920\u652f\u63f4\u7db2\u8def\u5217\u5370\uff0c\u6211\u5011\u53ef\u4ee5\u900f\u904e\n<a class=\"reference external\" href=\"https:\/\/octoprint.org\/\">OctoPrint<\/a> \u9019\u500b open-source \u5c08\u6848\u4f86\u9054\u6210\u3002<\/p>\n<div class=\"figure\">\n<a class=\"reference external image-reference\" href=\"https:\/\/octoprint.org\/\"><img alt=\"OctoPrint Logo\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/octoprint_logo.png\" style=\"width: 600px;\" \/><\/a>\n<\/div>\n<p>OctoPrint \u63d0\u4f9b\u4e86\u4e00\u500b Web UI \u8b93\u4f7f\u7528\u8005\u80fd\u5920\u9060\u7aef\u76e3\u63a7\u5370\u8868\u6a5f\u76ee\u524d\u7684\u72c0\u614b\uff0c\u4ee5\u53ca\u5c07 <tt class=\"docutils literal\">.gcode<\/tt> \u6a94\u300c\u4e32\u6d41\u300d\u81f3\u5370\u8868\u6a5f\u7684\u529f\u80fd\u3002\u4e5f\u5c31\u662f\u9019\u500b\u300c\u4e32\u6d41\u300d\u529f\u80fd\uff0c\u8b93\u6211\u5011\u53ef\u4ee5\u9060\u7aef\u5217\u5370\uff0c\u4e0d\u7528\u518d\u900f\u904e SD \u5361\u4e86\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"OctoPrint UI\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/octoprint_ui.png\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\nOctoPrint Web UI<\/div>\n<\/div>\n<p>\u6709\u4e86 OctoPrint \u4e4b\u5f8c\uff0c\u5217\u5370\u6d41\u7a0b\u8b8a\u6210\u4e86\uff1a<\/p>\n<ol class=\"arabic simple\">\n<li>\u628a Slicer \u7522\u751f\u51fa\u4f86\u7684 <tt class=\"docutils literal\">.gcode<\/tt> \u6a94\u900f\u904e Web UI \u4e0a\u50b3\u81f3 OctoPrint<\/li>\n<li>\u5f9e OctoPrint Web UI \u6307\u793a 3D \u5370\u8868\u6a5f\u958b\u59cb\u5217\u5370\u9019\u500b <tt class=\"docutils literal\">.gcode<\/tt> \u6a94<\/li>\n<\/ol>\n<p>\u540c\u6642\u6211\u5011\u4e5f\u53ef\u4ee5\u5f9e Web UI \u4e0a\u76e3\u63a7\u5217\u5370\u904e\u7a0b\uff0c\u8212\u670d\u3002<\/p>\n<div class=\"section\" id=\"octopi\">\n<h3>OctoPi<\/h3>\n<p>OctoPrint \u662f\u7528 Python \u5beb\u7684\uff0c\u4e26\u4f7f\u7528 USB \u4ecb\u9762\u8ddf 3D \u5370\u8868\u6a5f\u6e9d\u901a\uff0c\u6240\u4ee5\u57fa\u672c\u4e0a\u4efb\u4f55<strong>\u6709 USB \u4e14\u80fd\u8dd1 Python \u7684\u5e73\u53f0<\/strong>\u90fd\u53ef\u4ee5\u5b89\u88dd OctoPrint\u3002\u4e0d\u904e\u5927\u5bb6\u6700\u5e38\u7528\u7684\u9084\u662f Raspberry Pi\uff0c\u56e0\u70ba\u4fbf\u5b9c\u597d\u53d6\u5f97\uff0c\u4e14 OctoPrint \u5b98\u65b9\u4e5f\u63d0\u4f9b\n<a class=\"reference external\" href=\"https:\/\/octoprint.org\/download\/\">OctoPi<\/a> \u9019\u500b image \u53ef\u4ee5\u76f4\u63a5\u5c07 OctoPrint\n\u4f48\u7f72\u81f3 Raspberry Pi \u4e0a\u3002\u6211\u539f\u672c\u662f\u62ff\u4e00\u584a\u9592\u7f6e\u7684 Beaglebone Black \u4f86\u8dd1\uff0c\u4f46\u8dd1\u8d77\u4f86\u5be6\u5728\u662f\u76f8\u7576\u6162\u2026\u6240\u4ee5\u6700\u5f8c\u9084\u662f\u8cb7\u4e86\u4e8c\u624b Raspberry Pi 3B \u4f86\u4f48\u7f72\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"OctoPi\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/octopi.jpg\" style=\"width: 600px;\" \/>\n<div class=\"legend\">\nRaspberry Pi 3B \u8207 Prusa i3 MK3S+<\/div>\n<\/div>\n<p>\u53e6\u5916\u5462\uff0cOctoPrint \u9084\u6709\u5404\u5f0f\u5404\u6a23\u7684 Plugin \u53ef\u4ee5\u88dd\uff0c\u50cf\u662f\n<a class=\"reference external\" href=\"https:\/\/github.com\/OctoPrint\/OctoPrint-FirmwareUpdater\/blob\/master\/README.md\">Firmware Updater<\/a>\n\u80fd\u5920\u9060\u7aef\u66f4\u65b0\u5370\u8868\u6a5f Firmware\u3001\n<a class=\"reference external\" href=\"https:\/\/github.com\/jneilliii\/OctoPrint-BedLevelVisualizer\">Bed Visualizer<\/a>\n\u80fd\u5920\u770b\u5230 Bed Leveling \u7684\u72c0\u614b\u7b49\uff0c\u5be6\u5728\u597d\u7528\uff0c\u5efa\u8b70\u6709 3D \u5370\u8868\u6a5f\u4f46\u9084\u6c92\u88dd OctoPrint \u7684\u4e00\u5b9a\u8981\u8a66\u4e00\u4e0b\u3002<\/p>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"id4\">\n<h2>\u5370\u5217\u6210\u54c1<\/h2>\n<p>\u9019\u908a\u653e\u4e00\u4e9b\u6211\u81ea\u5df1\u8a2d\u8a08\u7684\u5c0f\u6771\u897f\u3002<\/p>\n<div class=\"section\" id=\"id5\">\n<h3>\u7b46\u96fb\u7acb\u67b6<\/h3>\n<p>\u6211\u7684\u87a2\u5e55\u3001\u9375\u76e4\u3001\u6ed1\u9f20\u90fd\u662f\u5916\u63a5\u7684\uff0c\u6240\u4ee5\u7b46\u96fb\u5176\u5be6\u6c92\u6709\u5fc5\u8981\u6253\u958b\u3002\u70ba\u4e86\u7bc0\u7701\u7a7a\u9593\uff0c\u6211\u60f3\u8981\u628a\u7b46\u96fb\u7acb\u8d77\u4f86\u85cf\u5728\u87a2\u5e55\u5f8c\u9762\uff0c\u6240\u4ee5\u8a2d\u8a08\u4e86\u4e00\u500b\u7acb\u67b6\u4f86\u653e\u3002<\/p>\n<div class=\"figure\">\n<img alt=\" \u7b46\u96fb\u7acb\u67b6 \" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/laptop_rack.jpg\" style=\"width: 800px;\" \/>\n<\/div>\n<div class=\"figure\">\n<img alt=\" \u7b46\u96fb\u7acb\u67b6\u7368\u7167 \" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/laptop_rack_detail.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\n\u7b46\u96fb\u7acb\u67b6<\/div>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"usb-hub\">\n<h3>USB Hub \u652f\u67b6<\/h3>\n<p>\u540c\u6a23\u662f\u70ba\u7701\u7a7a\u9593\uff08\u623f\u9593\u662f\u6709\u591a\u5c0f\uff09\u8ddf\u6574\u7dda\uff0c\u6211\u8a2d\u8a08\u4e86\u4e00\u500b USB Hub \u7684\u652f\u67b6\uff0c\u628a\u6211\u7528\u7684 USB Hub \u56fa\u5b9a\u5728\u87a2\u5e55\u67b6\u4e0a\uff0c\u8b93\u6574\u9ad4\u66f4\u4e7e\u6de8\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"USB Hub \u652f\u67b6 \" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/usb_hub_rack.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\nUSB Hub \u652f\u67b6<\/div>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"raspberry-pi-4-platform\">\n<h3>Raspberry Pi 4 Platform<\/h3>\n<p>\u81ea\u5f9e Raspberry Pi 4 \u6709\u4e86 USB 3.0 \u5f8c\uff0c\u5916\u63a5 SSD \u8b8a\u6210\u5f88\u5408\u7406\u7684\u9078\u64c7\uff0c\u6240\u4ee5\u6211\u8a2d\u8a08\u4e86\u4e00\u500b SATA SSD Mount \u628a Raspberry Pi \u8ddf SSD \u7d50\u5408\u5728\u4e00\u8d77\u3002\u53e6\u5916\u70ba\u4e86\u5be6\u9a57\u9700\u6c42\u4e5f\u8a2d\u8a08\u4e86\u4e00\u500b\u652f\u67b6\u4f86\u88dd\u4e9b\u6309\u9215\u8ddf LED\u3002<\/p>\n<p>\u9019\u908a\u8981\u63d0\u4e00\u4e0b Raspberry Pi \u672c\u9ad4\u7684 Case \u4e26\u4e0d\u662f\u6211\u8a2d\u8a08\u7684\uff0c\u800c\u662f Thingiverse \u4e0a\u627e\u7684\uff1a\n<a class=\"reference external\" href=\"https:\/\/www.thingiverse.com\/thing:3726254\">Raspberry Pi 4 snap fit case with 30mm Fan<\/a>\n\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"Raspberry Pi 4 Mount\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/rpi4_mount.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\nRaspberry Pi 4 Mount\uff08\u8acb\u5ffd\u7565\u4e0a\u9762\u7684\u7070\u5875\uff09<\/div>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"id6\">\n<h3>\u53ef\u8abf\u5f0f\u96fb\u6e90\u4f9b\u61c9\u5668<\/h3>\n<p>\u81ea\u5df1\u6709\u6642\u5019\u6703\u73a9\u4e00\u4e9b\u5fae\u63a7\u5236\u5668\u7b49\u7b49\u7684\u6771\u897f\uff0c\u9019\u6642\u5019\u5c31\u9700\u8981\u4e00\u53f0\u53ef\u8abf\u5f0f\u96fb\u6e90\u4f9b\u61c9\u5668\uff08Bench Power Supply\uff09\u5566\u3002\u8cb7\u4e86\u4e00\u4e9b DIY \u96f6\u4ef6\u5f8c\u81ea\u5df1\u8a2d\u8a08\u500b\u5916\u6bbc\u628a\u5b83\u5011\u88dd\u8d77\u4f86\u3002<\/p>\n<div class=\"figure\">\n<img alt=\"Bench Power Supply Front\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/bench-power-supply\/front.jpg\" style=\"width: 800px;\" \/>\n<\/div>\n<div class=\"figure\">\n<img alt=\"Bench Power Supply Back\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/bench-power-supply\/back.jpg\" style=\"width: 800px;\" \/>\n<\/div>\n<div class=\"figure\">\n<img alt=\"Bench Power Supply Inside\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/bench-power-supply\/inside.jpg\" style=\"width: 800px;\" \/>\n<\/div>\n<div class=\"figure\">\n<img alt=\"Bench Power Supply Power On\" src=\"https:\/\/johnliu55.tw\/3d-printing-stuff\/images\/bench-power-supply\/on.jpg\" style=\"width: 800px;\" \/>\n<div class=\"legend\">\nDIY Bench Power Supply\uff08\u8acb\u5ffd\u7565\u8981\u5370\u8d85\u4e45\u61f6\u5f97\u518d\u5370\u4e00\u6b21\u6240\u4ee5\u984f\u8272\u4e0d\u592a\u642d\u7684\u4e0a\u84cb\uff09<\/div>\n<\/div>\n<hr class=\"docutils\" \/>\n<p>\u6211\u9084\u8a18\u5f97\u525b\u62ff\u5230\u7b2c\u4e00\u53f0\u5370\u8868\u6a5f\u6642\uff0c\u6211\u5e38\u5e38\u53ef\u4ee5\u76ef\u8457\u5217\u5370\u904e\u7a0b\u5e7e\u5341\u5206\u9418\u6c92\u554f\u984c\uff0c\u770b\u5230\u81ea\u5df1\u8a2d\u8a08\u7684\u6771\u897f\u4e00\u5c0f\u6b65\u4e00\u5c0f\u6b65\u88ab\u5370\u51fa\u4f86\u7684\u611f\u52d5\u4e4b\u60c5\u96e3\u4ee5\u8a00\u8a9e\u554a\uff01\u5c24\u5176\u5370\u5b8c\u4e4b\u5f8c\u62ff\u5728\u624b\u4e0a\u7684\u611f\u89ba\uff0c\u771f\u7684\u662f\u6eff\u8db3\u5230\u4e0d\u884c\u3002<\/p>\n<p>\u5148\u5206\u4eab\u5230\u9019\u88e1\uff0c\u6709\u8208\u8da3\u6211\u518d\u591a\u5beb\u4e00\u9ede\u3002<\/p>\n<\/div>\n<\/div>\n","category":[{"@attributes":{"term":"3D Printing"}},{"@attributes":{"term":"3D Printing"}}]},{"title":"\u70ba\u4f55\u9700\u8981 Async\/await \u8a9e\u6cd5\uff1f","link":{"@attributes":{"href":"https:\/\/johnliu55.tw\/async-await-why.html","rel":"alternate"}},"published":"2020-06-14T00:00:00+08:00","updated":"2020-06-14T00:00:00+08:00","author":{"name":"John Liu"},"id":"tag:johnliu55.tw,2020-06-14:\/async-await-why.html","summary":"<p class=\"first last\">\u5728\u975e\u540c\u6b65\u7a0b\u5f0f\u8a2d\u8a08\uff08Asynchronous Programming\uff09\u7684\u9818\u57df\u88e1\uff0c\n\u70ba\u4f55\u9700\u8981\u8981 Async\/await \u8a9e\u6cd5\uff1f<\/p>\n","content":"<p>\u8fd1\u5e74\u4f86\u6709\u8a31\u591a\u8a9e\u8a00\u90fd\u52a0\u5165\u4e86 <a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Async\/await\">Async\/await<\/a> \u9019\u500b\u8a9e\u6cd5\u4f86\u5e6b\u52a9\u958b\u767c\u8005\u64b0\u5beb\u975e\u540c\u6b65\u7a0b\u5f0f\u78bc\uff0c\u5176\u4e2d\u4e0d\u4e4f\u50cf JavaScript \u8207 Python \u7b49\u71b1\u9580\u8a9e\u8a00\uff0c\u548c <a class=\"reference external\" href=\"https:\/\/blog.rust-lang.org\/2019\/11\/07\/Async-await-stable.html\">Rust<\/a>\n\u9019\u6a23\u7684\u975c\u614b\u5f62\u5225\u8a9e\u8a00\u3002\u9019\u908a\u5c31\u8a18\u9304\u4e00\u4e0b\u6211\u81ea\u5df1\u5c0d\u5b83\u7684\u770b\u6cd5\u3002<\/p>\n<div class=\"section\" id=\"tl-dr\">\n<h2>TL;DR<\/h2>\n<p>\u6211\u8a8d\u70ba\uff0c\u4f7f\u7528 async \u8207 await \u4f86\u5beb\u975e\u540c\u6b65\u7684\u7a0b\u5f0f\u78bc\uff0c\u6700\u5927\u7684\u597d\u8655\u5728\u65bc\u4f60\u53ef\u4ee5\u7528<strong>\u540c\u6b65\u7a0b\u5f0f\u8a2d\u8a08\u7684\u7a0b\u5f0f\u78bc\u67b6\u69cb\u4f86\u5be6\u4f5c\u975e\u540c\u6b65\u7684\u908f\u8f2f<\/strong>\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"http\">\n<h2>\u4e09\u500b HTTP \u8acb\u6c42\u8207\u4ed6\u5011\u7684\u56de\u547c<\/h2>\n<p>\u8b6c\u5982\uff0c\u4eca\u5929\u4f60\u60f3\u4f7f\u7528\u67d0\u500b\u90e8\u843d\u683c\u7684 API \u4f86\u53d6\u5f97<strong>\u67d0\u500b\u4f5c\u8005<\/strong>\u5beb\u7684<strong>\u6700\u65b0\u6587\u7ae0<\/strong>\u4e0a\u7684<strong>\u6700\u65b0\u8a55\u8ad6<\/strong>\uff0c\u7136\u5f8c\u62ff\u5b83\u4f86\u505a\u67d0\u4ef6\u4e8b\u3002\u4ee5\u540c\u6b65\u7684\u67b6\u69cb\u4f86\u5beb\u7684\u8a71\uff0c\u4f60\u53ef\u80fd\u6703\u9019\u6a23\u5b50\u767c\u9001 HTTP \u8acb\u6c42\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"kd\">function<\/span> <span class=\"nx\">doStuff<\/span><span class=\"p\">(<\/span><span class=\"nx\">user_id<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">try<\/span> <span class=\"p\">{<\/span>\n        <span class=\"kd\">let<\/span> <span class=\"nx\">user<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">syncHttpGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/users\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">user_id<\/span><span class=\"p\">);<\/span>\n        <span class=\"kd\">let<\/span> <span class=\"nx\">post<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">syncHttpGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/posts\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">user<\/span><span class=\"p\">.<\/span><span class=\"nx\">posts<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">].<\/span><span class=\"nx\">id<\/span><span class=\"p\">);<\/span>\n        <span class=\"kd\">let<\/span> <span class=\"nx\">comment<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">syncHttpGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/comments\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">post<\/span><span class=\"p\">.<\/span><span class=\"nx\">comments<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">].<\/span><span class=\"nx\">id<\/span><span class=\"p\">);<\/span>\n        <span class=\"nx\">doSomethingToComment<\/span><span class=\"p\">(<\/span><span class=\"nx\">comment<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span> <span class=\"k\">catch<\/span> <span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"nx\">setGetCommentError<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/pre><\/div>\n<p>\u9019\u662f\u500b\u5f88\u5e38\u898b\u7684\u6a21\u5f0f\uff1a<strong>\u9700\u8981\u4f7f\u7528\u4e0a\u4e00\u500b\u8acb\u6c42\u6240\u5f97\u5230\u7684\u56de\u8986\u4f86\u767c\u9001\u4e0b\u4e00\u500b<\/strong>\u3002\u4ee5\u540c\u6b65\u7684\u7a0b\u5f0f\u78bc\u4f86\u5be6\u4f5c\u7684\u8a71\uff1a<\/p>\n<ol class=\"arabic simple\">\n<li>\u908f\u8f2f\u7c21\u55ae\u660e\u77ad\uff0c\u4e00\u773c\u5c31\u53ef\u4ee5\u770b\u51fa\u505a\u4e86\u54ea\u4e9b\u4e8b<\/li>\n<li>\u53ef\u4ee5\u76f4\u63a5\u5229\u7528\u8a9e\u8a00\u672c\u8eab\u7684\u932f\u8aa4\u8655\u7406\u6a5f\u5236\uff08try-catch\uff09<\/li>\n<\/ol>\n<p>\u4e0d\u904e\u5728\u55ae\u57f7\u884c\u7dd2\u7684\u60c5\u6cc1\u4e0b\uff0c\u540c\u6b65\u8a2d\u8a08\u6700\u5927\u554f\u984c\u5728\u65bc\u6bcf\u4e00\u500b <tt class=\"docutils literal\">syncHttpGet<\/tt>\n\u90fd\u662f\u4ee5 Blocking \u7684\u65b9\u5f0f\u5728\u57f7\u884c\uff0c\u6240\u4ee5\u5728\u5f97\u5230\u56de\u8986\u4e4b\u524d\u662f\u4e0d\u6703\u91cb\u653e\u8a72\u57f7\u884c\u7dd2\u7684\u3002\u4ee5\u524d\u7aef\u4f86\u8aaa\uff0c\u9019\u6703\u9020\u6210<strong>\u6574\u500b UI \u6703\u88ab\u51cd\u7d50\uff0c\u5b8c\u5168\u7121\u6cd5\u8655\u7406\u4f7f\u7528\u8005\u7684\u5176\u4ed6\u64cd\u4f5c<\/strong>\u3002\u65bc\u662f\u4e4e\uff0c\u524d\u7aef\u6216\u662f UI \u76f8\u95dc\u7684\u6771\u897f\u57fa\u672c\u90fd\u63a1\u7528\u975e\u540c\u6b65\u7a0b\u5f0f\u8a2d\u8a08\uff08Asynchronous Programming\uff09\u4f86\u5be6\u4f5c\u3002<\/p>\n<p>\u9084\u8a18\u5f97\u6211\u7b2c\u4e00\u6b21\u63a5\u89f8\u5230\u975e\u540c\u6b65\u7a0b\u5f0f\u8a2d\u8a08\uff0c\u6700\u96e3\u5c31\u96e3\u5728\u7406\u89e3<strong>\u975e\u540c\u6b65\u51fd\u5f0f\u4e0d\u6703\u76f4\u63a5\u628a\u7d50\u679c Return \u7d66\u4f60<\/strong>\u9019\u4ef6\u4e8b\u3002\u554a\u9019\u6a23\u6211\u662f\u8981\u600e\u6a23\u62ff\u5230\u7d50\u679c\u5566\uff0c\u6211\u4e0b\u4e00\u500b\u8acb\u6c42\u8981\u600e\u9ebc\u767c\uff1f<\/p>\n<img alt=\"It's hard man\" src=\"https:\/\/johnliu55.tw\/async-await-why\/images\/oh-come-on.gif\" \/>\n<p>\u8981\u89e3\u6c7a\u9019\u554f\u984c\u6709\u5e7e\u7a2e\u65b9\u6cd5\uff0c\u4e0d\u904e\u5728 JavaScript \u7684\u4e16\u754c\u88e1\uff0c\u9019\u4e9b\u975e\u540c\u6b65\u64cd\u4f5c\u901a\u5e38\u90fd\u6703\u4ee5<strong>\u63a5\u6536\u51fd\u5f0f\u70ba\u5f15\u6578\u7684\u51fd\u5f0f<\/strong>\u4f86\u5be6\u4f5c\uff0c\u628a\u6700\u5f8c\u5f97\u5230\u7684\u7d50\u679c\u76f4\u63a5\u4e1f\u7d66\u4f60\u6240\u6307\u5b9a\u7684\u51fd\u5f0f\u3002\u9019\u6a23\u4e00\u4f86\u5c31\u7b97\u4e0d Return \u503c\u56de\u53bb\uff0c\u4e5f\u53ef\u4ee5\u7e7c\u7e8c\u4e0b\u500b\u52d5\u4f5c\u3002<\/p>\n<p>\u9019\u500b\u88ab\u50b3\u9032\u53bb\u7684\u51fd\u5f0f\u5c31\u7a31\u70ba\u300c\u56de\u547c\u51fd\u5f0f\u300d\uff08Callback Function\uff09\u3002\u7528\u9019\u500b\u6982\u5ff5\u4f86\u5be6\u505a\u540c\u6a23\u7684\u908f\u8f2f\u5927\u6982\u6703\u9577\u9019\u6a23\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"kd\">function<\/span> <span class=\"nx\">doStuff<\/span><span class=\"p\">(<\/span><span class=\"nx\">user_id<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nx\">callbackHttpGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/users\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">user_id<\/span><span class=\"p\">,<\/span> <span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">resp<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">resp<\/span><span class=\"p\">.<\/span><span class=\"nx\">statusCode<\/span> <span class=\"o\">===<\/span> <span class=\"mf\">200<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n            <span class=\"nx\">callbackHttpGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/posts\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">resp<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">posts<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">].<\/span><span class=\"nx\">id<\/span><span class=\"p\">,<\/span> <span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">resp<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n                <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">resp<\/span><span class=\"p\">.<\/span><span class=\"nx\">statusCode<\/span> <span class=\"o\">===<\/span> <span class=\"mf\">200<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n                    <span class=\"nx\">callbackHttpGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/comments\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">resp<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">comments<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">].<\/span><span class=\"nx\">id<\/span><span class=\"p\">,<\/span> <span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">resp<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n                        <span class=\"k\">if<\/span> <span class=\"p\">(<\/span><span class=\"nx\">resp<\/span><span class=\"p\">.<\/span><span class=\"nx\">statusCode<\/span> <span class=\"o\">===<\/span> <span class=\"mf\">200<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n                            <span class=\"kd\">let<\/span> <span class=\"nx\">comment<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">resp<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">;<\/span>\n                            <span class=\"nx\">doSomethingToComment<\/span><span class=\"p\">(<\/span><span class=\"nx\">comment<\/span><span class=\"p\">);<\/span>\n                        <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\n                            <span class=\"nx\">setGetCommentError<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Cannot get comment detail&#39;<\/span><span class=\"p\">);<\/span>\n                        <span class=\"p\">}<\/span>\n                    <span class=\"p\">})<\/span>\n                <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\n                    <span class=\"nx\">setGetCommentError<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Cannot get post&#39;<\/span><span class=\"p\">);<\/span>\n                <span class=\"p\">}<\/span>\n            <span class=\"p\">})<\/span>\n        <span class=\"p\">}<\/span> <span class=\"k\">else<\/span> <span class=\"p\">{<\/span>\n            <span class=\"nx\">setGetCommentError<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Cannot get user&#39;<\/span><span class=\"p\">);<\/span>\n        <span class=\"p\">}<\/span>\n    <span class=\"p\">})<\/span>\n<span class=\"p\">}<\/span>\n<\/pre><\/div>\n<p><strong>\u5c0d\uff0c\u9019\u5c31\u662f\u56de\u547c\u5730\u7344\uff08Callback Hell\uff09<\/strong>\u3002\u56e0\u70ba\u5fc5\u9808\u5728\u5f97\u5230\u56de\u8986\u5f8c<strong>\u7528\u88e1\u9762\u7684\u8cc7\u6599\u518d\u6b21\u9032\u884c\u975e\u540c\u6b65\u64cd\u4f5c<\/strong>\uff0c\u65bc\u662f\u5c31\u51fa\u73fe\u4e86\u5728\u56de\u547c\u4e2d\u518d\u6b21\u4f7f\u7528\u56de\u547c\u7684\u6a21\u5f0f\u3002\u5c0d\uff0c\u5b83\u6703\u52d5\uff0c\u4f46\u5f88\u50b7\u773c\u2026<\/p>\n<ol class=\"arabic simple\">\n<li>\u5c64\u5c64\u5957\u758a\u7684\u91d1\u5b57\u5854\u72c0\u7a0b\u5f0f\u78bc\uff0c\u5f88\u96e3\u4e00\u773c\u770b\u51fa\u5167\u90e8\u908f\u8f2f<\/li>\n<li>\u4e0d\u65b7\u91cd\u8986\u51fa\u73fe\u7684 <tt class=\"docutils literal\"><span class=\"pre\">if..else<\/span><\/tt> \u932f\u8aa4\u8655\u7406\u908f\u8f2f\u3002\u53e6\u5916\uff0c\u5982\u679c\u6211\u53ea\u5728\u4e4e\u6700\u5f8c\u7684\n<tt class=\"docutils literal\">comment<\/tt> \uff0c\u4e2d\u9593\u7684\u6bcf\u500b <tt class=\"docutils literal\">setGetCommentError<\/tt> \u90fd\u662f\u591a\u9918\u7684\u7a0b\u5f0f\u78bc\u3002<\/li>\n<\/ol>\n<img alt=\"Callback Hell meme\" src=\"https:\/\/johnliu55.tw\/async-await-why\/images\/callback_hell.jpg\" \/>\n<p>\u76f8\u4fe1\u5f88\u591a\u4eba\u90fd\u6df1\u53d7\u5176\u5bb3\uff0c\u8ff7\u5931\u5728\u9019\u500b\u5730\u7344\u2026<\/p>\n<\/div>\n<div class=\"section\" id=\"it-will-be-fun-i-promise\">\n<h2>It Will Be Fun, I Promise<\/h2>\n<p>\u4f60\u7684\u8072\u97f3\uff0c <a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/ECMAScript#6th_Edition_%E2%80%93_ECMAScript_2015\">ES6<\/a> \u807d\u5230\u4e86\uff0c\u65bc\u662f\u5c31\u51fa\u73fe\u4e86 <a class=\"reference external\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Promise\">Promise<\/a> \u9019\u500b\u7d50\u69cb\u4f86\u64fa\u812b\u56de\u547c\u5730\u7344\uff0c\u8b93\u4f60\u53ef\u4ee5\u7528 <a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Method_chaining\">Method chaining<\/a> \u7684\u65b9\u5f0f\u4f86\u4e32\u9023\u9019\u4e9b HTTP \u8acb\u6c42\uff0c\u800c\u4e0d\u662f\u4e00\u5c64\u5305\u4e00\u5c64\u7684\u6a21\u5f0f\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"kd\">function<\/span> <span class=\"nx\">doStuff<\/span><span class=\"p\">(<\/span><span class=\"nx\">user_id<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nx\">asyncPromiseGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/users\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">user_id<\/span><span class=\"p\">)<\/span>\n        <span class=\"p\">.<\/span><span class=\"nx\">then<\/span><span class=\"p\">(<\/span><span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">resp<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n            <span class=\"k\">return<\/span> <span class=\"nx\">asyncPromiseGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/posts\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">resp<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">posts<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">].<\/span><span class=\"nx\">id<\/span><span class=\"p\">);<\/span>\n        <span class=\"p\">})<\/span>\n        <span class=\"p\">.<\/span><span class=\"nx\">then<\/span><span class=\"p\">(<\/span><span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">resp<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n            <span class=\"k\">return<\/span> <span class=\"nx\">asyncPromiseGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/comments\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">resp<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">.<\/span><span class=\"nx\">comments<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">].<\/span><span class=\"nx\">id<\/span><span class=\"p\">);<\/span>\n        <span class=\"p\">})<\/span>\n        <span class=\"p\">.<\/span><span class=\"nx\">then<\/span><span class=\"p\">(<\/span><span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">resp<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n            <span class=\"kd\">let<\/span> <span class=\"nx\">comment<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">resp<\/span><span class=\"p\">.<\/span><span class=\"nx\">data<\/span><span class=\"p\">;<\/span>\n            <span class=\"nx\">doSomethingToComment<\/span><span class=\"p\">(<\/span><span class=\"nx\">comment<\/span><span class=\"p\">);<\/span>\n        <span class=\"p\">})<\/span>\n        <span class=\"p\">.<\/span><span class=\"k\">catch<\/span><span class=\"p\">(<\/span><span class=\"kd\">function<\/span> <span class=\"p\">(<\/span><span class=\"nx\">err<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n            <span class=\"nx\">setGetCommentError<\/span><span class=\"p\">(<\/span><span class=\"nx\">err<\/span><span class=\"p\">);<\/span>\n        <span class=\"p\">})<\/span>\n<span class=\"p\">}<\/span>\n<\/pre><\/div>\n<p><tt class=\"docutils literal\">asyncPromiseGet<\/tt> \u51fd\u5f0f\u6703\u56de\u50b3\u4e00\u500b Promise \u7269\u4ef6\uff0c\u4e0a\u9762\u6709\u4e00\u500b <tt class=\"docutils literal\">then()<\/tt> \u65b9\u6cd5\u63a5\u6536\u4e00\u500b\u56de\u547c\u51fd\u5f0f\u70ba\u5f15\u6578\uff0c\u4e26\u5728\u5f97\u5230 HTTP \u56de\u8986\u6642\u547c\u53eb\u9019\u500b\u51fd\u5f0f\u3002\u540c\u6642 <tt class=\"docutils literal\">then()<\/tt> \u65b9\u6cd5\u4e5f\u6703<strong>\u56de\u50b3\u4e00\u500b\u65b0\u7684\nPromise \u7269\u4ef6<\/strong>\uff0c\u8b93\u4f60\u53ef\u4ee5\u4e00\u76f4 <tt class=\"docutils literal\">.then()<\/tt> \u4e0b\u53bb\u3002<\/p>\n<div class=\"admonition note\">\n<p class=\"first admonition-title\">\u8a3b\u91cb<\/p>\n<p class=\"last\">\u5176\u5be6 <tt class=\"docutils literal\">then()<\/tt> \u65b9\u6cd5\u53ef\u4ee5\u63a5\u6536\u5169\u500b\u51fd\u5f0f\u5f15\u6578\uff0c\u9019\u908a\u70ba\u4e86\u7c21\u55ae\u8d77\u898b\u53ea\u8aaa\u660e\u50b3\u4e00\u500b\u7684\u60c5\u6cc1\uff0c\u7d30\u7bc0\u8acb\u770b <a class=\"reference external\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Guide\/Using_promises\">Using Promises<\/a>\u3002<\/p>\n<\/div>\n<p>Promise \u771f\u6b63\u53b2\u5bb3\u7684\u5730\u65b9\u5728\u65bc\uff0c\u5982\u679c\u4f60\u7d66 <tt class=\"docutils literal\">then()<\/tt> \u7684\u56de\u547c\u51fd\u5f0f\u4e5f\u56de\u50b3\u4e86 Promise \u7269\u4ef6\uff0c\u90a3\u9ebc\u9019\u500b Promise \u6700\u5f8c\u5f97\u5230\u7684\u503c\uff0c<strong>\u6703\u88ab\u9001\u5230<\/strong> <tt class=\"docutils literal\">then()<\/tt>\n<strong>\u6240\u56de\u50b3\u7684\u90a3\u500b\u65b0\u7684 Promise \u4e0a<\/strong>\uff0c\u8b93\u4e0b\u4e00\u500b\u4e32\u8d77\u4f86\u7684 <tt class=\"docutils literal\">then()<\/tt>\n\u80fd\u5920\u53d6\u5f97\u4f60\u7684\u56de\u547c\u51fd\u5f0f\u60f3\u8981\u5f97\u5230\u7684\u7d50\u679c\u3002\u5c31\u662f\u56e0\u70ba\u9019\u500b\u539f\u56e0\uff0c\u6211\u5011\u624d\u80fd\u5920\u4f7f\u7528 Method chaining \u800c\u4e0d\u662f\u5de2\u72c0\u7684\u65b9\u5f0f\u4f86\u4e32\u9023\u9019\u4e9b HTTP\n\u8acb\u6c42\u3002<\/p>\n<img alt=\"It will be fun, I promise!\" src=\"https:\/\/johnliu55.tw\/async-await-why\/images\/it-will-be-fun-i-promise.jpg\" \/>\n<p>Promise \u8b93\u91d1\u5b57\u5854\u6d88\u5931\u4e86\uff0c\u800c\u4e14\u4e5f\u7c21\u5316\u4e86\u932f\u8aa4\u8655\u7406\u7684\u6a5f\u5236\uff0c\u53ef\u4ee5\u770b\u5230\u6211\u53ea\u8981\u6700\u5f8c\u52a0\u500b\n<tt class=\"docutils literal\">.catch()<\/tt> \u5c31\u80fd\u5920\u7d71\u4e00\u8655\u7406\u932f\u8aa4\u3002\u7136\u800c\uff0c\u9019\u9084\u662f\u9003\u4e0d\u4e86\u56de\u547c\u7684\u6982\u5ff5\uff0c\u8207\u540c\u6b65\u7248\u672c\u76f8\u8f03\u4e4b\u4e0b\u9084\u662f\u6c92\u90a3\u9ebc\u512a\u96c5\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"id1\">\n<h2>\u5abd\uff0c\u53ef\u4ee5\u4e0d\u8981\u56de\u547c\u55ce\uff1f<\/h2>\n<p>\u5c31\u5728\u6211\u4ee5\u70ba\u9019\u8f29\u5b50\u5c31\u9019\u6a23\u5b50\u4e86\u7684\u6642\u5019\uff0c <a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/ECMAScript#8th_Edition_%E2%80%93_ECMAScript_2017\">ES8<\/a> \u51fa\u73fe\u4e86 async \u8207 await\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"k\">async<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">doStuff<\/span><span class=\"p\">(<\/span><span class=\"nx\">user_id<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n    <span class=\"k\">try<\/span> <span class=\"p\">{<\/span>\n        <span class=\"kd\">let<\/span> <span class=\"nx\">user<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">asyncHttpGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/users\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">user_id<\/span><span class=\"p\">);<\/span>\n        <span class=\"kd\">let<\/span> <span class=\"nx\">post<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">asyncHttpGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/posts\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">user<\/span><span class=\"p\">.<\/span><span class=\"nx\">posts<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">].<\/span><span class=\"nx\">id<\/span><span class=\"p\">);<\/span>\n        <span class=\"kd\">let<\/span> <span class=\"nx\">comment<\/span> <span class=\"o\">=<\/span> <span class=\"k\">await<\/span> <span class=\"nx\">asyncHttpGet<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/api\/comments\/&#39;<\/span> <span class=\"o\">+<\/span> <span class=\"nx\">post<\/span><span class=\"p\">.<\/span><span class=\"nx\">comments<\/span><span class=\"p\">[<\/span><span class=\"mf\">0<\/span><span class=\"p\">].<\/span><span class=\"nx\">id<\/span><span class=\"p\">);<\/span>\n        <span class=\"nx\">doSomethingToComment<\/span><span class=\"p\">(<\/span><span class=\"nx\">comment<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span> <span class=\"k\">catch<\/span> <span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">)<\/span> <span class=\"p\">{<\/span>\n        <span class=\"nx\">setGetCommentError<\/span><span class=\"p\">(<\/span><span class=\"nx\">e<\/span><span class=\"p\">);<\/span>\n    <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n<\/pre><\/div>\n<p>\u53ef\u4ee5\u770b\u5230\uff0c\u9664\u4e86\u51fa\u73fe <tt class=\"docutils literal\">await<\/tt> \u8ddf <tt class=\"docutils literal\">async<\/tt> \u9019\u5e7e\u500b\u5b57\u4ee5\u5916\uff0c\u57fa\u672c\u4e0a\u7a0b\u5f0f\u78bc\u7684\u67b6\u69cb\u8207\u540c\u6b65\u7248\u7684\u4e00\u6a21\u6a21\u4e00\u6a23\u6a23\uff0c\u4e5f\u53ef\u4ee5\u7528\u8a9e\u8a00\u539f\u751f\u7684 <tt class=\"docutils literal\">try catch<\/tt> \u6a5f\u5236\u4f86\u9032\u884c\u932f\u8aa4\u8655\u7406\uff0c\u8d85\u8b9a\u7684\u5566\uff01<\/p>\n<img alt=\"It's breathtacking!\" src=\"https:\/\/johnliu55.tw\/async-await-why\/images\/breathtaking.gif\" \/>\n<p>\u503c\u5f97\u6ce8\u610f\u7684\u662f\uff0casync\/await \u770b\u8d77\u4f86\u662f\u5168\u65b0\u7684\u6982\u5ff5\uff0c \u4f46\u5176\u5be6\u9019\u5169\u500b\u8a9e\u6cd5\u662f<a class=\"reference external\" href=\"https:\/\/stackoverflow.com\/questions\/46908575\/async-await-native-implementations\">\u7531 Promise \u548c\u751f\u6210\u5668\uff08Generator\uff09<\/a>\n\u4f86\u5be6\u4f5c\u7684\u3002\u4f60\u53ef\u4ee5\u8a66\u8457\u5728 Node.js \u88e1\u5b9a\u7fa9\u4e00\u500b async \u51fd\u5f0f\u4e26\u76f4\u63a5\u547c\u53eb\u5b83\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"o\">&gt;<\/span> <span class=\"k\">async<\/span> <span class=\"kd\">function<\/span> <span class=\"nx\">f<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span> <span class=\"k\">return<\/span> <span class=\"mf\">1<\/span><span class=\"p\">;<\/span> <span class=\"p\">}<\/span>\n<span class=\"kc\">undefined<\/span>\n<span class=\"o\">&gt;<\/span> <span class=\"nx\">f<\/span><span class=\"p\">()<\/span>\n<span class=\"nb\">Promise<\/span> <span class=\"p\">{<\/span> <span class=\"mf\">1<\/span> <span class=\"p\">}<\/span>\n<\/pre><\/div>\n<p>\u53ef\u4ee5\u770b\u5230 async \u51fd\u5f0f\u56de\u50b3\u7684\u5176\u5be6\u662f Promise\u3002\u5c0d\u80cc\u5f8c\u5be6\u4f5c\u6709\u8208\u8da3\u53ef\u4ee5\u53bb Google \u4e00\u4e0b\uff0c\u6216\u662f\u53bb\u7ffb\u4e00\u4e0b<a class=\"reference external\" href=\"https:\/\/www.tenlong.com.tw\/products\/9789864342525\">\u5fcd\u8005\uff1aJavaScript \u958b\u767c\u6280\u5de7\u63a2\u79d8 \u7b2c\u4e8c\u7248<\/a>\u9019\u672c\u66f8\u7684\u7b2c\u516d\u7ae0\uff0c\u88e1\u9762\u6709\u5f88\u8a73\u7d30\u7684\u89e3\u91cb\u3002<\/p>\n<p>\u9019\u7bc7\u5c31\u5beb\u5230\u9019\u2026\u4e0b\u4e00\u7bc7\u53ef\u80fd\u6703\u4f86\u804a\u804a Python \u7684 <tt class=\"docutils literal\">asyncio<\/tt> \u5427\u2026\uff1f<\/p>\n<\/div>\n<div class=\"section\" id=\"references\">\n<h2>References<\/h2>\n<ul class=\"simple\">\n<li><a class=\"reference external\" href=\"https:\/\/www.tenlong.com.tw\/products\/9789864342525\">\u5fcd\u8005\uff1aJavaScript \u958b\u767c\u6280\u5de7\u63a2\u79d8 \u7b2c\u4e8c\u7248<\/a><\/li>\n<li><a class=\"reference external\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Guide\/Using_promises\">Using Promises<\/a> - MDN<\/li>\n<li><a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Async\/await\">Async\/Await<\/a> - JAVASCRIPT.INFO<\/li>\n<\/ul>\n<\/div>\n","category":[{"@attributes":{"term":"Programming Language"}},{"@attributes":{"term":"Asynchronous Programming"}},{"@attributes":{"term":"JavaScript"}},{"@attributes":{"term":"Async\/await"}},{"@attributes":{"term":"\u9592\u804a"}}]},{"title":"SSH Tunneling (Port Forwarding) \u8a73\u89e3","link":{"@attributes":{"href":"https:\/\/johnliu55.tw\/ssh-tunnel.html","rel":"alternate"}},"published":"2020-05-21T00:00:00+08:00","updated":"2020-05-25T00:00:00+08:00","author":{"name":"John Liu"},"id":"tag:johnliu55.tw,2020-05-21:\/ssh-tunnel.html","summary":"<p class=\"first last\">\u8a73\u7d30\u89e3\u91cb\u4f7f\u7528SSH\u7684Local Port Forwarding\u3001Remote Port Forwarding\u3001\n\u548cDynamic Port Forwarding\u4f86\u5efa\u7acb\u52a0\u5bc6\u901a\u9053\uff08Tunneling\uff09\u7684\u65b9\u6cd5\u3002<\/p>\n","content":"<div class=\"admonition admonition-\">\n<p class=\"first admonition-title\">\u8b8a\u66f4\u8a18\u9304<\/p>\n<dl class=\"last docutils\">\n<dt>2020-05-25<\/dt>\n<dd>\u52a0\u5165\u300c<a class=\"reference internal\" href=\"#id10\">\u88dc\u5145\uff1a\u5c0f\u6280\u5de7\u548c\u5de5\u5177<\/a>\u300d\u7ae0\u7bc0\u4f86\u653e\u5404\u7a2e SSH Tunneling \u7684\u5c0f\u6280\u5de7\u548c\u5de5\u5177\u3002<\/dd>\n<\/dl>\n<\/div>\n<p>\u524d\u9663\u5b50\u7814\u7a76\u4e86\u4e00\u4e0b\u7528 SSH Tunneling \u4f86\u9023\u5230\u5167\u90e8\u7db2\u8def\u7684\u65b9\u6cd5\uff0c\u4e00\u958b\u59cb\u5be6\u5728\u6709\u9ede\u96e3\u7406\u89e3 SSH \u6307\u4ee4\u8207\u5be6\u969b\u60c5\u6cc1\u7684\u95dc\u4fc2\uff0c\u4f46\u4e86\u89e3\u5f8c\u624d\u767c\u73fe\u4ed6\u8d85\u7d1a\u5f37\u5927\uff0c\u65bc\u662f\u5c31\u628a\u7d30\u7bc0\u8a18\u9304\u4e0b\u4f86\u4e4b\u5f8c\u53ef\u4ee5\u53c3\u8003\uff0c\u4e5f\u5e0c\u671b\u80fd\u5e6b\u5927\u5bb6\u4e86\u89e3\u4e00\u4e0b\u9019\u6771\u897f\u3002<\/p>\n<div class=\"section\" id=\"id1\">\n<h2>\u4ec0\u9ebc\u662f SSH Tunneling (Port Forwarding)\uff1f<\/h2>\n<p>Tunneling \u6307\u7684\u662f\u5c07\u7db2\u8def\u4e0a\u7684 A\u3001B \u5169\u500b\u7aef\u9ede\u7528\u67d0\u7a2e\u65b9\u5f0f\u9023\u63a5\u8d77\u4f86\uff0c\u5f62\u6210\u4e00\u500b\u300c\u96a7\u9053\u300d\uff0c\u8b93\u5169\u7aef\u7684\u901a\u8a0a\u80fd\u5920\u7a7f\u900f\u67d0\u4e9b\u9650\u5236\uff08\u4f8b\u5982\u9632\u706b\u7246\uff09\uff0c\u6216\u662f\u80fd\u5c07\u901a\u8a0a\u5167\u5bb9\u52a0\u5bc6\u907f\u514d\u6d29\u6f0f\u3002\u800c SSH Tunneling \u5c31\u662f\u6307\u5229\u7528 SSH \u5354\u5b9a\u4f86\u5efa\u7acb\u9019\u500b\u96a7\u9053\uff0c\u6240\u4ee5\u4e0d\u4f46\u80fd\u52a0\u5bc6\u4f60\u7684\u901a\u8a0a\uff0c\u5982\u679c\u4e2d\u9593\u8a2d\u6709\u9632\u706b\u7246\u64cb\u6389\u67d0\u4e9b\u7279\u5b9a Port \u7684\u9023\u7dda\uff08\u4f8b\u5982 HTTP\/HTTPS \u7684 80\/443\uff09\u800c\u6c92\u6709\u64cb\u4e0b SSH \u7684 Port 22\uff0c\u9019\u500b\u96a7\u9053\u4fbf\u6703\u8b93\u9632\u706b\u7246\u8a8d\u70ba\u662f\u53ea\u662f\u4e00\u822c\u7684 SSH \u9023\u7dda\uff0c\u9032\u800c\u653e\u884c\uff0c\u4e5f\u5c31\u9054\u5230\u4e86\u300c\u7a7f\u900f\u9632\u706b\u7246\u300d\u7684\u6548\u679c\u3002<\/p>\n<p>\u53e6\u5916\uff0c\u56e0\u70ba SSH Tunneling \u7684\u76ee\u6a19\u662f\u5169\u500b\u7aef\u9ede\u4e0a\u7684 Port\uff0c\u800c\u904e\u7a0b\u5c31\u50cf\u662f\u628a\u5c0d A \u9ede\u4e0a\u7684\u67d0\u500b Port X \u6240\u50b3\u9001\u7684\u8cc7\u6599<strong>\u8f49\u9001<\/strong>\n\uff08Forward\uff09\u81f3 B \u9ede\u4e0a\u7684 Port Y\uff0c\u6240\u4ee5 SSH Tunneling \u53c8\u7a31\u70ba <strong>SSH Port Forwarding<\/strong> \u3002<\/p>\n<img alt=\"Tunneling \u793a\u610f\u5716 \" src=\"https:\/\/johnliu55.tw\/ssh-tunnel\/images\/tunneling.png\" \/>\n<p>SSH Port Forwarding \u6709\u4e0b\u5217\u4e09\u7a2e\u6a21\u5f0f\uff1a<\/p>\n<ul class=\"simple\">\n<li>Local Port Forwarding<\/li>\n<li>Remote Port Forwarding<\/li>\n<li>Dynamic Port Forwarding<\/li>\n<\/ul>\n<p>\u63a5\u4e0b\u4f86\u6703\u4e00\u4e00\u8aaa\u660e\u5404\u7a2e\u6a21\u5f0f\u3002\u5148\u4f86\u770b\u770b\u5728 SSH Port Forwarding \u7576\u4e2d\u53c3\u8207\u7684\u89d2\u8272\u6709\u54ea\u4e9b\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"port-fowarding\">\n<h2>Port Fowarding \u88e1\u7684\u89d2\u8272\u5b9a\u7fa9<\/h2>\n<p>\u5c0d Local \u548c Remote Port Forwarding \u4f86\u8aaa\uff0c\u90fd\u6703\u6709\u4e0b\u9762\u9019\u4e09\u500b\u89d2\u8272\uff1a<\/p>\n<dl class=\"docutils\">\n<dt>Client<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u4efb\u4f55\u4f60\u53ef\u4ee5\u6572 <tt class=\"docutils literal\">ssh<\/tt> \u6307\u4ee4\u4f86\u555f\u52d5 Port Forwarding \u7684\u6a5f\u5668<\/li>\n<\/ul>\n<\/dd>\n<dt>SSH Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u53ef\u4ee5\u88ab <strong>Client<\/strong> \u7528 SSH \u9023\u9032\u53bb\u7684\u6a5f\u5668<\/li>\n<\/ul>\n<\/dd>\n<dt>Target Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u67d0\u4e00\u53f0\u4f60\u60f3\u5efa\u7acb\u9023\u7dda\u7684\u6a5f\u5668\uff0c\u901a\u5e38\u662f\u70ba\u4e86\u5c0d\u5916\u958b\u653e\u9019\u53f0\u6a5f\u5668\u4e0a\u7684\u670d\u52d9<\/li>\n<li><strong>\u6ce8\u610f<\/strong>\uff0c <strong>Client<\/strong> \u8207 <strong>SSH Server<\/strong> \u672c\u8eab\u90fd\u53ef\u4ee5\u662f <strong>Target Server<\/strong> \uff0c\u4e0d\u662f\u771f\u7684\u8981\u6709\u4e09\u53f0\u6a5f\u5668\u624d\u53ef\u4ee5\u9032\u884c Port Forwarding\uff01<\/li>\n<\/ul>\n<\/dd>\n<\/dl>\n<p>\u800c Dynamic Port Forwarding \u6bd4\u8f03\u4e0d\u4e00\u6a23\uff0c\u5728\u65bc Target Server \u4e0d\u6703\u53ea\u6709\u4e00\u53f0\uff0c\u800c\u662f\u53ef\u4ee5\u88ab\u52d5\u614b\u6c7a\u5b9a\u7684\u3002<\/p>\n<p>\u4e86\u89e3\u4e86\u9019\u4e09\u500b\u89d2\u8272\uff0c\u90a3\u5c31\u5148\u4f86\u770b\u770b Local Port Forwarding \u662f\u600e\u9ebc\u56de\u4e8b\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"local-port-forwarding\">\n<h2>Local Port Forwarding<\/h2>\n<div class=\"section\" id=\"id2\">\n<h3>\u6307\u4ee4\u8a9e\u6cd5<\/h3>\n<div class=\"highlight\"><pre><span><\/span>ssh -L [bind_address:]&lt;port&gt;:&lt;host&gt;:&lt;host_port&gt; &lt;SSH Server&gt;\n<\/pre><\/div>\n<p>\u5728 <strong>Client<\/strong> \u4e0a\u958b\u555f <tt class=\"docutils literal\">bind_address:port<\/tt> \u7b49\u5f85\u9023\u7dda\uff0c\u7576\u6709\u4eba\u9023\u4e0a\u6642\uff0c\u5c07\u6240\u6709\u8cc7\u6599\u8f49\u9001\u5230 <tt class=\"docutils literal\">host:host_port<\/tt> \u53bb\u3002\n<strong>\u6ce8\u610f<\/strong>\uff0c <tt class=\"docutils literal\">host<\/tt> \u662f\u76f8\u5c0d\u65bc <strong>SSH Server<\/strong> \u7684\u4f4d\u5740\uff0c\u800c\u4e0d\u662f <strong>Client<\/strong> \uff01<\/p>\n<\/div>\n<div class=\"section\" id=\"id3\">\n<h3>\u4f7f\u7528\u60c5\u5883\u4e00\uff1a\u9023\u5230\u4f4d\u5728\u9632\u706b\u7246\u5f8c\u7684\u958b\u767c\u4f3a\u670d\u5668\u4e0a\u7684\u670d\u52d9<\/h3>\n<p>\u4f60\u6709\u4e00\u53f0\u4f4d\u65bc\u9632\u706b\u7246\u5f8c\u7684\u958b\u767c\u4f3a\u670d\u5668\uff0c \u4f60\u5728\u4e0a\u9762\u67b6\u4e86\u67d0\u500b\u670d\u52d9\u5728 Port 8080 \u4e0a\uff0c\u4f46\u9632\u706b\u7246\u53ea\u958b\u653e Port 22 \u7684 SSH \u9023\u7dda\uff0c\u8b93\u4f60\u7121\u6cd5\u5f9e\u4f60\u7684\u96fb\u8166\u76f4\u63a5\u9023\u5230 Port 8080\uff0c\u4f46\u4f60\u53c8\u5f88\u60f3\u9023\u5230\u4ed6\u2026<\/p>\n<img alt=\" \u60c5\u5883 1 \u793a\u610f\u5716 \" src=\"https:\/\/johnliu55.tw\/ssh-tunnel\/images\/local_scenario1_problem.png\" \/>\n<p>\u9019\u6642\u5019\u53ea\u8981\u4f60\u80fd\u5920 SSH \u5230\u90a3\u53f0\u4f3a\u670d\u5668\uff0c\u5c31\u53ef\u4ee5\u5229\u7528 Local Port Forwarding \u4f86\u958b\u555f\u4f60\u96fb\u8166\u4e0a\u7684\u67d0\u500b Port\uff08\u5047\u8a2d\u70ba 9090\uff09\uff0c\u5c07\u5c0d\u5b83\u767c\u9001\u7684\u8cc7\u6599\u8f49\u9001\u5230\u4f3a\u670d\u5668\u7684 Port 8080\u3002\u9019\u6a23\u4e00\u4f86\uff0c \u9023\u4e0a\u4f60\u7684\u96fb\u8166\u7684 Port 9090 \u5c31\u7b49\u65bc\u9023\u4e0a\u4e86\u9632\u706b\u7246\u5f8c\u7684\u4f3a\u670d\u5668\u7684 Port 8080 \uff0c\u4e5f\u5c31\u7e5e\u904e\u4e86\u9632\u706b\u7246\u7684\u9650\u5236\u3002<\/p>\n<img alt=\" \u60c5\u5883 1 \u89e3\u6cd5\u793a\u610f\u5716 \" src=\"https:\/\/johnliu55.tw\/ssh-tunnel\/images\/local_scenario1_solved.png\" \/>\n<dl class=\"docutils\">\n<dt>Client<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u4f60\u7684\u96fb\u8166<\/li>\n<\/ul>\n<\/dd>\n<dt>SSH Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u9632\u706b\u7246\u5f8c\u7684\u4f3a\u670d\u5668<\/li>\n<li>SSH Destination\uff1a <tt class=\"docutils literal\"><span class=\"pre\">johnliu&#64;my-server<\/span><\/tt><\/li>\n<\/ul>\n<\/dd>\n<dt>Target Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u9632\u706b\u7246\u5f8c\u7684\u4f3a\u670d\u5668<\/li>\n<\/ul>\n<\/dd>\n<\/dl>\n<p>SSH \u6307\u4ee4\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>ssh -L 9090:localhost:8080 johnliu@my-server\n<\/pre><\/div>\n<p>\u9019\u908a\u7684 <tt class=\"docutils literal\">localhost<\/tt> \u662f\u76f8\u5c0d\u65bc <tt class=\"docutils literal\"><span class=\"pre\">johnliu&#64;my-server<\/span><\/tt> \uff0c\u6307\u7684\u5c31\u662f\u9632\u706b\u7246\u5f8c\u7684\u4f3a\u670d\u5668\u672c\u8eab\u3002<\/p>\n<div class=\"admonition note\">\n<p class=\"first admonition-title\">\u8a3b\u91cb<\/p>\n<ul class=\"last\">\n<li><p class=\"first\">\u4f60\u5b8c\u5168\u53ef\u4ee5\u5728\u4f60\u7684\u96fb\u8166\u4e0a\u7528\u76f8\u540c\u7684 Port number \u4f86\u505a Port Forwarding\uff0c\u9019\u908a\u7528 <tt class=\"docutils literal\">9090<\/tt> \u53ea\u662f\u70ba\u4e86\u907f\u514d\u6df7\u6dc6\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>ssh -L 8080:localhost:8080 johnliu@my-server\n<\/pre><\/div>\n<\/li>\n<li><p class=\"first\">\u5982\u679c\u4f60\u6c92\u6709\u7d66 <tt class=\"docutils literal\">bind_address<\/tt> \uff0c\u9810\u8a2d\u6703 Bind \u5728 <tt class=\"docutils literal\">localhost<\/tt> \u4e0a\u3002\u5982\u679c\u4f60\u60f3\u628a Port <tt class=\"docutils literal\">9090<\/tt> \u958b\u653e\u7d66\u6240\u6709\u4eba\u7528\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>ssh -L 0.0.0.0:9090:localhost:8080 johnliu@my-server\n<\/pre><\/div>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"id4\">\n<h3>\u4f7f\u7528\u60c5\u5883\u4e8c\uff1a\u900f\u904e\u9632\u706b\u7246\u5f8c\u7684\u6a5f\u5668\uff0c\u9023\u5230\u9632\u706b\u7246\u5f8c\u7684\u7279\u5b9a\u670d\u52d9<\/h3>\n<p>\u60c5\u5883\u4e00\u6709\u7528\u7684\u524d\u63d0\u662f<strong>\u4f60\u80fd\u5920 SSH \u5230\u63d0\u4f9b\u670d\u52d9\u7684\u4f3a\u670d\u5668\u88e1<\/strong>\uff0c\u4f46\u4eca\u5929\u5982\u679c\u4f60\u6c92\u6709\u6b0a\u9650\uff0c\u7121\u6cd5 SSH \u9032\u5230\u63d0\u4f9b\u670d\u52d9\u7684\u4f3a\u670d\u5668\uff0c\u90a3\u8a72\u600e\u9ebc\u8fa6\u5462\uff1f<\/p>\n<img alt=\" \u60c5\u5883 1 \u793a\u610f\u5716 \" src=\"https:\/\/johnliu55.tw\/ssh-tunnel\/images\/local_scenario2_problem.png\" \/>\n<p>\u6c92\u554f\u984c\uff01\u53ea\u8981\u4f60\u5728\u9632\u706b\u7246\u5f8c\u6709\u4efb\u4f55\u4e00\u53f0\u4f60\u53ef\u4ee5 SSH \u7684\u6a5f\u5668\uff0c\u63a5\u8457\u4fee\u6539\u4e00\u4e0b\u6307\u4ee4\u88e1\u7684 <tt class=\"docutils literal\">host<\/tt> \u8a2d\u5b9a\uff0c\u4f60\u5c31\u53ef\u4ee5\u5229\u7528\u9019\u53f0\u6a5f\u5668\u9032\u884c\u8cc7\u6599\u8f49\u9001\uff1a<\/p>\n<img alt=\" \u60c5\u5883 1 \u89e3\u6cd5\u793a\u610f\u5716 \" src=\"https:\/\/johnliu55.tw\/ssh-tunnel\/images\/local_scenario2_solved.png\" \/>\n<dl class=\"docutils\">\n<dt>Client<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u4f60\u7684\u96fb\u8166<\/li>\n<\/ul>\n<\/dd>\n<dt>SSH Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u9632\u706b\u7246\u5f8c\u4f60\u7684\u6a5f\u5668<\/li>\n<li>SSH Destination\uff1a <tt class=\"docutils literal\"><span class=\"pre\">johnliu&#64;my-server<\/span><\/tt><\/li>\n<\/ul>\n<\/dd>\n<dt>Target Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u9632\u706b\u7246\u5f8c\u7684\u4f3a\u670d\u5668<\/li>\n<li><tt class=\"docutils literal\">192.168.1.101:8080<\/tt><\/li>\n<\/ul>\n<\/dd>\n<\/dl>\n<p>SSH \u6307\u4ee4\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>ssh -L 9090:192.168.1.101:8080 johnliu@my-server\n<\/pre><\/div>\n<p>\u9019\u908a\u7684 <tt class=\"docutils literal\">192.168.1.101<\/tt> \u662f\u76f8\u5c0d\u65bc <tt class=\"docutils literal\"><span class=\"pre\">johnliu&#64;my-server<\/span><\/tt> \uff0c\u6240\u4ee5\u662f\u9632\u706b\u7246\u5f8c\u7684\u4f3a\u670d\u5668\u7684 IP \u4f4d\u5740\u3002<\/p>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"remote-port-forwarding\">\n<h2>Remote Port Forwarding<\/h2>\n<div class=\"section\" id=\"id5\">\n<h3>\u6307\u4ee4\u8a9e\u6cd5<\/h3>\n<div class=\"highlight\"><pre><span><\/span>ssh -R [bind_address:]&lt;port&gt;:&lt;host&gt;:&lt;host_port&gt; &lt;SSH Server&gt;\n<\/pre><\/div>\n<p>\u5728 <strong>SSH Server<\/strong> \u4e0a\u958b\u555f <tt class=\"docutils literal\">bind_address:port<\/tt> \u7b49\u5f85\u9023\u7dda\uff0c\u7576\u6709\u4eba\u9023\u4e0a\u6642\uff0c\u5c07\u6240\u6709\u8cc7\u6599\u8f49\u9001\u5230 <tt class=\"docutils literal\">host:host_port<\/tt> \u53bb\u3002\n<strong>\u6ce8\u610f<\/strong>\uff0c <tt class=\"docutils literal\">host<\/tt> \u662f\u76f8\u5c0d\u65bc <strong>Client<\/strong> \u7684\u4f4d\u5740\uff0c\u800c\u4e0d\u662f <strong>SSH Server<\/strong> \uff01<\/p>\n<\/div>\n<div class=\"section\" id=\"id6\">\n<h3>\u4f7f\u7528\u60c5\u5883\u4e00\uff1a\u900f\u904e\u5c0d\u5916\u6a5f\u5668\uff0c\u8b93\u5176\u4ed6\u4eba\u80fd\u5920\u9023\u5230\u4f60\u7684\u96fb\u8166\u4e0a\u7684\u670d\u52d9<\/h3>\n<p>\u4f60\u5728\u4f60\u7684\u96fb\u8166\u4e0a\u958b\u767c\u5b8c\u4e86\u4e00\u500b\u670d\u52d9\u67b6\u5728 Port 8080 \u4e0a\uff0c\u7136\u5f8c\u4f60\u60f3\u8981 Demo \u7d66\u5ba2\u6236\u770b\uff0c\u4f46\u4f60\u7684\u96fb\u8166\u53ea\u6709\u5167\u90e8 IP\uff0c\u6240\u4ee5\u7121\u6cd5\u8b93\u5ba2\u6236\u9023\u9032\u4f86\uff1a<\/p>\n<img alt=\"Remote \u60c5\u5883 1 \u793a\u610f\u5716 \" src=\"https:\/\/johnliu55.tw\/ssh-tunnel\/images\/remote_scenario1_problem.png\" \/>\n<p>\u9019\u6642\u5019\u53ea\u8981\u5229\u7528 SSH Remote Forwarding\uff0c\u5c31\u53ef\u4ee5\u85c9\u7531\u4e00\u53f0\u6709 Internet IP \u7684\u5c0d\u5916\u6a5f\u5668\uff0c\u958b\u555f\u4e0a\u9762\u7684\u67d0\u500b Port\uff08\u5047\u8a2d\u70ba 9090\uff09\u4f86\u8f49\u9001\u8cc7\u6599\u5230\u4f60\u7684\u96fb\u8166\u4e0a\u7684 Port 8080\u3002\u9019\u6a23\u5b50\uff0c\u5ba2\u6236\u53ea\u8981\u9023\u4e0a\u5c0d\u5916\u6a5f\u5668\u7684 Port 9090 \u5c31\u7b49\u65bc\u662f\u9023\u4e0a\u4e86\u4f60\u96fb\u8166\u7684 Port 8080\u3002<\/p>\n<img alt=\"Remote \u60c5\u5883 1 \u89e3\u6cd5\u793a\u610f\u5716 \" src=\"https:\/\/johnliu55.tw\/ssh-tunnel\/images\/remote_scenario1_solved.png\" \/>\n<dl class=\"docutils\">\n<dt>Client<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u4f60\u7684\u96fb\u8166<\/li>\n<\/ul>\n<\/dd>\n<dt>SSH Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u5c0d\u5916\u6a5f\u5668<\/li>\n<li>SSH Destination\uff1a <tt class=\"docutils literal\"><span class=\"pre\">johnliu&#64;external-server<\/span><\/tt><\/li>\n<\/ul>\n<\/dd>\n<dt>Target Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u4f60\u7684\u96fb\u8166<\/li>\n<\/ul>\n<\/dd>\n<\/dl>\n<p>SSH \u6307\u4ee4\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>ssh -R 0.0.0.0:9090:localhost:8080 johnliu@external-server\n<\/pre><\/div>\n<p>\u9019\u908a\u7684 <tt class=\"docutils literal\">localhost<\/tt> \u662f\u76f8\u5c0d\u65bc <strong>Client<\/strong>  \uff0c\u6307\u7684\u5c31\u662f\u4f60\u7684\u96fb\u8166\u672c\u8eab\u3002<\/p>\n<div class=\"admonition warning\">\n<p class=\"first admonition-title\">\u8b66\u544a<\/p>\n<p>\u57fa\u65bc\u5b89\u5168\u8003\u91cf\uff0c\n<strong>Remote Forwarding \u9810\u8a2d\u90fd\u53ea\u80fd\u5920 bind \u5728 SSH Server \u7684 localhost \u4e0a<\/strong>\uff0c\u6240\u4ee5\u55ae\u9760\u4ee5\u4e0a\u6307\u4ee4\u662f\u7121\u6cd5\u8b93 Port 9090 \u958b\u653e\u7d66\u5916\u90e8\u9023\u7dda\u7684\u3002\u4f60\u5fc5\u9808\u8abf\u6574 SSH Server \u4e0a\u7684 SSH \u670d\u52d9\u7684\u8a2d\u5b9a\u6a94\uff08\u4e00\u822c\u5728 <tt class=\"docutils literal\">\/etc\/ssh\/sshd_config<\/tt> \uff09\u52a0\u5165 <tt class=\"docutils literal\">GatewayPorts<\/tt> \u8a2d\u5b9a\uff0c\u624d\u80fd\u8b93\u6240\u6709\u4eba\u90fd\u9023\u5230\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>GatewayPorts yes\n<\/pre><\/div>\n<p class=\"last\">\u9019\u908a\u6709\u4e09\u500b\u9078\u9805\uff1a\u9810\u8a2d\u70ba <tt class=\"docutils literal\">no<\/tt> \uff0c\u4e5f\u5c31\u662f\u552f\u4e00\u6307\u5b9a localhost\uff1b\u8a2d\u5b9a\u70ba <tt class=\"docutils literal\">yes<\/tt> \u53ef\u4ee5\u552f\u4e00\u6307\u5b9a\u70ba wildcard\uff08 <tt class=\"docutils literal\">0.0.0.0<\/tt> \uff09\uff1b\u8a2d\u5b9a\u70ba <tt class=\"docutils literal\">clientspecified<\/tt> \u53ef\u4ee5\u8b93\u555f\u52d5 Remote Forwarding \u7684 Client \u81ea\u884c\u6307\u5b9a\u3002<\/p>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"id7\">\n<h3>\u4f7f\u7528\u60c5\u5883\u4e8c\uff1a\u900f\u904e\u5c0d\u5916\u6a5f\u5668\uff0c\u5f9e\u5916\u9762\u9023\u56de\u5167\u90e8\u7db2\u8def\u4e0a\u7684\u670d\u52d9<\/h3>\n<p>\u6709\u4e00\u500b\u5728\u5167\u7db2\u88e1\u7684\u5167\u90e8\u670d\u52d9\uff0c\u4f60\u7684\u96fb\u8166\u53ef\u4ee5\u7528 IP <tt class=\"docutils literal\">192.168.1.100<\/tt>\n\u548c Port 8080 \u9023\u5230\u9019\u500b\u670d\u52d9\uff0c\u4f46\u56e0\u70ba\u90fd\u5728\u5167\u7db2\u6240\u4ee5\u5927\u5bb6\u90fd\u6c92\u6709 Internet IP\uff0c\u6240\u4ee5\u7121\u6cd5\u8b93\u4f60\u5f9e\u5bb6\u88e1\u900f\u904e Internet \u9023\u56de\u4f86\uff1a<\/p>\n<img alt=\"Remote \u60c5\u5883 2 \u793a\u610f\u5716 \" src=\"https:\/\/johnliu55.tw\/ssh-tunnel\/images\/remote_scenario2_problem.png\" \/>\n<p>\u9019\u6642\u5019\u85c9\u7531 Remote Forwarding \u548c\u4e00\u53f0\u5c0d\u5916\u6a5f\u5668\uff0c \u53ef\u4ee5\u8b93\u4f60\u5f9e\u4efb\u4f55\u5730\u65b9\u9023\u56de\u9019\u500b\u670d\u52d9\uff1a<\/p>\n<img alt=\"Remote \u60c5\u5883 2 \u89e3\u6cd5\u793a\u610f\u5716 \" src=\"https:\/\/johnliu55.tw\/ssh-tunnel\/images\/remote_scenario2_solved.png\" \/>\n<dl class=\"docutils\">\n<dt>Client<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u4f60\u7684\u96fb\u8166<\/li>\n<\/ul>\n<\/dd>\n<dt>SSH Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u5c0d\u5916\u6a5f\u5668<\/li>\n<li>SSH Destination\uff1a <tt class=\"docutils literal\"><span class=\"pre\">johnliu&#64;external-server<\/span><\/tt><\/li>\n<\/ul>\n<\/dd>\n<dt>Target Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u5167\u90e8\u670d\u52d9<\/li>\n<li><tt class=\"docutils literal\">192.168.1.100:8080<\/tt><\/li>\n<\/ul>\n<\/dd>\n<\/dl>\n<p>SSH \u6307\u4ee4\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>ssh -R 0.0.0.0:9090:192.168.1.100:8080 johnliu@external-server\n<\/pre><\/div>\n<p>\u5728\u9019\u88e1\uff0c <tt class=\"docutils literal\">192.168.1.100<\/tt> \u662f\u76f8\u5c0d\u65bc\u4f60\u7684\u96fb\u8166\uff0c\u6240\u4ee5\u5c31\u7b97\u5916\u90e8\u6a5f\u5668\u9023\u4e0d\u5230\u9019\u500b\u4f4d\u5740\u4e5f\u6c92\u95dc\u4fc2\uff0c\u56e0\u70ba\u662f\u900f\u904e\u4f60\u7684\u96fb\u8166\u505a\u8cc7\u6599\u8f49\u9001\u3002\u9019\u6a23\u5b50\uff0c\u53ea\u8981\u9023\u5230\u5c0d\u5916\u6a5f\u5668\u4e0a\u7684 Port 9090 \u5c31\u7b49\u65bc\u662f\u9023\u5230\u5167\u90e8\u670d\u52d9\u4e0a\u7684 Port 8080 \u4e86\uff0c\u4f60\u5c31\u80fd\u5920\u5f9e\u5916\u90e8\u5b58\u53d6\u5167\u7db2\u670d\u52d9\u3002<\/p>\n<p>\u9019\u61c9\u8a72\u662f SSH Port Forwarding \u6700\u5f37\u5927\u7684\u529f\u80fd\u4e86\uff01\u53ea\u8981\u5728\u7db2\u8def\u4e0a\u79df\u4e00\u53f0\u6700\u4fbf\u5b9c\u7684\u4e3b\u6a5f\uff08Linode, Digital Ocean \u4e4b\u985e\u7684\uff09\uff0c\u5c31\u53ef\u4ee5\u62ff\u4ed6\u4f86\u7576\u5716\u793a\u4e2d\u7684\u5c0d\u5916\u6a5f\u5668\uff0c\u4f86\u9023\u56de\u5167\u90e8\u7db2\u8def\u4e0a\u7684\u670d\u52d9\u3002\u4e0d\u904e\u524d\u63d0\u662f\u4f60\u5f97\u5728\u6709\u5167\u7db2\u9023\u7dda\u6642\u5c07 Port Forwarding \u8a2d\u5b9a\u597d\uff0c\u5982\u679c\u4f60\u5230\u5bb6\u5f8c\u624d\u60f3\u5230\uff0c\u90a3\u5c31\u8acb\u4f60\u518d\u8dd1\u4e00\u8d9f\u5427\u2026<\/p>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"dynamic-port-forwarding\">\n<h2>Dynamic Port Forwarding<\/h2>\n<div class=\"section\" id=\"id8\">\n<h3>\u6307\u4ee4\u8a9e\u6cd5<\/h3>\n<div class=\"highlight\"><pre><span><\/span>ssh -D [bind_address:]&lt;port&gt; &lt;SSH Server&gt;\n<\/pre><\/div>\n<p>\u5728 SSH Server \u4e0a\u555f\u52d5\u4e00\u500b <a class=\"reference external\" href=\"https:\/\/zh.wikipedia.org\/wiki\/SOCKS\">SOCKS<\/a> \u4ee3\u7406\u4f3a\u670d\u5668\uff0c\u540c\u6642\u5728 <strong>Client<\/strong> \u4e0a\u958b\u555f <tt class=\"docutils literal\">bind_address:port<\/tt> \u7b49\u5f85\u9023\u7dda\uff0c\u7576\u6709\u4eba\u9023\u4e0a\u6642\uff0c\u5c07\u6240\u6709\u8cc7\u6599\u8f49\u9001\u5230\u9019\u500b SOCKS \u4ee3\u7406\u4f3a\u670d\u5668\u4e0a\uff0c\u555f\u52d5\u76f8\u5c0d\u61c9\u7684\u9023\u7dda\u8acb\u6c42\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"httphttp-s\">\n<h3>\u4f7f\u7528\u60c5\u5883\uff1a\u5efa\u7acb\u4e00\u500b HTTP \u4ee3\u7406\u4f3a\u670d\u5668\u9023\u5230\u5167\u7db2\u7684\u6240\u6709 HTTP(S) \u670d\u52d9<\/h3>\n<p>\u53ea\u8981\u6709\u4e00\u53f0\u4f4d\u65bc\u5167\u7db2\u4e14<strong>\u5177\u6709\u5916\u90e8 IP<\/strong> \u7684\u6a5f\u5668\uff0c\u4f60\u5c31\u53ef\u4ee5\u5229\u7528\u9019\u500b\u65b9\u6cd5\u5efa\u7acb\u4e00\u500b HTTP \u4ee3\u7406\u4f3a\u670d\u5668\uff0c\u8b93\u4f60\u80fd\u5920\u5f9e\u5916\u9762\u9023\u56de\u5167\u7db2\u88e1\u7684\u6240\u6709 HTTP(S) \u670d\u52d9\uff1a<\/p>\n<img alt=\"Dynamic \u60c5\u5883\u793a\u610f\u5716 \" src=\"https:\/\/johnliu55.tw\/ssh-tunnel\/images\/dynamic.png\" \/>\n<dl class=\"docutils\">\n<dt>Client<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u4f60\u7684\u96fb\u8166<\/li>\n<\/ul>\n<\/dd>\n<dt>SSH Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>\u5167\u7db2\u88e1\u5177\u6709\u5916\u90e8 IP \u7684\u6a5f\u5668<\/li>\n<\/ul>\n<\/dd>\n<dt>Target Server<\/dt>\n<dd><ul class=\"first last simple\">\n<li>N\/A<\/li>\n<\/ul>\n<\/dd>\n<\/dl>\n<p>SSH \u6307\u4ee4\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>ssh -D 9090 johnliu@internal-machine\n<\/pre><\/div>\n<p>\u5047\u8a2d\u4f60\u662f\u7528 Linux \u548c Chrome\uff0c\u4f60\u53ef\u4ee5\u5728\u4f60\u7684\u96fb\u8166\u4e0a\u7528\u4ee5\u4e0b\u6307\u4ee4\u8b93 Chrome \u4f7f\u7528\u9019\u500b\u4ee3\u7406\u4f3a\u670d\u5668\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>google-chrome --user-data-dir=~\/proxied-chrome --proxy-server=socks5:\/\/localhost:9090\n<\/pre><\/div>\n<div class=\"admonition note\">\n<p class=\"first admonition-title\">\u8a3b\u91cb<\/p>\n<ul class=\"last simple\">\n<li>\u9019\u908a\u7684 <tt class=\"docutils literal\"><span class=\"pre\">google-chrome<\/span><\/tt> \u53ea\u662f\u7bc4\u4f8b\uff0c\u4e0d\u540c\u7684 Linux \u767c\u884c\u7248\u540d\u5b57\u53ef\u80fd\u6703\u4e0d\u540c<\/li>\n<li><tt class=\"docutils literal\"><span class=\"pre\">--user-data-dir<\/span><\/tt> \u662f\u70ba\u4e86\u8b93 Chrome \u80fd\u5920\u958b\u555f\u4e00\u500b\u65b0\u7684 Chrome session\uff0c\u4e0d\u52a0\u7684\u8a71 <tt class=\"docutils literal\"><span class=\"pre\">--proxy-server<\/span><\/tt> \u9019\u500b\u8a2d\u5b9a\u5c31\u6c92\u7528\u4e86<\/li>\n<\/ul>\n<\/div>\n<p>\u4e00\u822c\u7684 Port Forwarding \u53ea\u80fd\u5920\u8f49\u9001<strong>\u4e00\u500b IP \u4e0a\u7684\u4e00\u500b Port<\/strong> \uff0c\u7576\u4f60\u6709\u5f88\u591a IP \u6216\u5f88\u591a Port \u60f3\u8f49\u6642\u5c31\u53ea\u80fd\u4e00\u500b\u4e00\u500b\u958b\uff0c \u5f88\u4e0d\u65b9\u4fbf\u3002\u76f8\u6bd4\u4e4b\u4e0b\uff0cDynamic Port Forwarding \u80fd\u76f4\u63a5\u67b6\u8d77\u4e00\u500b\u4ee3\u7406\u4f3a\u670d\u5668\uff0c\u53ea\u8981\u4f60\u7528\u7684\u7a0b\u5f0f\u6709\u652f\u63f4 SOCKS \u5354\u5b9a\uff0c\u900f\u904e\u9019\u500b\u4ee3\u7406\u4f3a\u670d\u5668\u8b93\u4f60\u60f3\u600e\u9ebc\u8f49\u5c31\u600e\u9ebc\u8f49\u3002\u4e0d\u904e\u9019\u65b9\u5f0f\u4e5f\u4e0d\u662f\u6c92\u7f3a\u9ede\uff0c\u5c31\u662f\u90a3\u53f0\u8f49\u9001\u7528\u7684\u6a5f\u5668\u4e00\u5b9a\u5f97\u8981\u6709\u5c0d\u5916 IP\uff0c\u9019\u6a23\u624d\u80fd\u5920\u5f9e\u4f60\u7684\u96fb\u8166\u9023\u56de\u4f86\u3002<\/p>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"id9\">\n<h2>\u7d50\u8ad6<\/h2>\n<p>\u5f9e\u5716\u53ef\u4ee5\u770b\u51fa\u4f86\uff0cLocal \u8ddf Remote Forwarding \u7684\u5dee\u7570\u4e3b\u8981\u5728 <strong>Port \u958b\u555f\u7684\u5730\u65b9<\/strong>\uff1aLocal Forwarding \u662f\u5c07 Client \u4e0a\u7684 Port \u6253\u958b\u4ee5\u4f9b\u9023\u7dda\uff1bRemote Forwarding \u5247\u662f\u5c07 SSH Server \u4e0a\u7684 Port \u6253\u958b\u3002\u53e6\u5916\u8981\u6ce8\u610f\u7684\u9ede\u662f\u8f49\u9001\u7684\u76ee\u7684\u5730 <tt class=\"docutils literal\">host<\/tt> \uff1aLocal Forwarding \u662f\u76f8\u5c0d\u65bc SSH Server\uff0c\u800c Remote Forwarding \u5247\u662f\u76f8\u5c0d\u65bc Client\u3002<\/p>\n<p>\u96d6\u7136 Dynamic Port Forwarding \u7684\u5f48\u6027\u66f4\u5927\uff0c\u4f46\u689d\u4ef6\u5c31\u662f SSH Server \u5c31\u5fc5\u9808\u8981\u80fd\u5920\u5f9e\u5916\u9762\u9023\u56de\u4f86\u3002\u4e0d\u904e\u5176\u5be6\u4e5f\u662f\u6709 Workaround \u5566\uff0c\u642d\u914d\u4e00\u4e0b Port Forwarding \u5c31\u884c\u4e86\uff0c\u4f46\u9019\u6a23\u7684\u8a71\u4f60\u6709\u66f4\u597d\u7684 Proxy \u9078\u64c7\uff0c\u50cf\u662f <a class=\"reference external\" href=\"http:\/\/tinyproxy.github.io\/\">Tinyproxy<\/a> \u7b49\u7b49\u3002<\/p>\n<p>\u5c31\u5beb\u5230\u9019\u908a\uff0c\u6709\u554f\u984c\u4e5f\u6b61\u8fce\u5927\u5bb6\u8a0e\u8ad6\u5537\uff01<\/p>\n<\/div>\n<div class=\"section\" id=\"id10\">\n<h2>\u88dc\u5145\uff1a\u5c0f\u6280\u5de7\u548c\u5de5\u5177<\/h2>\n<p>\u9019\u908a\u653e\u4e00\u4e9b\u5927\u5bb6\u5728\u4f7f\u7528 SSH Tunneling \u4e0a\u7684\u5c0f\u6280\u5de7\u548c\u5de5\u5177\uff0c\u4f46\u7d30\u7bc0\u5c31\u8acb\u5927\u5bb6\u81ea\u884c Google \u56c9\u3002<\/p>\n<div class=\"section\" id=\"ssh\">\n<h3>\u5e38\u7528\u7684 SSH \u6307\u4ee4\u53c3\u6578<\/h3>\n<dl class=\"docutils\">\n<dt><tt class=\"docutils literal\"><span class=\"pre\">-N<\/span><\/tt><\/dt>\n<dd>\u4e0d\u8981\u57f7\u884c\u4efb\u4f55\u9060\u7aef\u6307\u4ee4\u3002\u6c92\u6709\u52a0\u9019\u500b\u53c3\u6578\u6642\uff0c\u5efa\u7acb Port Forwarding \u7684\u540c\u6642\u4e5f\u6703\u958b\u555f\nRemote Shell\uff0c\u8b93\u4f60\u53ef\u4ee5\u5c0d SSH Server \u4e0b\u6307\u4ee4\uff0c\u800c\u9019\u500b\u53c3\u6578\u53ef\u4ee5\u8b93 Remote Shell\n\u4e0d\u8981\u6253\u958b\u3002<\/dd>\n<dt><tt class=\"docutils literal\"><span class=\"pre\">-f<\/span><\/tt><\/dt>\n<dd>\u8b93 <tt class=\"docutils literal\">ssh<\/tt> \u6307\u4ee4\u5728\u80cc\u666f\u57f7\u884c\uff0c\u8b93\u4f60\u53ef\u4ee5\u7e7c\u7e8c\u7528 Shell \u505a\u4e8b\u60c5\u3002\u901a\u5e38\u6703\u642d\u4e0a\u9762\u7684\n<tt class=\"docutils literal\"><span class=\"pre\">-N<\/span><\/tt> \u4f7f\u7528\u3002<\/dd>\n<\/dl>\n<\/div>\n<div class=\"section\" id=\"ssh-client\">\n<h3>\u5e38\u7528\u7684 SSH Client \u7aef\u8a2d\u5b9a<\/h3>\n<div class=\"admonition note\">\n<p class=\"first admonition-title\">\u8a3b\u91cb<\/p>\n<p class=\"last\">\u8a2d\u5b9a\u6a94\u901a\u5e38\u5728 <tt class=\"docutils literal\"><span class=\"pre\">~\/.ssh\/config<\/span><\/tt> \u6216\u662f <tt class=\"docutils literal\">\/etc\/ssh\/ssh_config<\/tt>\u3002<\/p>\n<\/div>\n<dl class=\"docutils\">\n<dt><tt class=\"docutils literal\">ServerAliveInterval<\/tt><\/dt>\n<dd>\u8a2d\u5b9a\u4e00\u6bb5\u6642\u9593\uff0c\u5982\u679c Client \u5728\u9019\u6bb5\u6642\u9593\u5167\u90fd\u6c92\u5f9e SSH Server \u6536\u5230\u8cc7\u6599\uff0c\u5c31\u767c\u51fa\u4e00\u6bb5\u8a0a\u606f\u8acb SSH Server \u56de\u61c9\u3002\u9019\u6703\u8b93\u9023\u7dda\u4e0d\u6703\u5448\u73fe\u9592\u7f6e\u72c0\u614b\uff0c\u907f\u514d\u9632\u706b\u7246\u6216 Router \u5207\u65b7\u4f60\u7684\u9023\u7dda\u3002\u9810\u8a2d\u70ba <tt class=\"docutils literal\">0<\/tt> \uff0c\u4e0d\u6703\u767c\u51fa\u4efb\u4f55\u8a0a\u606f\u3002<\/dd>\n<dt><tt class=\"docutils literal\">ServerAliveCountMax<\/tt><\/dt>\n<dd>\u8a2d\u5b9a\u5728 SSH Server \u6c92\u56de\u61c9\u7684\u60c5\u6cc1\u4e0b\uff0cClient \u6700\u591a\u8981\u9001\u5e7e\u6b21\u8acb\u6c42\u56de\u61c9\u7684\u8a0a\u606f\uff08\u4e0a\u9762\u63d0\u5230\u7684\u90a3\u500b\uff09\u3002\u9054\u5230\u6b64\u6b21\u6578\u5f8c\uff0cClient \u5c31\u6703\u5207\u65b7\u8207 SSH Server \u4e4b\u9593\u7684\u9023\u7dda\u3002\u9019\u500b\u4e3b\u8981\u662f\u907f\u514d\u5728 SSH Server \u5df2\u7d93\u7121\u6cd5\u9023\u7dda\u5f8c\uff0cClient \u9084\u4e0d\u65b7\u9001\u51fa\u8acb\u6c42\u56de\u61c9\u7684\u60c5\u6cc1\u3002\u9810\u8a2d\u70ba <tt class=\"docutils literal\">3<\/tt> \u3002<\/dd>\n<\/dl>\n<\/div>\n<div class=\"section\" id=\"autossh-ssh\">\n<h3>autossh\uff1a\u81ea\u52d5\u91cd\u555f SSH \u9023\u7dda<\/h3>\n<p><a class=\"reference external\" href=\"https:\/\/linux.die.net\/man\/1\/autossh\">autossh<\/a>\n\u662f\u4e00\u652f\u53ef\u4ee5\u5e6b\u4f60\u76e3\u63a7 SSH \u9023\u7dda\u72c0\u614b\u4e26\u81ea\u52d5\u91cd\u9023\u7684\u7a0b\u5f0f\u3002\u5982\u679c\u4f60\u7684\u7db2\u8def\u72c0\u6cc1\u5f88\u7cdf\u7cd5\uff0c\u6216\u662f\u9632\u706b\u7246\u6703\u4e09\u4e0d\u4e94\u6642\u628a\u4f60\u65b7\u7dda\uff0c\u4ed6\u53ef\u4ee5\u5e6b\u4f60\u81ea\u52d5\u91cd\u555f\u9023\u7dda\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"fail2ban\">\n<h3>Fail2Ban\uff1a\u963b\u64cb\u4e0d\u660e\u9023\u7dda<\/h3>\n<p><a class=\"reference external\" href=\"https:\/\/www.fail2ban.org\/wiki\/index.php\/Main_Page\">Fail2Ban<\/a>\n\u53ef\u4ee5\u5e6b\u4f60\u963b\u64cb\u4e0d\u660e\u9023\u7dda\uff0c\u539f\u7406\u5c31\u662f\u53bb\u76e3\u770b SSH \u670d\u52d9\u7684 log \u4f86\u5075\u6e2c\u767b\u5165\u5931\u6557\u7684 IP\uff0c\u7136\u5f8c\u5728\u9019\u4e9b IP \u7684\u5931\u6557\u6b21\u6578\u9054\u5230\u4e00\u5b9a\u503c\u6642\uff0c\u5229\u7528\u9632\u706b\u7246\u4f86\u66ab\u6642\u505c\u6b62\u8a72 IP \u7684\u9023\u7dda\u8acb\u6c42\uff0c\u904e\u4e00\u5b9a\u6642\u9593\u5f8c\u518d\u6062\u5fa9\u3002\u53ef\u4ee5\u62ff\u4f86\u64cb\u6389\u6700\u57fa\u672c\u7684\u66b4\u529b\u653b\u64ca\u3002<\/p>\n<p>\u5982\u679c\u4f60\u79df\u4e86\u7dda\u4e0a\u4e3b\u6a5f\u4f86\u73a9\uff0c\u5efa\u8b70\u6700\u5c11\u8981\u88dd Fail2Ban \u4f86\u4fdd\u8b77\u4f60\u7684 SSH Server\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"port-knocking-ssh-port\">\n<h3>Port Knocking\uff1a\u6709\u689d\u4ef6\u7684\u958b\u555f SSH Port<\/h3>\n<p><a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Port_knocking\">Port Knocking<\/a>\n\u6307\u7684\u662f Client \u5fc5\u9808\u7528\u7279\u6b8a\u7684\u9806\u5e8f\u4f86\u5c0d SSH Server \u4e0a\u7684\u67d0\u4e9b Port \u767c\u51fa\u9023\u7dda\u8acb\u6c42\u5f8c\uff0cSSH Server \u624d\u6703\u958b\u653e Client \u9023\u7dda\u7684\u6280\u5de7\uff08\u6bd4\u5982\u4f9d\u5e8f\u5c0d Port 1000\u30012000\u30013000 \u767c\u51fa\u8acb\u6c42\uff0c\u624d\u6703\u5c0d\u4f60\u958b\u653e Port 22\uff09\u3002\u9019\u6a23\u7684\u597d\u8655\u662f\u5e73\u6642 Port 22 \u5c31\u6703\u662f\u95dc\u9589\u7684\u72c0\u614b\uff0c\u8b93\u653b\u64ca\u8005\u4ee5\u70ba SSH \u6c92\u6709\u958b\u653e\uff0c\u6e1b\u5c11\u88ab\u653b\u64ca\u7684\u6a5f\u6703\u3002\u6211\u6c92\u7528\u904e\uff0c\u4f46\u770b\u8d77\u4f86\u6703\u642d\u914d\u5176\u4ed6\u670d\u52d9\uff08\u50cf <a class=\"reference external\" href=\"https:\/\/linux.die.net\/man\/1\/knockd\">knockd<\/a> \uff09\u4e00\u8d77\u7528\u3002<\/p>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"references\">\n<h2>References<\/h2>\n<ul class=\"simple\">\n<li><tt class=\"docutils literal\">man ssh<\/tt><\/li>\n<li><a class=\"reference external\" href=\"https:\/\/zh.wikipedia.org\/wiki\/SOCKS\">SOCKS (Wiki)<\/a><\/li>\n<li><a class=\"reference external\" href=\"https:\/\/www.ssh.com\/ssh\/tunneling\/example\">SSH Port Forwarding Example<\/a><\/li>\n<li><a class=\"reference external\" href=\"https:\/\/www.booleanworld.com\/guide-ssh-port-forwarding-tunnelling\/\">A Guide to SSH Port Forwarding\/Tunnelling<\/a><\/li>\n<\/ul>\n<\/div>\n","category":[{"@attributes":{"term":"SSH, Linux"}},{"@attributes":{"term":"SSH"}},{"@attributes":{"term":"Linux"}},{"@attributes":{"term":"Tunneling"}},{"@attributes":{"term":"Port Forwarding"}}]},{"title":"\u7528 Pelican \u5beb\u4e2d\u6587\u6587\u7ae0","link":{"@attributes":{"href":"https:\/\/johnliu55.tw\/when-pelican-meets-cjk.html","rel":"alternate"}},"published":"2019-05-25T00:00:00+08:00","updated":"2019-06-08T00:00:00+08:00","author":{"name":"John Liu"},"id":"tag:johnliu55.tw,2019-05-25:\/when-pelican-meets-cjk.html","summary":"<p class=\"first last\">\u7528Pelican\u8207reStructuredText\u5beb\u4e2d\u6587\u6587\u7ae0\u9047\u5230\u4e86\u7a7a\u767d\u3001\u63db\u884c\u7b49\u7b49\u683c\u5f0f\u554f\u984c\uff0c\n\u6240\u4ee5\u81ea\u5df1\u5beb\u4e86\u4e00\u500bPelican Plugin\u4f86\u89e3\u6c7a\u9019\u4e9b\u554f\u984c\u3002<\/p>\n","content":"<div class=\"admonition admonition-\">\n<p class=\"first admonition-title\">\u8b8a\u66f4\u8a18\u9304<\/p>\n<dl class=\"last docutils\">\n<dt>2019-06-08<\/dt>\n<dd>\u4fee\u6b63\u300c\u89e3\u51b3 jekyll \u4e2d\u6587\u6362\u884c\u53d8\u6210\u7a7a\u683c\u7684\u95ee\u9898\u300d\u9019\u7bc7\u6587\u7ae0\u7684\u9023\u7d50\u3002<\/dd>\n<\/dl>\n<\/div>\n<p>\u60f3\u5beb Blog \u5f88\u4e45\u4e86\uff0c\u4e00\u76f4\u89ba\u5f97\u8a72\u627e\u500b\u5730\u65b9\u8a18\u9304\u4e00\u4e0b\u8166\u5b50\u88e1\u7684\u60f3\u6cd5\uff0c\u4e0d\u7136\u6211\u8a18\u6027\u8d85\u7d1a\u5dee\uff0c\u9694\u5929\u5c31\u5fd8\u4e86\u81ea\u5df1\u5230\u5e95\u5728\u5fd9\u4ec0\u9ebc\u3002<\/p>\n<p>\u90a3\u8981\u7528\u4ec0\u9ebc\u5beb\uff1f<\/p>\n<p>\u4e00\u822c\u7684 Blog \u670d\u52d9\u7576\u7136\u662f\u4e0d\u8003\u616e\uff0c\u5c0d<strong>\u6642\u5e38\u8981\u653e\u7a0b\u5f0f\u78bc<\/strong>\u7684\u4eba\u4f86\u8aaa\u5b8c\u5168\u4e0d\u9069\u5408\u3002<\/p>\n<p>\u5728\u5f88\u6f6e\u7684 Medium \u4e0a\u5beb\u904e\u4e00\u7bc7\u6587\u7ae0\uff0c\u6c92\u6709\u539f\u751f\u652f\u63f4 Code Syntax Highlighting\n\u8b93\u4eba\u975e\u5e38\u6d88\u706b\uff0c\u6bcf\u6b21\u90fd\u5f97\u7528 GitHub Gist \u653e\u7a0b\u5f0f\u78bc\u5be6\u5728\u6709\u9ede\u9ebb\u7169\u3002\u800c\u4e14\u7528 Vim + Markup Language \u5beb\u6587\u4ef6\u5beb\u7fd2\u6163\u4e86\uff0c\u5c0d\u5fc5\u9808\u7528\u6ed1\u9f20\u6539\u683c\u5f0f\u9019\u4ef6\u4e8b\u89ba\u5f97\u4e0d\u592a\u9806\u624b\u3002<\/p>\n<p>Google \u4e86\u4e00\u4e0b\uff0c\u8eab\u70ba Python \u5de5\u7a0b\u5e2b\u53ca\u611b\u597d\u8005\uff0c\u7528 <a class=\"reference external\" href=\"https:\/\/docs.getpelican.com\/en\/stable\/\">Pelican<\/a> \u5beb\u7136\u5f8c\u67b6\u5728 GitHub Pages\n\u4e0a\u4f3c\u4e4e\u662f\u6700\u597d\u7684\u9078\u64c7\uff1a<\/p>\n<ul class=\"simple\">\n<li>\u7528 Python \u5beb\u7684\uff0c\u53ef\u4ee5\u7528 Python \u64f4\u5145\u548c\u4fee\u6539\u529f\u80fd<\/li>\n<li>\u652f\u63f4 reStructuredText \u53ca Markdown<\/li>\n<li>\u652f\u63f4 Disqus \u548c Google Analytic \u7b49\u5176\u4ed6\u597d\u7528\u7684\u670d\u52d9<\/li>\n<li>\u652f\u63f4\u8a31\u591a\u4e3b\u984c\uff1a <a class=\"reference external\" href=\"http:\/\/www.pelicanthemes.com\/\">Pelican Themes<\/a><\/li>\n<\/ul>\n<p>\u8b9a\uff0c\u90a3\u5c31\u958b\u59cb\u5beb\u5427\uff01<\/p>\n<hr class=\"docutils\" \/>\n<div class=\"section\" id=\"id1\">\n<h2>\u7576 Pelican \u9047\u4e0a\u4e2d\u6587<\/h2>\n<p>\u5e73\u5e38\u6587\u4ef6\u90fd\u662f\u7528\u82f1\u6587\u5728\u5beb\uff0c\u7576\u958b\u59cb\u7528 reStructuredText \u5beb\u8d77\u4e2d\u6587\u7acb\u523b\u89ba\u5f97\u4e0d\u592a\u5c0d\u52c1\u2026\u597d\u50cf\u51fa\u73fe\u4e86\u5f88\u591a\u4e0d\u8a72\u51fa\u73fe\u7684\u7a7a\u683c\uff1f<\/p>\n<div class=\"figure\">\n<img alt=\" \u8a6d\u7570\u7684\u7a7a\u683c\u5011 \" src=\"https:\/\/johnliu55.tw\/when-pelican-meets-cjk\/images\/weird-spaces.png\" \/>\n<p class=\"caption\">\u8a6d\u7570\u7684\u7a7a\u683c\u5011<\/p>\n<\/div>\n<div class=\"section\" id=\"id2\">\n<h3>\u63db\u884c\u8b8a\u6210\u4e86\u7a7a\u683c<\/h3>\n<p>\u6211\u662f\u9f9c\u6bdb\u4eba\uff0c\u539f\u59cb\u78bc\u7684\u884c\u6578\u4e0d\u8d85\u904e 80 \u500b\u5b57\u5143\u662f\u57fa\u672c\uff0c\u6700\u591a\u4e5f\u4e0d\u80fd\u8d85\u904e 100\uff0c\u6240\u4ee5\u5f88\u9577\u7684\u4e00\u500b\u6bb5\u843d\u6703\u7528\u591a\u884c\u8868\u793a\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"n\">\u6211\u662f\u9f9c\u6bdb\u4eba<\/span><span class=\"err\">\uff0c<\/span><span class=\"n\">\u539f\u59cb\u78bc\u7684\u884c\u6578\u4e0d\u8d85\u904e 80 \u500b\u5b57\u5143\u662f\u57fa\u672c<\/span><span class=\"err\">\uff0c<\/span><span class=\"n\">\u6700\u591a\u4e5f\u4e0d\u80fd\u8d85\u904e 100<\/span><span class=\"err\">\uff0c<\/span>\n<span class=\"n\">\u6240\u4ee5\u5f88\u9577\u7684\u4e00\u500b\u6bb5\u843d\u6703\u7528\u591a\u884c\u8868\u793a<\/span><span class=\"err\">\uff1a<\/span>\n<\/pre><\/div>\n<p>reStructuredText \u548c Markdown \u90fd\u6703\u4fdd\u7559\u9019\u500b\u63db\u884c\u5b57\u5143\u5230\u8f49\u63db\u5f8c\u7684 HTML \u4e2d\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"o\">&lt;<\/span><span class=\"n\">p<\/span><span class=\"o\">&gt;<\/span><span class=\"n\">\u6211\u662f\u9f9c\u6bdb\u4eba<\/span><span class=\"err\">\uff0c<\/span><span class=\"n\">\u539f\u59cb\u78bc\u7684\u884c\u6578\u4e0d\u8d85\u904e 80 \u500b\u5b57\u5143\u662f\u57fa\u672c<\/span><span class=\"err\">\uff0c<\/span><span class=\"n\">\u6700\u591a\u4e5f\u4e0d\u80fd\u8d85\u904e 100<\/span><span class=\"err\">\uff0c<\/span>\n<span class=\"n\">\u6240\u4ee5\u5f88\u9577\u7684\u4e00\u500b\u6bb5\u843d\u6703\u7528\u591a\u884c\u8868\u793a<\/span><span class=\"err\">\uff1a<\/span><span class=\"o\">&lt;\/<\/span><span class=\"n\">p<\/span><span class=\"o\">&gt;<\/span>\n<\/pre><\/div>\n<p>\u800c\u700f\u89bd\u5668\u5728\u9047\u5230\u9019\u6a23\u7684\u63db\u884c\u5b57\u5143\u6642\u6703\u5c07\u4ed6\u8f49\u63db\u70ba\u7a7a\u683c\uff0c\u56e0\u70ba\n<a class=\"reference external\" href=\"https:\/\/www.w3.org\/MarkUp\/html-spec\/html-spec_4.html#SEC4.2.2\">Spec<\/a>\n\u5c31\u662f\u9019\u6a23\u898f\u5b9a\uff1a<\/p>\n<blockquote>\nAn HTML user agent should treat end of line in any of its variations as\na word space in all contexts except preformatted text.<\/blockquote>\n<p>\u9019\u4ef6\u4e8b\u5728\u82f1\u6587\u5f88\u5408\u7406\uff0c\u4f46\u5230\u4e86\u4e2d\u6587\u5c31\u4e0d\u5408\u7406\u4e86\uff0c<strong>\u56e0\u70ba\u6211\u5011\u4e0d\u6703\u7528\u7a7a\u683c\u628a\u6587\u5b57\u9694\u958b<\/strong>\u3002\u65bc\u662f\u4e4e\uff0c\u4e0a\u9762\u7684\u4f8b\u5b50\u700f\u89bd\u5668\u6703\u986f\u793a\u70ba\uff1a<\/p>\n<blockquote>\n\u6211\u662f\u9f9c\u6bdb\u4eba\uff0c\u539f\u59cb\u6a94\u7684\u884c\u6578\u4e0d\u8d85\u904e 80 \u500b\u5b57\u5143\u662f\u57fa\u672c\uff0c100 \u5247\u662f\u6700\u5927\u503c\uff0c \u6240\u4ee5\u5f88\u9577\u7684\u4e00\u500b\u6bb5\u843d\u6703\u7528\u591a\u884c\u8868\u793a\uff1a<\/blockquote>\n<p>\u6ce8\u610f\u5230\u300c\u6240\u4ee5\u300d\u524d\u9762\u591a\u4e86\u4e00\u500b\u7a7a\u683c\u3002\u5982\u679c\u4f60\u7fd2\u6163\u5f88\u597d\uff0c\u53ea\u6709\u5728\u4f7f\u7528\u6a19\u9ede\u7b26\u865f\u4e4b\u5f8c\u624d\u6703\u63db\u884c\uff0c\u90a3\u770b\u8d77\u4f86\u5f71\u97ff\u4e0d\u5927\u3002\u4f46\u5982\u679c\u63db\u884c\u662f\u4ecb\u65bc\u5169\u500b\u4e2d\u6587\u5b57\u4e4b\u9593\uff0c\u90a3\u5c31\u6703 \u50cf\u9019\u6a23\u5728\u6587\u5b57\u9593\u51fa\u73fe\u8a6d\u7570\u7684\u7a7a\u683c\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"inline-markup\">\n<h3>Inline Markup \u7684\u7a7a\u683c<\/h3>\n<p>\u4e0d\u50cf Markdown\uff0creStructuredText \u8981\u6c42<strong>\u5fc5\u9808\u7528\u7a7a\u683c<\/strong>\n\uff08<a class=\"reference external\" href=\"http:\/\/docutils.sourceforge.net\/docs\/ref\/rst\/restructuredtext.html#inline-markup-recognition-rules\">\u6216\u5176\u4ed6\u985e\u4f3c\u529f\u80fd\u7684\u5b57\u5143<\/a>\uff09\u5c07 Inline Markup\n\u8207\u5176\u4ed6\u7684\u6587\u5b57\u5340\u9694\u958b\u4f86\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>This is **inline markup** bold.\n<\/pre><\/div>\n<p>\u9019\u6a23\u7684\u7a7a\u683c\u6703\u88ab\u4fdd\u7559\u5230 HTML\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"p\">&lt;<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span>This is <span class=\"p\">&lt;<\/span><span class=\"nt\">strong<\/span><span class=\"p\">&gt;<\/span>inline markup<span class=\"p\">&lt;\/<\/span><span class=\"nt\">strong<\/span><span class=\"p\">&gt;<\/span> bold.<span class=\"p\">&lt;\/<\/span><span class=\"nt\">p<\/span><span class=\"p\">&gt;<\/span>\n<\/pre><\/div>\n<p>\u8ddf\u4e0a\u9762\u63d0\u5230\u7684\u4e00\u6a23\uff0c\u9019\u7a7a\u683c\u5728\u82f1\u6587\u6c92\u5dee\uff0c\u4e2d\u6587\u5c31\u4e0d\u884c\u4e86\u3002\u4e0d\u904e\u6c5f\u6e56\u5728\u8d70\uff0cWorkaround \u8981\u6709\uff0c\u6700\u7c21\u55ae\u7684\u65b9\u6cd5\u662f\u81ea\u5df1\u7528 <tt class=\"docutils literal\">\\<\/tt> \u4f86\u300c\u8df3\u812b\u300d\u9019\u500b\u7a7a\u683c\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>This is\\ **inline markup**\\ bold.\n<\/pre><\/div>\n<p>\u4f46\u6bcf\u6b21\u90fd\u8981\u624b\u52d5\u52a0\u5165\u9019\u53cd\u659c\u7dda\u5be6\u5728\u6709\u9ede\u9ebb\u7169\u3002\u5982\u679c\u9019\u7a7a\u683c\u80fd\u81ea\u5df1\u6d88\u5931\uff0c\u90a3\u8a72\u6709\u591a\u597d\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"bonus\">\n<h3>Bonus\uff1a\u4e2d\u82f1\u6587\u9593\u7684\u7a7a\u683c<\/h3>\n<blockquote>\n\u6709\u7814\u7a76\u986f\u793a\uff0c\u6253\u5b57\u7684\u6642\u5019\u4e0d\u559c\u6b61\u5728\u4e2d\u6587\u548c\u82f1\u6587\u4e4b\u9593\u52a0\u7a7a\u683c\u7684\u4eba\uff0c\u611f\u60c5\u8def\u90fd\u8d70\u5f97\u5f88\u8f9b\u82e6\uff0c\u6709\u4e03\u6210\u7684\u6bd4\u4f8b\u6703\u5728 34 \u6b72\u7684\u6642\u5019\u8ddf\u81ea\u5df1\u4e0d\u611b\u7684\u4eba\u7d50\u5a5a\uff0c\u800c\u5176\u9918\u4e09\u6210\u7684\u4eba\u6700\u5f8c\u53ea\u80fd\u628a\u907a\u7522\u7559\u7d66\u81ea\u5df1\u7684\u8c93\u3002\u7562\u7adf\u611b\u60c5\u8ddf\u66f8\u5beb\u90fd\u9700\u8981\u9069\u6642\u5730\u7559\u767d\u3002\n\u2014\u2014 <a class=\"reference external\" href=\"https:\/\/github.com\/vinta\/pangu.js\">vinta\/pangu.js<\/a><\/blockquote>\n<p>\u2026\u9019\u7a2e\u7a7a\u683c\u6211\u500b\u4eba\u662f\u89ba\u5f97\u9084\u53ef\u4ee5\u63a5\u53d7\u5566\uff0c\u4e0d\u904e\u5982\u679c Pelican \u80fd\u81ea\u52d5\u5e6b\u6211\u52a0\u4e0a\u9019\u4e9b\u7a7a\u683c\uff0c\u90a3\u6211\u5c31\u4e0d\u7528\u64d4\u5fc3\u672a\u4f86\u6703\u8ddf\u4e0d\u611b\u7684\u4eba\u7d50\u5a5a\u4e86\u3002\u5beb\u7a0b\u5f0f\u771f\u662f\u4efd\u5049\u5927\u7684\u5de5\u4f5c\u3002<\/p>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"pelican-plugin\">\n<h2>\u5beb\u500b Pelican Plugin \u5427\uff01<\/h2>\n<p>\u539f\u672c\u60f3\u8aaa\u53ef\u4ee5\u5f9e\u8655\u7406 reStructuredText \u7684\u51fd\u5f0f\u5eab <a class=\"reference external\" href=\"http:\/\/docutils.sourceforge.net\/\">docutils<\/a> \u4e0b\u624b\uff0c\u7121\u5948\u529f\u529b\u4e0d\u5920\u9ad8\u6df1\uff0c\u770b\u4e0d\u51fa\u4f86\u5230\u5e95\u8a72\u600e\u9ebc\u4fee\u6539\u4ed6\u7684\u884c\u70ba\uff0c\u53ea\u597d\u5f9e Pelican \u4e0b\u624b\u3002<\/p>\n<p>\u4e4b\u524d\u63d0\u5230 Pelican \u80fd\u5920\u7528 Python \u81ea\u5df1\u64f4\u5145\u529f\u80fd\uff0c\u800c\u5728\u5b98\u65b9\u7684 <a class=\"reference external\" href=\"https:\/\/github.com\/getpelican\/pelican-plugins\">pelican-plugins<\/a>\n\u5217\u8868\u4e2d\u641c\u5c0b\u4e86\u4e00\u4e0b\u53ea\u6709 <a class=\"reference external\" href=\"https:\/\/github.com\/yuex\/cjk-auto-spacing\">cjk-auto-spacing<\/a> \u80fd\u5920\u81ea\u52d5\u8abf\u6574\u4e2d\u82f1\u6587\u9593\u7684\u7a7a\u683c\uff0c\u4f46\u9084\u662f\u6c92\u6709\u89e3\u6c7a\u6240\u6709\u7684\u554f\u984c\u3002Google \u4e86\u4e00\u4e0b\u627e\u5230\u9019\u7bc7\u300c<a class=\"reference external\" href=\"http:\/\/blog.guorongfei.com\/2015\/04\/25\/how-to-fix-the-markdown-newline-blank-problem\/\">\u89e3\u51b3 jekyll \u4e2d\u6587\u6362\u884c\u53d8\u6210\u7a7a\u683c\u7684\u95ee\u9898<\/a>\u300d\uff0c\u4f46\u4ed6\u662f\u7528\n<a class=\"reference external\" href=\"https:\/\/jekyllrb.com\/\">Jekyll<\/a> \u800c\u4e0d\u662f Pelican\u3002\u5b89\u634f\u2026\u4e0d\u5982\u81ea\u5df1\u5beb\u4e00\u500b\u5427\uff01<\/p>\n<div class=\"section\" id=\"id4\">\n<h3>Pelican Plugin \u7684\u904b\u4f5c\u65b9\u5f0f<\/h3>\n<blockquote>\nPelican \u5b9a\u7fa9\u4e86\u5404\u7a2e\u300c<strong>\u4fe1\u865f<\/strong>\u300d\uff08Signal\uff09\uff0c\u4ee3\u8868\u4e86\u5f9e\u539f\u59cb\u78bc\u5230\u6700\u5f8c\u751f\u51fa HTML \u7684\u5404\u500b<strong>\u968e\u6bb5<\/strong>\u3002\u4f60\u53ef\u4ee5\u5c07\u81ea\u5df1\u5beb\u7684 Python \u51fd\u5f0f<strong>\u8a3b\u518a<\/strong>\u5230\u9019\u4e9b\u4fe1\u865f\u4e0a\uff0cPelican \u5c31\u6703\u5728\u90a3\u4e9b<strong>\u4fe1\u865f\u5c0d\u61c9\u7684\u968e\u6bb5\u767c\u751f\u6642<\/strong>\u547c\u53eb\u4f60\u7684\u51fd\u5f0f\uff0c\u4e26\u5c07\u7576\u4e0b\u7684\u72c0\u614b\u6216\u8655\u7406\u7684\u7269\u4ef6\u50b3\u9032\u9019\u500b\u51fd\u5f0f\uff0c\u8b93\u4f60\u7684\u51fd\u5f0f\u80fd\u5920\u8abf\u6574 Pelican \u7684\u884c\u70ba\u3002\u7d30\u7bc0\u548c\u4fe1\u865f\u5217\u8868\u8acb\u53c3\u8003 <a class=\"reference external\" href=\"https:\/\/docs.getpelican.com\/en\/stable\/plugins.html\">Pelican Plugin Document<\/a> \u3002<\/blockquote>\n<p>\u524d\u9762\u63d0\u5230\u4e86 <a class=\"reference external\" href=\"https:\/\/github.com\/yuex\/cjk-auto-spacing\">cjk-auto-spacing<\/a> \uff0c\u7406\u6240\u7576\u7136\u62ff\u4ed6\u4f86\u53c3\u8003\u4e00\u4e0b\u3002\u5b83\u8655\u7406\u7684\u65b9\u5f0f\u662f\u4f7f\u7528\u4fe1\u865f\n<em>content_object_init<\/em> \u4f86\u53d6\u5f97 <tt class=\"docutils literal\">content_object<\/tt> \u7269\u4ef6\uff0c\u800c\u9019\u500b\u7269\u4ef6\u7684 <tt class=\"docutils literal\">_content<\/tt>\n\u5c6c\u6027\u5b58\u653e\u4e86\u5f9e reStructuredText \u53ca Markdown \u539f\u59cb\u78bc\u8f49\u63db\u800c\u4f86\u7684 <strong>HTML<\/strong> \uff0c\u4ee5 <tt class=\"docutils literal\">str<\/tt>\n\u5132\u5b58\u3002\u6211\u5011\u53ef\u4ee5\u6839\u64da\u9700\u6c42\u4f86\u8abf\u6574\u9019\u500b HTML\uff0c\u8abf\u6574\u5b8c\u5f8c\u518d assign \u56de <tt class=\"docutils literal\">_content<\/tt> \uff0cPelican \u5c31\u6703\u7528\u9019\u4efd\u65b0\u7684 HTML \u7e7c\u7e8c\u4e4b\u5f8c\u7684\u5de5\u4f5c\u3002<\/p>\n<p>\u8209\u4f8b\u4f86\u8aaa\uff0c\u5982\u679c\u6211\u5011\u60f3\u628a HTML \u88e1\u7684\u6240\u6709 <tt class=\"docutils literal\">&lt;p&gt;<\/tt> Tag \u63db\u6210 <tt class=\"docutils literal\">&lt;foo&gt;<\/tt> \uff0c\u53ef\u4ee5\u5f88\u5feb\u7684\u7528\nRegular Expression \u4f86\u9054\u6210\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"kn\">import<\/span> <span class=\"nn\">re<\/span>\n\n<span class=\"kn\">from<\/span> <span class=\"nn\">pelican<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">signals<\/span>\n\n\n<span class=\"k\">def<\/span> <span class=\"nf\">process<\/span><span class=\"p\">(<\/span><span class=\"n\">content<\/span><span class=\"p\">):<\/span>\n    <span class=\"n\">new_content<\/span> <span class=\"o\">=<\/span> <span class=\"n\">re<\/span><span class=\"o\">.<\/span><span class=\"n\">sub<\/span><span class=\"p\">(<\/span><span class=\"sa\">r<\/span><span class=\"s1\">&#39;&lt;(\/)?p&gt;&#39;<\/span><span class=\"p\">,<\/span> <span class=\"sa\">r<\/span><span class=\"s1\">&#39;&lt;\\1foo&gt;&#39;<\/span><span class=\"p\">,<\/span> <span class=\"n\">content<\/span><span class=\"o\">.<\/span><span class=\"n\">_content<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">content<\/span><span class=\"o\">.<\/span><span class=\"n\">_content<\/span> <span class=\"o\">=<\/span> <span class=\"n\">new_content<\/span>\n\n\n<span class=\"k\">def<\/span> <span class=\"nf\">register<\/span><span class=\"p\">():<\/span>\n    <span class=\"n\">signals<\/span><span class=\"o\">.<\/span><span class=\"n\">content_object_init<\/span><span class=\"o\">.<\/span><span class=\"n\">connect<\/span><span class=\"p\">(<\/span><span class=\"n\">process<\/span><span class=\"p\">)<\/span>\n<\/pre><\/div>\n<p>Pelican \u898f\u5b9a\u6bcf\u500b Plugin \u90fd\u5fc5\u9808\u8981\u6709 <tt class=\"docutils literal\">register<\/tt> \u51fd\u5f0f\uff0c\u76ee\u7684\u5728\u6307\u5b9a\u4f60\u9700\u8981\u54ea\u4e9b\u4fe1\u865f\u4ee5\u53ca\u4ed6\u5011\u8981\u89f8\u767c\u7684\u51fd\u5f0f\u3002<\/p>\n<\/div>\n<\/div>\n<div class=\"section\" id=\"pelican-cjk\">\n<h2>Pelican-CJK<\/h2>\n<p>\u82b1\u4e86\u4e9b\u6642\u9593\u7528 Regular Expression \u523b\u4e86\u4e00\u500b\u80fd\u5920\u81ea\u52d5\u8655\u7406\u4ee5\u4e0a\u554f\u984c\u7684 Plugin\uff1a\n<a class=\"reference external\" href=\"https:\/\/github.com\/johnliu55tw\/pelican-cjk\">pelican-cjk<\/a> \u3002\u5b83\u80fd\u5920\u81ea\u52d5\u6839\u64da\u4f60\u5beb\u7684\u5167\u5bb9\u8abf\u6574 HTML\uff0c\u89e3\u6c7a\u4e0a\u8ff0\u90a3\u4e9b\u5c0f\u6bdb\u75c5\u3002<\/p>\n<p>\u5728\u958b\u767c\u9019\u500b Plugin \u7684\u6642\u5019\u8003\u616e\u4e86\u4ee5\u4e0b\u5e7e\u9ede\uff1a<\/p>\n<ul class=\"simple\">\n<li>\u5fc5\u9808\u652f\u63f4 reStructuredText \u53ca Markdown<\/li>\n<li>\u4e0d\u60f3\u4f9d\u8cf4\u5176\u4ed6\u7b2c\u4e09\u65b9\u6a21\u7d44<\/li>\n<\/ul>\n<p>\u5982\u679c\u8981\u5f9e\u539f\u59cb\u78bc\uff08 <tt class=\"docutils literal\">.md<\/tt> \u8207 <tt class=\"docutils literal\">.rst<\/tt> \uff09\u6216 Parser \u4e0b\u624b\uff0c\u5c31\u9084\u5f97\u8003\u616e reStructuredText \u548c Markdown \u7684\u5dee\u7570\uff0c\u6240\u4ee5\u5982\u679c\u5169\u500b\u90fd\u5f97\u652f\u63f4\uff0c\u76f4\u63a5\u5f9e HTML \u4e0b\u624b\u6703\u597d\u8655\u7406\u5f88\u591a\u3002<\/p>\n<p>\u800c\u57fa\u65bc\u7b2c\u4e8c\u9ede\uff0c\n<a class=\"reference external\" href=\"https:\/\/www.crummy.com\/software\/BeautifulSoup\/bs4\/doc\/\">Beautiful Soup<\/a>\n\u7b49\u7b49\u80fd\u5920\u5e6b\u52a9\u8655\u7406 HTML \u7684\u6a21\u7d44\u4e5f\u5c31\u4e0d\u8003\u616e\u4e86\uff0c\u800c Python \u5167\u5efa\u7684\n<a class=\"reference external\" href=\"https:\/\/docs.python.org\/3\/library\/html.parser.html\">HTML Parser<\/a> \u53c8\u592a\u967d\u6625\uff0c\u6240\u4ee5\u6700\u5f8c\u6211\u76f4\u63a5\u7528 Regex \u4f86\u8655\u7406\u3002\u4f46\u9019\u4e0d\u514d\u6709\u4e9b\u5c0f\u554f\u984c\uff1a<\/p>\n<ul class=\"simple\">\n<li>\u7121\u6cd5\u5224\u65b7\u76ee\u524d\u8981\u8abf\u6574\u7684\u6587\u5b57\u5c6c\u65bc\u90a3\u7a2e\u5340\u584a\u3002reStructuredText \u548c Markdown \u90fd\u6709\u6240\u8b02\u7684\u300cLiteral Block\u300d\uff0c\u5728\u9019\u500b\u5340\u584a\u5167\u662f\u4e0d\u6703\u8655\u7406\u4efb\u4f55\u6a19\u8a18\u7684\u3002\n<strong>\u4f46\u56e0\u70ba\u7a0b\u5f0f\u7121\u6cd5\u6839\u64da HTML \u5224\u65b7\u5340\u584a\uff0c\u5b83\u4e00\u6a23\u6703\u8abf\u6574\u9019\u500b\u5340\u584a\u5167\u7684\u6587\u5b57\u3002<\/strong>\n\u4e0d\u904e Literal Block \u901a\u5e38\u662f\u7528\u4f86\u653e\u7bc4\u4f8b\u7a0b\u5f0f\u78bc\u7684\uff0c\u6bd4\u8f03\u4e0d\u6703\u51fa\u73fe\u4e2d\u82f1\u6df7\u7528\u7684\u60c5\u6cc1\uff0c\u6240\u4ee5\u5c31\u6211\u8a8d\u70ba\u5f71\u97ff\u4e0d\u5927\u3002<\/li>\n<li>\u900f\u904e\u4e0a\u8ff0\u4fe1\u865f\u62ff\u5230\u7684 HTML <strong>\u4e0d\u5305\u542b\u6587\u7ae0\u7684\u6a19\u984c<\/strong>\uff0c\u6240\u4ee5\u6a19\u984c\u7121\u6cd5\u8abf\u6574\uff0c\u5f97\u81ea\u5df1\u52a0\u5165\u4e2d\u82f1\u6587\u9593\u7684\u7a7a\u683c\u3002\u9019\u61c9\u8a72\u53ef\u4ee5\u900f\u904e\u5176\u4ed6\u4fe1\u865f\u53d6\u5f97\uff0c\u4f46\u6211\u9084\u6c92\u7814\u7a76\u3002<\/li>\n<li>\u70ba\u4e86\u7c21\u55ae\u8d77\u898b\uff0c\u6211\u5beb\u7684 Regex \u4e0d\u6703\u91dd\u5c0d\u4ee5\u4e0b\u60c5\u6cc1\u8abf\u6574\u7a7a\u683c\uff1a<ul>\n<li>\u5de2\u72c0 Inline Markup\uff1areStructuredText \u4e0d\u5141\u8a31\u9019\u7a2e\u60c5\u6cc1\uff0c\u4e5f\u5c31\u662f\u8aaa HTML \u4e2d\u4e0d\u6703\u51fa\u73fe\n<tt class=\"docutils literal\"><span class=\"pre\">English&lt;em&gt;&lt;strong&gt;\u659c\u9ad4\u53c8\u7c97\u9ad4&lt;\/strong&gt;&lt;\/em&gt;<\/span><\/tt> \u9019\u6a23\u7684\u6771\u897f\u3002\u4f46 Markdown \u5141\u8a31\uff0c\u6240\u4ee5\u9019\u662f\u6709\u6a5f\u6703\u51fa\u73fe\u7684\u3002\u4ee5\u9019\u500b\u4f8b\u5b50\u4f86\u8aaa\uff0c\u300cEnglish\u300d\u8207\u300c\u659c\u9ad4\u53c8\u7c97\u9ad4\u300d\u9593\u5c31\u4e0d\u6703\u81ea\u52d5\u52a0\u7a7a\u683c\u3002<\/li>\n<li>\u9023\u7e8c Inline Markup\uff1a <tt class=\"docutils literal\"><span class=\"pre\">&lt;em&gt;English&lt;\/em&gt;&lt;strong&gt;\u5f88\u5f37&lt;\/strong&gt;<\/span><\/tt>\n\u9023\u7e8c\u7684\u5169\u500b Inline Markup \u4e5f\u9700\u8981\u984d\u5916\u5224\u65b7\uff0c\u800c\u4e14\u4f7f\u7528\u60c5\u6cc1\u4e5f\u4e0d\u591a\uff0c\u6240\u4ee5\u5728\u6b64\u4e5f\u4e0d\u8003\u616e\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>\u5e0c\u671b\u9019\u500b Plugin \u80fd\u5920\u5e6b\u52a9\u66f4\u591a\u8ddf\u6211\u4e00\u6a23\u6bdb\u5f88\u591a\u7684\u4eba\uff0c\u5982\u679c\u5927\u5bb6\u6709\u4ec0\u9ebc\u66f4\u597d\u7684\u65b9\u6cd5\u4e5f\u6b61\u8fce\u4e00\u8d77\u8a0e\u8ad6\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"references\">\n<h2>References<\/h2>\n<ul class=\"simple\">\n<li><a class=\"reference external\" href=\"https:\/\/github.com\/vinta\/pangu.js\">vinta\/pangu.js<\/a><\/li>\n<li><a class=\"reference external\" href=\"http:\/\/blog.guorongfei.com\/2015\/04\/25\/how-to-fix-the-markdown-newline-blank-problem\/\">\u89e3\u51b3 jekyll \u4e2d\u6587\u6362\u884c\u53d8\u6210\u7a7a\u683c\u7684\u95ee\u9898<\/a><\/li>\n<\/ul>\n<\/div>\n","category":[{"@attributes":{"term":"Python"}},{"@attributes":{"term":"Python"}},{"@attributes":{"term":"Pelican"}}]},{"title":"\u7528 Python \u63a7\u5236\u5176\u4ed6\u884c\u7a0b\u7684 TTY \u7d42\u7aef\u88dd\u7f6e","link":{"@attributes":{"href":"https:\/\/johnliu55.tw\/use-python-to-control-other-process-tty.html","rel":"alternate"}},"published":"2018-04-11T00:00:00+08:00","updated":"2018-04-11T00:00:00+08:00","author":{"name":"John Liu"},"id":"tag:johnliu55.tw,2018-04-11:\/use-python-to-control-other-process-tty.html","summary":"<p class=\"first last\">\u9019\u7bc7\u6587\u7ae0\u6703\u7a0d\u5fae\u89e3\u91cbUnix\u7684TTY\u7cfb\u7d71\u4ee5\u53caPseudo Terminal\u7684\u6982\u5ff5\uff0c\n\u63a5\u8457\u8a0e\u8ad6\u5982\u4f55\u4f7f\u7528Python\u7684 <tt class=\"docutils literal\">pty<\/tt> \u6a21\u7d44\u4e2d\u7684 <tt class=\"docutils literal\">pty.fork()<\/tt>\n\u4f86\u5efa\u7acb\u4e26\u63a7\u5236\u5b50\u884c\u7a0b\u7684TTY\u7cfb\u7d71\uff0c\n\u6700\u5f8c\u7528Python\u4f86\u5be6\u4f5c\u63a7\u5236 <em>madplay<\/em> \u9019\u500bCLI MP3 Player\u3002<\/p>\n","content":"<div class=\"section\" id=\"foreword\">\n<h2>Foreword<\/h2>\n<p>\u9019\u7bc7\u6587\u7ae0\u6703\u7a0d\u5fae\u89e3\u91cb Unix \u7684 TTY \u7cfb\u7d71\u4ee5\u53ca Pseudo Terminal \u7684\u6982\u5ff5\uff0c\u63a5\u8457\u8a0e\u8ad6\u5982\u4f55\u4f7f\u7528 Python \u7684 <tt class=\"docutils literal\">pty<\/tt> \u6a21\u7d44\u4e2d\u7684 <tt class=\"docutils literal\">pty.fork()<\/tt>\n\u4f86\u5efa\u7acb\u4e26\u63a7\u5236\u5b50\u884c\u7a0b\u7684 TTY \u7cfb\u7d71\uff0c\u6700\u5f8c\u7528 Python \u4f86\u5be6\u4f5c\u63a7\u5236 <em>madplay<\/em> \u9019\u500b CLI MP3 Player\u3002<\/p>\n<p>\u6709\u554f\u984c\u6b61\u8fce\u5927\u5bb6\u8a62\u554f\uff0c\u6709\u4efb\u4f55\u932f\u8aa4\u4e5f\u8acb\u5927\u5bb6\u5e6b\u5fd9\u6307\u6b63 :D<\/p>\n<\/div>\n<hr class=\"docutils\" \/>\n<div class=\"section\" id=\"python-mp3-player\">\n<h2>Python MP3 Player<\/h2>\n<p>\u9019\u5e7e\u5929\u5728\u73a9 <a class=\"reference external\" href=\"https:\/\/www.seeedstudio.com\/ReSpeaker-Core-Based-On-MT7688-and-OpenWRT-p-2716.html\">ReSpeaker<\/a> \uff08\u4e00\u500b\u57fa\u65bc MT7688 \u7684\u8072\u63a7\u88dd\u7f6e\u958b\u767c\u677f\uff09\u9047\u5230\u4e86\u500b\u554f\u984c\uff1a\u5728 ReSpeaker \u4e2d\u8981\u5982\u4f55\u4f7f\u7528 Python \u64ad\u653e MP3 \u6a94\u6848\uff1f<\/p>\n<p>\u597d\u5728 ReSpeaker \u4e0a\u5df2\u6709 <em>madplay<\/em> \u9019\u500b\u6307\u4ee4\uff0c\u80fd\u5920\u76f4\u63a5\u64ad\u653e MP3\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>$ madplay MP3_FILE\n<\/pre><\/div>\n<p>\u66f4\u8b9a\u7684\u662f\u53ea\u8981\u52a0\u4e0a <tt class=\"docutils literal\"><span class=\"pre\">--tty-control<\/span><\/tt> \u5c31\u80fd\u76f4\u63a5\u63a7\u5236\u64ad\u653e\u72c0\u614b\uff01<\/p>\n<div class=\"highlight\"><pre><span><\/span>$ madplay MP3_FILE --tty-control MP3_FILE\n<\/pre><\/div>\n<p>\u4f8b\u5982\u6309\u4e0b <tt class=\"docutils literal\">p<\/tt> \u5c31\u80fd\u63a7\u5236\u97f3\u6a02\u64ad\u653e\u7684\u66ab\u505c \/ \u7e7c\u7e8c\u3002\u5f88\u597d\uff01\u9019\u6a23\u5c31\u53ef\u4ee5\u7528 <tt class=\"docutils literal\">subprocess<\/tt> \u6a21\u7d44\u555f\u52d5 madplay \u4f86\u63a7\u5236\u97f3\u6a02\u64ad\u653e\u5566\uff01\u7136\u5f8c<strong>\u5c0d\u9019\u500b\u5b50\u884c\u7a0b\u7684 stdin<\/strong> \u5beb\u5165\u9019\u4e9b\u63a7\u5236\u7528\u7684\u9375\u61c9\u8a72\u5c31\u80fd\u63a7\u5236\u97f3\u6a02\u64ad\u653e\u4e86\u5427\uff1f\u81f3\u5c11\u6211\u662f\u9019\u6a23\u60f3\u7684\u2026<\/p>\n<\/div>\n<div class=\"section\" id=\"the-mystery-tty\">\n<h2>The mystery TTY<\/h2>\n<p>\u4fd7\u8a71\u8aaa\u7684\u597d\uff0c\u4ee3\u8a8c\u5f80\u5f80\u4e0d\u662f\u61a8\u4eba\u6240\u60f3\u7684\u90a3\u9ebc\u7c21\u55ae\uff0c\u8a66\u4e86\u5e7e\u56de\u5f8c\u767c\u73fe\u7adf\u7136\u6c92\u6548\uff1f\uff01\u4e00\u6c23\u4e4b\u4e0b\u7ffb\u4e86\u7ffb madplay \u7684\u539f\u59cb\u78bc\uff0c\u767c\u73fe <tt class=\"docutils literal\">player.c<\/tt>\n\u88e1\u6709\u4e9b\u8ddf <em>TTY<\/em> \u9019\u73a9\u610f\u5152\u6709\u95dc\u7684\u7a0b\u5f0f\u78bc\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"cp\"># define TTY_DEVICE &quot;\/dev\/tty&quot;<\/span>\n<span class=\"p\">...<\/span>\n<span class=\"n\">tty_fd<\/span> <span class=\"o\">=<\/span> <span class=\"n\">open<\/span><span class=\"p\">(<\/span><span class=\"n\">TTY_DEVICE<\/span><span class=\"p\">,<\/span> <span class=\"n\">O_RDONLY<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">...<\/span>\n<span class=\"n\">count<\/span> <span class=\"o\">=<\/span> <span class=\"n\">read<\/span><span class=\"p\">(<\/span><span class=\"n\">tty_fd<\/span><span class=\"p\">,<\/span> <span class=\"o\">&amp;<\/span><span class=\"n\">key<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"p\">...<\/span>\n<\/pre><\/div>\n<p>\u73a9\u904e Linux \u7684\u670b\u53cb\u5011\u61c9\u8a72\u5c0d TTY \u9019\u500b\u5b57\u6709\u5f37\u70c8\u7684\u719f\u6089\u611f\uff0c\u597d\u50cf\u4e09\u4e0d\u4e94\u6642\u5c31\u6703\u770b\u5230\u9019\u6771\u897f\u51fa\u73fe\u3002\u65bc\u662f\u6211\u62ff\u4ed6\u53bb Google \u5f8c\u5f97\u5230\u4e0b\u9762\u5e7e\u500b\u7d50\u8ad6\uff1a<\/p>\n<ol class=\"arabic simple\">\n<li>TTY \u6e90\u81ea\u65bc <em>Teletype<\/em> \u9019\u500b\u55ae\u5b57\uff0c\u4e2d\u6587\u7a31\u70ba<strong>\u96fb\u50b3\u6253\u5b57\u6a5f<\/strong>\uff0c\u662f\u53e4\u65e9\u5e74\u4ee3\u7528\u4f86\u9060\u8ddd\u96e2\u50b3\u905e\u6587\u5b57\u8cc7\u8a0a\u7528\u7684\u6a5f\u5668\u4ee5\u53ca\u6a5f\u5236\u3002<\/li>\n<li>\u5f88\u4e45\u5f88\u4e45\u4ee5\u524d\u4e26\u6c92\u6709 PC\u200a\u2014\u200aPersonal Computer \u9019\u7a2e\u6771\u897f\uff0c\u6709\u7684\u53ea\u662f\u4e00\u53f0\u87a2\u5e55\u52a0\u9375\u76e4\u7d44\u6210\u7684\u7d42\u7aef\u6a5f\uff0c\u900f\u904e\u4e32\u5217\u57e0\u7b49\u7b49\u7684\u50b3\u8f38\u65b9\u5f0f\u8207\u4e00\u53f0\u4e2d\u592e\u4e3b\u6a5f\u6e9d\u901a\uff0c\u9032\u884c\u63a7\u5236\u8207\u904b\u7b97\u7684\u5de5\u4f5c\u3002Unix \u4e0b\u7684 TTY \u88dd\u7f6e\u6982\u5ff5\u5c31\u662f\u5f9e\u9019\u88e1\u51fa\u73fe\u7684\uff0c\u7d30\u7bc0\u53ef\u53c3\u8003\u6211\u5217\u5728\u6700\u5f8c\u9762\u7684 References\u3002<\/li>\n<li>TTY \u88dd\u7f6e\u67b6\u69cb\u4e2d\u6709\u4e00\u5c64\u53eb\u505a <a class=\"reference external\" href=\"https:\/\/en.wikipedia.org\/wiki\/Line_discipline\">Line discipline<\/a> \u3002\u9019\u6771\u897f\u4ecb\u65bc\u8edf\u9ad4\u5c64\uff08\u884c\u7a0b\u63a5\u6536\u5230\u7684\u8cc7\u8a0a\uff09\u548c\u9a45\u52d5\u5c64\uff08\u5be6\u969b\u4e0a\u8207\u786c\u9ad4\u6253\u4ea4\u9053\u90a3\u4e00\u5c64\uff09\u9593\uff0c\u8ca0\u8cac\u5c0d\u5f9e\u5176\u4e2d\u4e00\u5c64\u50b3\u905e\u904e\u4f86\u7684\u8cc7\u8a0a\u505a\u524d\u8655\u7406\uff0c\u518d\u50b3\u5230\u53e6\u4e00\u5c64\u3002\u8209\u4f8b\u50cf\u662f Line editing\uff08Buffering\u3001Backspace\u3001Echoing\u3001\u79fb\u52d5\u6e38\u6a19\u7b49\u2026\uff09\u3001\u5b57\u5143\u8f49\u63db\uff08 <tt class=\"docutils literal\">\\n<\/tt> \u8207 <tt class=\"docutils literal\">\\r\\n<\/tt> \u4e92\u76f8\u8f49\u63db\u2026\uff09\u3001\u63a7\u5236\u5b57\u5143\u8f49\u63db\u70ba\u4fe1\u865f\uff08ASCII 0x03\u2192SIGINT\uff09\u7b49\u7b49\u7684\u529f\u80fd\u3002<\/li>\n<li><tt class=\"docutils literal\">\/dev\/tty<\/tt> \u88dd\u7f6e\u4ee3\u8868\u7684\u662f<strong>\u76ee\u524d\u884c\u7a0b\u6240\u9023\u63a5\u8457\u7684\u7d42\u7aef\uff08Terminal\uff09\u88dd\u7f6e<\/strong>\u3002<\/li>\n<\/ol>\n<p>\u5f9e <tt class=\"docutils literal\">player.c<\/tt> \u4e2d\u7684\u7a0b\u5f0f\u78bc\u770b\u8d77\u4f86\uff0cmadplay \u662f\u76f4\u63a5\u5f9e \/dev\/tty \u9019\u500b\u88dd\u7f6e\u8b80\u53d6\u9375\u76e4\u8f38\u5165\uff0c\u800c\u4e0d\u662f\u5f9e stdin \u8b80\u53d6\u3002\u807d\u8d77\u4f86\u6709\u9ede\u591a\u6b64\u4e00\u8209\uff0c\u4f46\u9019\u9ebc\u505a\u6709\u500b\u597d\u8655\uff1a<\/p>\n<blockquote>\n\u4e00\u500b\u884c\u7a0b\u53ef\u4ee5\u5728\u5f9e stdin \u63a5\u6536\u8cc7\u6599\u7684\u540c\u6642\uff0c\u63a5\u6536\u4f86\u81ea\u9375\u76e4\u7684\u8a0a\u606f\u3002<\/blockquote>\n<p>\u8209\u4f8b\u4f86\u8aaa\uff0c <tt class=\"docutils literal\">cat MP3_FILE | madplay <span class=\"pre\">-\u2014tty-control<\/span> -<\/tt>\n\u9019\u4e32\u6307\u4ee4\u4e2d\u7684 madplay \u6703\u8b80\u53d6 stdin\uff0c\u800c <tt class=\"docutils literal\">cat MP3_FILE<\/tt> \u9019\u500b\u6307\u4ee4\u6703\u5c07 <tt class=\"docutils literal\">MP3_FILE<\/tt>\n\u9019\u500b\u6a94\u6848\u8f38\u51fa\u5230 stdout\uff0c\u4e2d\u9593\u6211\u5011\u85c9\u7531 <tt class=\"docutils literal\">|<\/tt> \u4f86\u5c07\u9019\u4e9b\u8cc7\u6599\u5c0e\u5411\u81f3 madplay \u9032\u884c\u64ad\u653e\u3002\u5728\u9019\u4e00\u9023\u4e32\u4e8b\u60c5\u767c\u751f\u7684\u540c\u6642\uff0c\u4f7f\u7528\u8005\u540c\u6a23\u53ef\u4ee5\u7528\u9375\u76e4\u63a7\u5236\u64ad\u653e\u72c0\u614b\u3002<\/p>\n<p>\u65e2\u7136\u5982\u6b64\uff0c\u90a3\u6709\u6c92\u6709\u8fa6\u6cd5\u63a7\u5236\u4e00\u500b\u884c\u7a0b\u6240\u9023\u63a5\u8457\u7684\u7d42\u7aef\u88dd\u7f6e\u5462\uff1f\u66f4\u91cd\u8981\u7684\u662f\uff0cPython \u505a\u7684\u5230\u55ce\uff1f<\/p>\n<\/div>\n<div class=\"section\" id=\"pseudo-terminal\">\n<h2>Pseudo Terminal<\/h2>\n<p>\u7576\u7136\u53ef\u4ee5\uff01\u91dd\u5c0d\u63a7\u5236\u4e00\u500b\u884c\u7a0b\u7684\u7d42\u7aef\uff0cPython \u6a19\u6e96\u51fd\u5f0f\u5eab\u63d0\u4f9b\u4e86\n<a class=\"reference external\" href=\"https:\/\/docs.python.org\/3\/library\/pty.html\">pty<\/a> \u9019\u500b\u6a21\u7d44\u4f86\u8655\u7406\u8207 <em>Pseudo Terminal<\/em> \u6709\u95dc\u7684\u6982\u5ff5\u3002\u90a3\u4ec0\u9ebc\u662f Pseudo Terminal\uff1f<\/p>\n<p>\u73fe\u5728 PC \u7576\u9053\uff0c\u57fa\u672c\u4e0a\u5df2\u7d93\u4e0d\u5b58\u5728\u904e\u53bb\u90a3\u7a2e\u300c\u4f7f\u7528\uff08\u4e0d\u5177\u904b\u7b97\u80fd\u529b\u7684\uff09\u7d42\u7aef\u9023\u4e0a\u4e00\u53f0\u96fb\u8166\u9032\u884c\u63a7\u5236\u8207\u904b\u7b97\u300d\u7684\u60c5\u5883\u3002\u4f46\u662f\u6211\u5011\u60f3\u628a TTY \u9019\u500b\u6982\u5ff5\u5ef6\u7e8c\u5230\u73fe\u5728\u7e7c\u7e8c\u7528\u8a72\u600e\u9ebc\u8fa6\uff1f\u65bc\u662f\u5c31\u51fa\u73fe\u4e86 Pseudo Terminal\uff08\u6ce8\u610f\uff1a\u8ddf Virtual Terminal \u662f\u4e0d\u540c\u7684\u6982\u5ff5\uff09\u3002<\/p>\n<p>\u95dc\u65bc\u4ed6\u7684\u5b9a\u7fa9\uff0c\u6211\u5011\u76f4\u63a5\u4f86\u770b\u4e00\u4e0b\n<a class=\"reference external\" href=\"https:\/\/linux.die.net\/man\/7\/pty\">pty \u7684 Linux man page<\/a> \uff1a<\/p>\n<blockquote>\nA pseudoterminal (sometimes abbreviated \u201cpty\u201d) is a pair of virtual\ncharacter devices that provide a bidirectional communication channel.\nOne end of the channel is called the master; the other end is called the\nslave. The slave end of the pseudoterminal provides an interface that\nbehaves exactly like a classical terminal. A process that expects to be\nconnected to a terminal, can open the slave end of a pseudoterminal and\nthen be driven by a program that has opened the master end. Anything that is\nwritten on the master end is provided to the process on the slave end as\nthough it was input typed on a terminal.<\/blockquote>\n<p>Pseudo Terminal \u5efa\u7acb\u4e86\u5169\u500b\u865b\u64ec\u5b57\u5143\u88dd\u7f6e\uff0c\u5206\u5225\u7a31\u70ba master \u8207 slave\uff0c\u63d0\u4f9b\u4e86\u4e00\u500b\u96d9\u5411\u6e9d\u901a\u7684\u7ba1\u9053\u3002\u8b80\u5beb slave \u7aef\u7684\u7684\u884c\u7a0b\u53ef\u4ee5\u628a\u8a72 slave \u88dd\u7f6e\u5b8c\u5168\u7576\u4f5c\u662f\u4e00\u500b\u666e\u901a\u7684 TTY \u88dd\u7f6e\u8edf\u9ad4\u5c64\uff0c\u5177\u6709\u7d42\u7aef\u7684\u884c\u70ba\u6a21\u5f0f\u3002\u800c\u53e6\u4e00\u500b\u884c\u7a0b\u5247\u80fd\u5c0d master \u7aef\u9032\u884c\u8b80\u5beb\uff0c\u628a master \u7aef\u7576\u4f5c\u662f TTY \u88dd\u7f6e\u7684\u786c\u9ad4\u5c64\u3002<strong>\u800c\u5176\u4e2d\u5c0d master \u6216 slave \u7aef\u5beb\u5165\u7684\u8cc7\u8a0a\uff0c\u540c\u6a23\u6703\u7d93\u904e line discipline \u7684\u8655\u7406\uff0c\u518d\u9032\u5230\u53e6\u4e00\u7aef\u3002<\/strong><\/p>\n<p>\u501f <a class=\"reference external\" href=\"http:\/\/www.linusakesson.net\/programming\/tty\/\">The TTY demystified<\/a> \u9019\u7bc7\u6587\u7ae0\u4e2d\u7684\u5716\u4f86\u8aaa\u660e\uff1a<\/p>\n<img alt=\"How xterm works\" src=\"https:\/\/johnliu55.tw\/use-python-to-control-other-process-tty\/images\/how-xterm-works.png\" \/>\n<p>\u63db\u53e5\u8a71\u8aaa\uff0c\u5c31\u662f<strong>\u4e32\u5217\u57e0\u63a5\u982d\u8b8a\u6210\u4e86\u4e00\u500b file descriptor<\/strong> \u3002\u65bc\u662f\u5462\uff0c\u50cf xterm \u4e4b\u985e\u7684\u7d42\u7aef\u6a21\u64ec\u5668\uff08Terminal Emulator\uff09\u5c31\u80fd\u5920\u4ee5\u7a0b\u5f0f\u7684\u65b9\u5f0f\u53bb\u6a21\u64ec\u4e00\u53f0\u53e4\u65e9\u5e74\u4ee3\u7d42\u7aef\u6a5f\uff0c\u5c07\u4f7f\u7528\u8005\u4f7f\u7528\u7d42\u7aef\u6a5f\u5c0d\u4e32\u5217\u57e0\u5beb\u5165\u53ca\u8b80\u53d6\u7684\u884c\u70ba\u6a21\u5f0f\uff0c\u6539\u70ba<strong>\u5beb\u5165\u53ca\u8b80\u53d6\u9019\u500b file descriptor<\/strong> \uff0c\u5728\u540c\u4e00\u53f0\u6a5f\u5668\u4e0a\u6a21\u64ec\u7d42\u7aef\u7684\u8f38\u5165\u53ca\u8f38\u51fa\u3002<\/p>\n<p>\u5927\u6982\u4e86\u89e3\u4e86 Pseudo Terminal\uff0c\u63a5\u4e0b\u4f86\u770b\u770b Python \u600e\u9ebc\u505a\u9019\u4ef6\u4e8b\u3002<\/p>\n<\/div>\n<div class=\"section\" id=\"the-pty-module\">\n<h2>The pty module<\/h2>\n<p>\u4e00\u53e5\u8a71\u89e3\u91cb\u5b8c pty \u6a21\u7d44\uff1a<\/p>\n<blockquote>\nstarting another process and being able to write to and read from its\ncontrolling terminal programmatically.<\/blockquote>\n<p>Bingo\uff0c\u9019\u807d\u8d77\u4f86\u5c31\u662f\u6211\u60f3\u8981\u7684\u554a\uff01\u5176\u4e2d\u6211\u5011\u6703\u9700\u8981\u7528\u5230 <tt class=\"docutils literal\">pty.fork<\/tt> \u9019\u500b\u51fd\u5f0f\uff1a<\/p>\n<blockquote>\n<tt class=\"docutils literal\">pty.fork()<\/tt> \uff1aFork \u4e00\u500b\u5b50\u884c\u7a0b\uff0c\u4e26\u8b93\u8a72\u5b50\u884c\u7a0b\u7684\u63a7\u5236\u7d42\u7aef\u63a5\u4e0a\u4e00\u500b Pseudo Terminal \u7684 slave \u7aef\u3002\u7236\u884c\u7a0b\u6703\u5f97\u5230\u8a72 Pseudo Terminal \u7684 master \u7aef\uff0c\u4ee5\u4e00\u500b file descriptor \u8868\u793a\u3002\u9019\u500b\u51fd\u5f0f\u7684\u56de\u50b3\u503c\u662f\u500b tuple\uff1a(pid, fd)\uff0c\u5b50\u884c\u7a0b\u5f97\u5230\u7684 pid \u6703\u662f 0\uff0c\u800c\u7236\u884c\u7a0b\u6703\u5f97\u5230\u4e00\u500b\u975e 0 \u7684\u503c\uff0c\u70ba\u5b50\u884c\u7a0b\u7684 pid\u3002<\/blockquote>\n<p>\u63db\u53e5\u8a71\u8aaa\uff0c\u6211\u5011\u53ef\u4ee5\u555f\u52d5\u4e00\u500b\u5b50\u884c\u7a0b\uff0c\u4e26\u4f7f\u7528\u7236\u884c\u7a0b\u4f86\u63a7\u5236\u8a72\u5b50\u884c\u7a0b\u7684\u7d42\u7aef\u88dd\u7f6e\uff0c\u4e5f\u5c31\u662f \/dev\/tty\u3002\u5728\u5be6\u505a\u4e4b\u524d\uff0c\u5148\u4f86\u6e2c\u8a66\u4e00\u4e0b <tt class=\"docutils literal\">pty.fork()<\/tt> \uff1a<\/p>\n<table class=\"highlighttable\"><tr><td class=\"linenos\"><div class=\"linenodiv\"><pre><span class=\"normal\"> 1<\/span>\n<span class=\"normal\"> 2<\/span>\n<span class=\"normal\"> 3<\/span>\n<span class=\"normal\"> 4<\/span>\n<span class=\"normal\"> 5<\/span>\n<span class=\"normal\"> 6<\/span>\n<span class=\"normal\"> 7<\/span>\n<span class=\"normal\"> 8<\/span>\n<span class=\"normal\"> 9<\/span>\n<span class=\"normal\">10<\/span>\n<span class=\"normal\">11<\/span>\n<span class=\"normal\">12<\/span>\n<span class=\"normal\">13<\/span>\n<span class=\"normal\">14<\/span>\n<span class=\"normal\">15<\/span>\n<span class=\"normal\">16<\/span>\n<span class=\"normal\">17<\/span>\n<span class=\"normal\">18<\/span>\n<span class=\"normal\">19<\/span>\n<span class=\"normal\">20<\/span>\n<span class=\"normal\">21<\/span>\n<span class=\"normal\">22<\/span>\n<span class=\"normal\">23<\/span>\n<span class=\"normal\">24<\/span>\n<span class=\"normal\">25<\/span>\n<span class=\"normal\">26<\/span>\n<span class=\"normal\">27<\/span>\n<span class=\"normal\">28<\/span>\n<span class=\"normal\">29<\/span>\n<span class=\"normal\">30<\/span>\n<span class=\"normal\">31<\/span>\n<span class=\"normal\">32<\/span>\n<span class=\"normal\">33<\/span>\n<span class=\"normal\">34<\/span>\n<span class=\"normal\">35<\/span>\n<span class=\"normal\">36<\/span>\n<span class=\"normal\">37<\/span><\/pre><\/div><\/td><td class=\"code\"><div class=\"highlight\"><pre><span><\/span><span class=\"kn\">import<\/span> <span class=\"nn\">pty<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">time<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">os<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">sys<\/span>\n\n\n<span class=\"n\">pid<\/span><span class=\"p\">,<\/span> <span class=\"n\">fd<\/span> <span class=\"o\">=<\/span> <span class=\"n\">pty<\/span><span class=\"o\">.<\/span><span class=\"n\">fork<\/span><span class=\"p\">()<\/span>\n<span class=\"k\">if<\/span> <span class=\"n\">pid<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">0<\/span><span class=\"p\">:<\/span>\n    <span class=\"c1\"># Child process<\/span>\n    <span class=\"k\">while<\/span> <span class=\"kc\">True<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">try<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">stdout<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Hello World!<\/span><span class=\"se\">\\n<\/span><span class=\"s1\">&#39;<\/span><span class=\"p\">)<\/span>\n            <span class=\"n\">time<\/span><span class=\"o\">.<\/span><span class=\"n\">sleep<\/span><span class=\"p\">(<\/span><span class=\"mi\">100<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">except<\/span> <span class=\"ne\">KeyboardInterrupt<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">stdout<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;SIGINT Received!<\/span><span class=\"se\">\\n<\/span><span class=\"s1\">&#39;<\/span><span class=\"p\">)<\/span>\n            <span class=\"n\">sys<\/span><span class=\"o\">.<\/span><span class=\"n\">exit<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n<span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n    <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Parent wait for 1 sec then write 0x03...&#39;<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">time<\/span><span class=\"o\">.<\/span><span class=\"n\">sleep<\/span><span class=\"p\">(<\/span><span class=\"mi\">1<\/span><span class=\"p\">)<\/span>\n    <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Parent write 0x03&#39;<\/span><span class=\"p\">)<\/span>\n    <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"n\">fd<\/span><span class=\"p\">,<\/span> <span class=\"sa\">b<\/span><span class=\"s1\">&#39;<\/span><span class=\"se\">\\x03<\/span><span class=\"s1\">&#39;<\/span><span class=\"p\">)<\/span>\n    <span class=\"c1\"># Read until EOF or Input\/Output Error<\/span>\n    <span class=\"n\">data<\/span> <span class=\"o\">=<\/span> <span class=\"sa\">b<\/span><span class=\"s1\">&#39;&#39;<\/span>\n    <span class=\"k\">while<\/span> <span class=\"kc\">True<\/span><span class=\"p\">:<\/span>\n        <span class=\"k\">try<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">buf<\/span> <span class=\"o\">=<\/span> <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">read<\/span><span class=\"p\">(<\/span><span class=\"n\">fd<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1024<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">except<\/span> <span class=\"ne\">OSError<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">break<\/span>\n        <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">if<\/span> <span class=\"n\">buf<\/span> <span class=\"o\">!=<\/span> <span class=\"sa\">b<\/span><span class=\"s1\">&#39;&#39;<\/span><span class=\"p\">:<\/span>\n                <span class=\"n\">data<\/span> <span class=\"o\">+=<\/span> <span class=\"n\">buf<\/span>\n            <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n                <span class=\"k\">break<\/span>\n    <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Parent read from pty fd: <\/span><span class=\"si\">{}<\/span><span class=\"s1\">&#39;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"nb\">repr<\/span><span class=\"p\">(<\/span><span class=\"n\">data<\/span><span class=\"p\">)))<\/span>\n    <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Parent wait for child process <\/span><span class=\"si\">{!r}<\/span><span class=\"s1\"> to exit...&#39;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">pid<\/span><span class=\"p\">))<\/span>\n    <span class=\"n\">pid<\/span><span class=\"p\">,<\/span> <span class=\"n\">status<\/span> <span class=\"o\">=<\/span> <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">waitpid<\/span><span class=\"p\">(<\/span><span class=\"n\">pid<\/span><span class=\"p\">,<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n    <span class=\"nb\">print<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Parent exit&#39;<\/span><span class=\"p\">)<\/span>\n<\/pre><\/div>\n<\/td><\/tr><\/table><p>\u57f7\u884c\u4ee5\u4e0a\u7a0b\u5f0f\u78bc\u5f8c\u61c9\u8a72\u6703\u51fa\u73fe\u4ee5\u4e0b\u7d50\u679c (Ubuntu 16.04 with Python 3.5)\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span>$ python3.5 pty_fork_test.py\nParent <span class=\"nb\">wait<\/span> <span class=\"k\">for<\/span> <span class=\"m\">1<\/span> sec <span class=\"k\">then<\/span> write 0x03...\nParent write 0x03\nParent <span class=\"nb\">read<\/span> from pty fd: b<span class=\"s1\">&#39;Hello World!\\r\\n^CSIGINT Received!\\r\\n&#39;<\/span>\nParent <span class=\"nb\">wait<\/span> <span class=\"k\">for<\/span> child process <span class=\"m\">17676<\/span> to exit...\nParent <span class=\"nb\">exit<\/span>\n<\/pre><\/div>\n<p>\u9019\u6bb5\u7a0b\u5f0f\u78bc\u5c55\u793a\u4e86\u7236\u884c\u7a0b\u5982\u4f55\u4f7f\u7528 <tt class=\"docutils literal\">pty.fork()<\/tt>\n\u56de\u50b3\u7684 file descriptor \u8207\u5b50\u884c\u7a0b\u6e9d\u901a\u7684\u904e\u7a0b\uff1a<\/p>\n<ol class=\"arabic simple\">\n<li>\u5b50\u884c\u7a0b\u7684 stdout \u9023\u63a5\u5230 slave \u7aef\uff0c\u56e0\u6b64\u5b50\u884c\u7a0b\u5c0d stdout \u5beb\u5165\u7684\u5167\u5bb9\u53ef\u4ee5\u88ab\u7236\u884c\u7a0b\u900f\u904e\u8b80\u53d6 master \u7aef\uff0c\u4e5f\u5c31\u662f <tt class=\"docutils literal\">pty.fork()<\/tt> \u56de\u50b3\u7684 file descriptor\uff0c\u4f86\u63a5\u6536\u3002\u56e0\u6b64\uff0c\u7236\u884c\u7a0b\u80fd\u5920\u8b80\u53d6\u5230\u5b50\u884c\u7a0b\u5c0d stdout \u5beb\u5165\u7684 <tt class=\"docutils literal\">Hello <span class=\"pre\">World!\\n<\/span><\/tt> \u5b57\u4e32\u3002<\/li>\n<li>\u5b50\u884c\u7a0b\u5beb\u5165\u7684 <tt class=\"docutils literal\">Hello World\\n<\/tt> \u5230\u4e86\u7236\u884c\u7a0b\u8b8a\u6210\u4e86 <tt class=\"docutils literal\">Hello World\\r\\n<\/tt> \uff0c\u591a\u4e86\u4e00\u500b <em>Carriage Return<\/em> <tt class=\"docutils literal\">\\r<\/tt> \u5b57\u5143\uff0c\u9019\u662f Line discipline \u6b63\u5728\u4f5c\u7528\u7684\u7d50\u679c\u3002\u9019\u8b49\u660e\u4e86\u4e2d\u9593\u4e26\u4e0d\u662f\u53ea\u6709\u55ae\u7d14\u7684\u8cc7\u6599\u4ea4\u63db\uff0c\u800c\u662f Linux \u7684 TTY \u7cfb\u7d71\u5728\u4f5c\u52d5\u4e2d\u3002<\/li>\n<li>\u7236\u884c\u7a0b\u5c0d file descriptor \u5beb\u5165\u6578\u503c <tt class=\"docutils literal\">0x03<\/tt> \u5f8c\uff0c\u5230\u4e86\u5b50\u884c\u7a0b\u8b8a\u6210\u4e86 SIGINT \u4fe1\u865f\u800c\u88ab Python \u6355\u6349\u70ba <tt class=\"docutils literal\">KeyboardInterrupt<\/tt> \u4f8b\u5916\uff0c\u63a5\u8457\u5b50\u884c\u7a0b\u5c0d stdout \u5beb\u5165 <tt class=\"docutils literal\">SIGINT <span class=\"pre\">Received!\\n<\/span><\/tt> \u5b57\u4e32\uff0c\u7136\u5f8c\u88ab\u7236\u884c\u7a0b\u8b80\u53d6\u4e26\u986f\u793a\u70ba <tt class=\"docutils literal\">^CSIGINT <span class=\"pre\">Received!\\r\\n<\/span><\/tt> \u3002\u9019\u4e5f\u8b49\u660e\u4e86 Line discipline \u4ee5\u53ca TTY \u7cfb\u7d71\u7684\u4f5c\u7528\u3002<\/li>\n<\/ol>\n<p>\u4ee5\u4e0a\u662f\u5c0d <tt class=\"docutils literal\">pty.fork()<\/tt> \u505a\u7684\u7c21\u55ae\u6e2c\u8a66\u3002\u63a5\u4e0b\u4f86\u4f86\u5be6\u505a\u5566\uff01<\/p>\n<\/div>\n<div class=\"section\" id=\"the-mp3-player-powered-by-madplay\">\n<h2>The MP3 player powered by madplay<\/h2>\n<p>\u91dd\u5c0d\u300c\u4f7f\u7528 Python + madplay \u63a7\u5236 MP3 \u6a94\u6848\u7684\u64ad\u653e\u300d\u9019\u4ef6\u4e8b\uff0c\u53ef\u4ee5\u9019\u6a23\u505a\uff1a<\/p>\n<ol class=\"arabic simple\">\n<li>\u4f7f\u7528 <tt class=\"docutils literal\">pty.fork()<\/tt> Fork \u51fa\u4e00\u500b\u5b50\u884c\u7a0b\uff0c\u8b93\u8a72\u5b50\u884c\u7a0b\u4f7f\u7528 Python \u7684 <tt class=\"docutils literal\">os.exec*<\/tt>\n\u7cfb\u5217\u51fd\u5f0f\u4f86\u555f\u52d5 madplay \u53d6\u4ee3\u76ee\u524d\u884c\u7a0b\uff0c\u4e26\u64ad\u653e\u4e00\u500b MP3 \u6a94\u6848\u3002<\/li>\n<li>\u7236\u884c\u7a0b\u5229\u7528 <tt class=\"docutils literal\">pty.fork()<\/tt> \u53d6\u5f97\u7684 file descriptor \u4f86\u63a7\u5236\u5b50\u884c\u7a0b\u7684\u7d42\u7aef\u88dd\u7f6e\uff0c\u9032\u800c\u63a7\u5236 madplay\u3002<\/li>\n<li>\u6c92\u4e8b\u5f97\u6e05\u6e05 file descriptor \u7684 receive buffer\uff0c\u907f\u514d\u8b93\u5b50\u884c\u7a0b\u6301\u7e8c\u5beb\u5165\u800c\u585e\u7206 buffer\uff08\u9019\u662f\u6211\u81ea\u5df1\u60f3\u7684\uff0c\u5be6\u969b\u4e0a\u53ef\u80fd\u4e0d\u7528\uff0c\u4f46\u8cb7\u500b\u4fdd\u96aa\u561b\uff09\u3002<\/li>\n<li>\u5b50\u884c\u7a0b\u7684 madplay \u64ad\u653e\u5b8c\u7562\u5f8c\u5fc5\u9808\u901a\u77e5\u7236\u884c\u7a0b\uff0c\u9019\u6642\u7236\u884c\u7a0b\u5fc5\u9808\u4f7f\u7528 <tt class=\"docutils literal\">os.wait<\/tt> \u6216 <tt class=\"docutils literal\">os.waitpid<\/tt> \u4f86\u6536\u62fe\u5b50\u884c\u7a0b\uff0c\u5426\u5247\u6703\u7522\u751f\u5f4a\u5c4d\u884c\u7a0b\u3002<\/li>\n<\/ol>\n<p>\u4e0d\u56c9\u55e6\uff0c\u76f4\u63a5\u4e0a code\uff1a<\/p>\n<table class=\"highlighttable\"><tr><td class=\"linenos\"><div class=\"linenodiv\"><pre><span class=\"normal\">  1<\/span>\n<span class=\"normal\">  2<\/span>\n<span class=\"normal\">  3<\/span>\n<span class=\"normal\">  4<\/span>\n<span class=\"normal\">  5<\/span>\n<span class=\"normal\">  6<\/span>\n<span class=\"normal\">  7<\/span>\n<span class=\"normal\">  8<\/span>\n<span class=\"normal\">  9<\/span>\n<span class=\"normal\"> 10<\/span>\n<span class=\"normal\"> 11<\/span>\n<span class=\"normal\"> 12<\/span>\n<span class=\"normal\"> 13<\/span>\n<span class=\"normal\"> 14<\/span>\n<span class=\"normal\"> 15<\/span>\n<span class=\"normal\"> 16<\/span>\n<span class=\"normal\"> 17<\/span>\n<span class=\"normal\"> 18<\/span>\n<span class=\"normal\"> 19<\/span>\n<span class=\"normal\"> 20<\/span>\n<span class=\"normal\"> 21<\/span>\n<span class=\"normal\"> 22<\/span>\n<span class=\"normal\"> 23<\/span>\n<span class=\"normal\"> 24<\/span>\n<span class=\"normal\"> 25<\/span>\n<span class=\"normal\"> 26<\/span>\n<span class=\"normal\"> 27<\/span>\n<span class=\"normal\"> 28<\/span>\n<span class=\"normal\"> 29<\/span>\n<span class=\"normal\"> 30<\/span>\n<span class=\"normal\"> 31<\/span>\n<span class=\"normal\"> 32<\/span>\n<span class=\"normal\"> 33<\/span>\n<span class=\"normal\"> 34<\/span>\n<span class=\"normal\"> 35<\/span>\n<span class=\"normal\"> 36<\/span>\n<span class=\"normal\"> 37<\/span>\n<span class=\"normal\"> 38<\/span>\n<span class=\"normal\"> 39<\/span>\n<span class=\"normal\"> 40<\/span>\n<span class=\"normal\"> 41<\/span>\n<span class=\"normal\"> 42<\/span>\n<span class=\"normal\"> 43<\/span>\n<span class=\"normal\"> 44<\/span>\n<span class=\"normal\"> 45<\/span>\n<span class=\"normal\"> 46<\/span>\n<span class=\"normal\"> 47<\/span>\n<span class=\"normal\"> 48<\/span>\n<span class=\"normal\"> 49<\/span>\n<span class=\"normal\"> 50<\/span>\n<span class=\"normal\"> 51<\/span>\n<span class=\"normal\"> 52<\/span>\n<span class=\"normal\"> 53<\/span>\n<span class=\"normal\"> 54<\/span>\n<span class=\"normal\"> 55<\/span>\n<span class=\"normal\"> 56<\/span>\n<span class=\"normal\"> 57<\/span>\n<span class=\"normal\"> 58<\/span>\n<span class=\"normal\"> 59<\/span>\n<span class=\"normal\"> 60<\/span>\n<span class=\"normal\"> 61<\/span>\n<span class=\"normal\"> 62<\/span>\n<span class=\"normal\"> 63<\/span>\n<span class=\"normal\"> 64<\/span>\n<span class=\"normal\"> 65<\/span>\n<span class=\"normal\"> 66<\/span>\n<span class=\"normal\"> 67<\/span>\n<span class=\"normal\"> 68<\/span>\n<span class=\"normal\"> 69<\/span>\n<span class=\"normal\"> 70<\/span>\n<span class=\"normal\"> 71<\/span>\n<span class=\"normal\"> 72<\/span>\n<span class=\"normal\"> 73<\/span>\n<span class=\"normal\"> 74<\/span>\n<span class=\"normal\"> 75<\/span>\n<span class=\"normal\"> 76<\/span>\n<span class=\"normal\"> 77<\/span>\n<span class=\"normal\"> 78<\/span>\n<span class=\"normal\"> 79<\/span>\n<span class=\"normal\"> 80<\/span>\n<span class=\"normal\"> 81<\/span>\n<span class=\"normal\"> 82<\/span>\n<span class=\"normal\"> 83<\/span>\n<span class=\"normal\"> 84<\/span>\n<span class=\"normal\"> 85<\/span>\n<span class=\"normal\"> 86<\/span>\n<span class=\"normal\"> 87<\/span>\n<span class=\"normal\"> 88<\/span>\n<span class=\"normal\"> 89<\/span>\n<span class=\"normal\"> 90<\/span>\n<span class=\"normal\"> 91<\/span>\n<span class=\"normal\"> 92<\/span>\n<span class=\"normal\"> 93<\/span>\n<span class=\"normal\"> 94<\/span>\n<span class=\"normal\"> 95<\/span>\n<span class=\"normal\"> 96<\/span>\n<span class=\"normal\"> 97<\/span>\n<span class=\"normal\"> 98<\/span>\n<span class=\"normal\"> 99<\/span>\n<span class=\"normal\">100<\/span>\n<span class=\"normal\">101<\/span>\n<span class=\"normal\">102<\/span>\n<span class=\"normal\">103<\/span>\n<span class=\"normal\">104<\/span>\n<span class=\"normal\">105<\/span>\n<span class=\"normal\">106<\/span>\n<span class=\"normal\">107<\/span>\n<span class=\"normal\">108<\/span>\n<span class=\"normal\">109<\/span>\n<span class=\"normal\">110<\/span>\n<span class=\"normal\">111<\/span>\n<span class=\"normal\">112<\/span>\n<span class=\"normal\">113<\/span>\n<span class=\"normal\">114<\/span>\n<span class=\"normal\">115<\/span>\n<span class=\"normal\">116<\/span>\n<span class=\"normal\">117<\/span>\n<span class=\"normal\">118<\/span>\n<span class=\"normal\">119<\/span>\n<span class=\"normal\">120<\/span>\n<span class=\"normal\">121<\/span><\/pre><\/div><\/td><td class=\"code\"><div class=\"highlight\"><pre><span><\/span><span class=\"kn\">import<\/span> <span class=\"nn\">logging<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">select<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">signal<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">pty<\/span>\n<span class=\"kn\">import<\/span> <span class=\"nn\">os<\/span>\n\n\n<span class=\"n\">logger<\/span> <span class=\"o\">=<\/span> <span class=\"n\">logging<\/span><span class=\"o\">.<\/span><span class=\"n\">getLogger<\/span><span class=\"p\">(<\/span><span class=\"vm\">__name__<\/span><span class=\"p\">)<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">Error<\/span><span class=\"p\">(<\/span><span class=\"ne\">Exception<\/span><span class=\"p\">):<\/span>\n    <span class=\"sd\">&quot;&quot;&quot;Base error&quot;&quot;&quot;<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">ReadTimeout<\/span><span class=\"p\">(<\/span><span class=\"n\">Error<\/span><span class=\"p\">):<\/span>\n    <span class=\"sd\">&quot;&quot;&quot;Polling timeout&quot;&quot;&quot;<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">PlayerState<\/span><span class=\"p\">(<\/span><span class=\"nb\">object<\/span><span class=\"p\">):<\/span>\n    <span class=\"sd\">&quot;&quot;&quot;The state of the player&quot;&quot;&quot;<\/span>\n    <span class=\"n\">PLAY<\/span> <span class=\"o\">=<\/span> <span class=\"s1\">&#39;play&#39;<\/span>\n    <span class=\"n\">PAUSE<\/span> <span class=\"o\">=<\/span> <span class=\"s1\">&#39;pause&#39;<\/span>\n    <span class=\"n\">STOP<\/span> <span class=\"o\">=<\/span> <span class=\"s1\">&#39;stop&#39;<\/span>\n\n\n<span class=\"k\">class<\/span> <span class=\"nc\">Mp3FilePlayer<\/span><span class=\"p\">(<\/span><span class=\"nb\">object<\/span><span class=\"p\">):<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"fm\">__init__<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">file_path<\/span><span class=\"p\">):<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">file_path<\/span> <span class=\"o\">=<\/span> <span class=\"n\">file_path<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">player_state<\/span> <span class=\"o\">=<\/span> <span class=\"n\">PlayerState<\/span><span class=\"o\">.<\/span><span class=\"n\">STOP<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_tty_fd<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">None<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_pid<\/span> <span class=\"o\">=<\/span> <span class=\"kc\">None<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">poller<\/span> <span class=\"o\">=<\/span> <span class=\"n\">select<\/span><span class=\"o\">.<\/span><span class=\"n\">poll<\/span><span class=\"p\">()<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">_start_play<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"sd\">&quot;&quot;&quot;This method forks a child process and start exec &#39;madplay&#39; to play<\/span>\n<span class=\"sd\">            the mp3 file. Since &#39;madplay&#39; can ONLY be controlled by tty, we have<\/span>\n<span class=\"sd\">            to use pty.fork and use the return fd in the parent process (which<\/span>\n<span class=\"sd\">            connects the child&#39;s controlling terminal) to control the playback.<\/span>\n<span class=\"sd\">        &quot;&quot;&quot;<\/span>\n        <span class=\"c1\"># Register SIGCHLD to get notified when the child process terminated<\/span>\n        <span class=\"n\">signal<\/span><span class=\"o\">.<\/span><span class=\"n\">signal<\/span><span class=\"p\">(<\/span><span class=\"n\">signal<\/span><span class=\"o\">.<\/span><span class=\"n\">SIGCHLD<\/span><span class=\"p\">,<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">_sigchld_handler<\/span><span class=\"p\">)<\/span>\n\n        <span class=\"n\">pid<\/span><span class=\"p\">,<\/span> <span class=\"n\">fd<\/span> <span class=\"o\">=<\/span> <span class=\"n\">pty<\/span><span class=\"o\">.<\/span><span class=\"n\">fork<\/span><span class=\"p\">()<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">pid<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">0<\/span><span class=\"p\">:<\/span>\n            <span class=\"c1\"># Child process. Exec madplay<\/span>\n            <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">execl<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/usr\/bin\/madplay&#39;<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;--tty-control&#39;<\/span><span class=\"p\">,<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">file_path<\/span><span class=\"p\">)<\/span>\n        <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n            <span class=\"c1\"># Parent process<\/span>\n            <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_tty_fd<\/span> <span class=\"o\">=<\/span> <span class=\"n\">fd<\/span>\n            <span class=\"n\">logger<\/span><span class=\"o\">.<\/span><span class=\"n\">debug<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Forked child TTY fd: <\/span><span class=\"si\">{}<\/span><span class=\"s1\">&#39;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_tty_fd<\/span><span class=\"p\">))<\/span>\n            <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_pid<\/span> <span class=\"o\">=<\/span> <span class=\"n\">pid<\/span>\n            <span class=\"n\">logger<\/span><span class=\"o\">.<\/span><span class=\"n\">debug<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Forked child PID: <\/span><span class=\"si\">{}<\/span><span class=\"s1\">&#39;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_pid<\/span><span class=\"p\">))<\/span>\n            <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">_clear_tty<\/span><span class=\"p\">()<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">_read_tty<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">n<\/span><span class=\"p\">,<\/span> <span class=\"n\">timeout<\/span><span class=\"o\">=<\/span><span class=\"kc\">None<\/span><span class=\"p\">):<\/span>\n        <span class=\"sd\">&quot;&quot;&quot;Read the TTY fd by n bytes or raise ReadTimeout if reached specified timeout.<\/span>\n<span class=\"sd\">            The timeout value is in milliseconds.<\/span>\n<span class=\"sd\">        &quot;&quot;&quot;<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">poller<\/span><span class=\"o\">.<\/span><span class=\"n\">register<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_tty_fd<\/span><span class=\"p\">,<\/span> <span class=\"n\">select<\/span><span class=\"o\">.<\/span><span class=\"n\">POLLIN<\/span><span class=\"p\">)<\/span>\n        <span class=\"n\">events<\/span> <span class=\"o\">=<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">poller<\/span><span class=\"o\">.<\/span><span class=\"n\">poll<\/span><span class=\"p\">(<\/span><span class=\"n\">timeout<\/span><span class=\"p\">)<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">poller<\/span><span class=\"o\">.<\/span><span class=\"n\">unregister<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_tty_fd<\/span><span class=\"p\">)<\/span>  <span class=\"c1\"># Immediately after the polling<\/span>\n        <span class=\"k\">if<\/span> <span class=\"ow\">not<\/span> <span class=\"n\">events<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">raise<\/span> <span class=\"n\">ReadTimeout<\/span>\n\n        <span class=\"k\">assert<\/span> <span class=\"nb\">len<\/span><span class=\"p\">(<\/span><span class=\"n\">events<\/span><span class=\"p\">)<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">1<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;Number of polled events != 1&#39;<\/span>\n\n        <span class=\"n\">fd<\/span><span class=\"p\">,<\/span> <span class=\"n\">event<\/span> <span class=\"o\">=<\/span> <span class=\"n\">events<\/span><span class=\"p\">[<\/span><span class=\"mi\">0<\/span><span class=\"p\">]<\/span>\n        <span class=\"k\">if<\/span> <span class=\"n\">event<\/span> <span class=\"o\">!=<\/span> <span class=\"n\">select<\/span><span class=\"o\">.<\/span><span class=\"n\">POLLIN<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">raise<\/span> <span class=\"n\">Error<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Unexpected polled event: <\/span><span class=\"si\">{}<\/span><span class=\"s1\">&#39;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"n\">event<\/span><span class=\"p\">))<\/span>\n        <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">data<\/span> <span class=\"o\">=<\/span> <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">read<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_tty_fd<\/span><span class=\"p\">,<\/span> <span class=\"n\">n<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">return<\/span> <span class=\"n\">data<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">_clear_tty<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"sd\">&quot;&quot;&quot;Clearing the TTY fd. Preventing the receiving buffer to overflow.&quot;&quot;&quot;<\/span>\n        <span class=\"k\">while<\/span> <span class=\"kc\">True<\/span><span class=\"p\">:<\/span>\n            <span class=\"c1\"># Keep reading until timeout, which means nothing more to read.<\/span>\n            <span class=\"k\">try<\/span><span class=\"p\">:<\/span>\n                <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">_read_tty<\/span><span class=\"p\">(<\/span><span class=\"mi\">1024<\/span><span class=\"p\">,<\/span> <span class=\"n\">timeout<\/span><span class=\"o\">=<\/span><span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"k\">except<\/span> <span class=\"n\">ReadTimeout<\/span><span class=\"p\">:<\/span>\n                <span class=\"k\">return<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">_sigchld_handler<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">,<\/span> <span class=\"n\">signum<\/span><span class=\"p\">,<\/span> <span class=\"n\">frame<\/span><span class=\"p\">):<\/span>\n        <span class=\"sd\">&quot;&quot;&quot;Handler function of SIGCHLD&quot;&quot;&quot;<\/span>\n        <span class=\"n\">logger<\/span><span class=\"o\">.<\/span><span class=\"n\">info<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;SIGCHLD signal received.&#39;<\/span><span class=\"p\">)<\/span>\n        <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">stop<\/span><span class=\"p\">()<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">play<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"sd\">&quot;&quot;&quot;Start the playback or resume from pausing&quot;&quot;&quot;<\/span>\n        <span class=\"k\">if<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">player_state<\/span> <span class=\"o\">==<\/span> <span class=\"n\">PlayerState<\/span><span class=\"o\">.<\/span><span class=\"n\">STOP<\/span><span class=\"p\">:<\/span>\n            <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">_start_play<\/span><span class=\"p\">()<\/span>\n            <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">player_state<\/span> <span class=\"o\">=<\/span> <span class=\"n\">PlayerState<\/span><span class=\"o\">.<\/span><span class=\"n\">PLAY<\/span>\n        <span class=\"k\">elif<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">player_state<\/span> <span class=\"o\">==<\/span> <span class=\"n\">PlayerState<\/span><span class=\"o\">.<\/span><span class=\"n\">PAUSE<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_tty_fd<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;p&#39;<\/span><span class=\"p\">)<\/span>\n            <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">_clear_tty<\/span><span class=\"p\">()<\/span>\n            <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">player_state<\/span> <span class=\"o\">=<\/span> <span class=\"n\">PlayerState<\/span><span class=\"o\">.<\/span><span class=\"n\">PLAY<\/span>\n        <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">pass<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">pause<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"sd\">&quot;&quot;&quot;Pause the playback&quot;&quot;&quot;<\/span>\n        <span class=\"k\">if<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">player_state<\/span> <span class=\"o\">==<\/span> <span class=\"n\">PlayerState<\/span><span class=\"o\">.<\/span><span class=\"n\">PLAY<\/span><span class=\"p\">:<\/span>\n            <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">write<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_tty_fd<\/span><span class=\"p\">,<\/span> <span class=\"s1\">&#39;p&#39;<\/span><span class=\"p\">)<\/span>\n            <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">_clear_tty<\/span><span class=\"p\">()<\/span>\n            <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">player_state<\/span> <span class=\"o\">=<\/span> <span class=\"n\">PlayerState<\/span><span class=\"o\">.<\/span><span class=\"n\">PAUSE<\/span>\n        <span class=\"k\">else<\/span><span class=\"p\">:<\/span>\n            <span class=\"k\">pass<\/span>\n\n    <span class=\"k\">def<\/span> <span class=\"nf\">stop<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"p\">):<\/span>\n        <span class=\"sd\">&quot;&quot;&quot;Stop the playback. This will stop the child process.&quot;&quot;&quot;<\/span>\n        <span class=\"k\">if<\/span> <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">player_state<\/span> <span class=\"o\">!=<\/span> <span class=\"n\">PlayerState<\/span><span class=\"o\">.<\/span><span class=\"n\">STOP<\/span><span class=\"p\">:<\/span>\n            <span class=\"c1\"># Unregister the signal (set to SIG_DFL) to prevent recusively calling stop()<\/span>\n            <span class=\"n\">signal<\/span><span class=\"o\">.<\/span><span class=\"n\">signal<\/span><span class=\"p\">(<\/span><span class=\"n\">signal<\/span><span class=\"o\">.<\/span><span class=\"n\">SIGCHLD<\/span><span class=\"p\">,<\/span> <span class=\"n\">signal<\/span><span class=\"o\">.<\/span><span class=\"n\">SIG_DFL<\/span><span class=\"p\">)<\/span>\n\n            <span class=\"n\">logger<\/span><span class=\"o\">.<\/span><span class=\"n\">debug<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Kill pid <\/span><span class=\"si\">{}<\/span><span class=\"s1\">&#39;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_pid<\/span><span class=\"p\">))<\/span>\n            <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">kill<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_pid<\/span><span class=\"p\">,<\/span> <span class=\"n\">signal<\/span><span class=\"o\">.<\/span><span class=\"n\">SIGTERM<\/span><span class=\"p\">)<\/span>\n            <span class=\"n\">logger<\/span><span class=\"o\">.<\/span><span class=\"n\">debug<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Wait pid <\/span><span class=\"si\">{}<\/span><span class=\"s1\">&#39;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_pid<\/span><span class=\"p\">))<\/span>\n            <span class=\"n\">os<\/span><span class=\"o\">.<\/span><span class=\"n\">waitpid<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_pid<\/span><span class=\"p\">,<\/span> <span class=\"mi\">0<\/span><span class=\"p\">)<\/span>\n            <span class=\"n\">logger<\/span><span class=\"o\">.<\/span><span class=\"n\">debug<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;Child process <\/span><span class=\"si\">{}<\/span><span class=\"s1\"> died.&#39;<\/span><span class=\"o\">.<\/span><span class=\"n\">format<\/span><span class=\"p\">(<\/span><span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">child_pid<\/span><span class=\"p\">))<\/span>\n            <span class=\"bp\">self<\/span><span class=\"o\">.<\/span><span class=\"n\">player_state<\/span> <span class=\"o\">=<\/span> <span class=\"n\">PlayerState<\/span><span class=\"o\">.<\/span><span class=\"n\">STOP<\/span>\n<\/pre><\/div>\n<\/td><\/tr><\/table><p>\u9019\u6bb5\u7a0b\u5f0f\u78bc\u5b9a\u7fa9\u4e86\u985e\u5225 <tt class=\"docutils literal\">Mp3FilePlayer<\/tt> \u4f86\u63a7\u5236\u64ad\u653e\u3002\u4ee5\u4e0b\u662f\u5e7e\u500b\u91cd\u9ede\uff1a<\/p>\n<ol class=\"arabic simple\">\n<li><tt class=\"docutils literal\">Mp3FilePlayer<\/tt> \u5b9a\u7fa9\u4e86 <tt class=\"docutils literal\">play<\/tt> \uff0c <tt class=\"docutils literal\">pause<\/tt> \u53ca <tt class=\"docutils literal\">stop<\/tt>\n\u9019\u4e09\u500b\u65b9\u6cd5\u4f86\u63a7\u5236 MP3 \u6a94\u6848\u7684\u64ad\u653e\u3001\u66ab\u505c\u53ca\u505c\u6b62\u3002<\/li>\n<li><tt class=\"docutils literal\">stop<\/tt> \u65b9\u6cd5\u6703\u85c9\u7531\u9001\u51fa SIGTERM \u4fe1\u865f\u4f86\u505c\u6389\u5b50\u884c\u7a0b\uff0c\u4e26\u4f7f\u7528 <tt class=\"docutils literal\">waitpid()<\/tt>\n\u4f86\u6536\u62fe\u5584\u5f8c\u3002<\/li>\n<li>\u4f7f\u7528 <tt class=\"docutils literal\">select.poll()<\/tt> \uff0c\u800c\u975e\u76f4\u63a5\u4f7f\u7528 <tt class=\"docutils literal\">os.read()<\/tt>\n\u76f4\u63a5\u8b80\u53d6 file descriptor\u3002\u539f\u56e0\u662f\u6211\u9700\u8981\u5c0d\u8b80\u53d6\u9019\u4ef6\u4e8b\u8a2d\u5b9a timeout\uff0c\u800c <tt class=\"docutils literal\">os.read()<\/tt> \u9019\u500b\u51fd\u5f0f\u7121\u6cd5\u505a\u5230\u3002<\/li>\n<li>\u8a2d\u5b9a <tt class=\"docutils literal\">Mp3FilePlayer._sigchld_handler<\/tt> \u65b9\u6cd5\u7576 SIGCHLD \u4fe1\u865f\u7684\u8655\u7406\u51fd\u5f0f\uff0c\u4ee5\u4fbf\u5728 madplay \u64ad\u653e\u5b8c MP3 \u6a94\u5f8c\uff0c\u8b93\u7236\u884c\u7a0b\u547c\u53eb <tt class=\"docutils literal\">stop<\/tt> \u65b9\u6cd5\u4f86\u6536\u62fe\u5b50\u884c\u7a0b\uff0c\u907f\u514d\u7522\u751f\u5f4a\u5c4d\u884c\u7a0b\u3002<\/li>\n<\/ol>\n<p><tt class=\"docutils literal\">Mp3FilePlayer<\/tt> \u53ef\u4ee5\u9019\u6a23\u4f7f\u7528\uff1a<\/p>\n<div class=\"highlight\"><pre><span><\/span><span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"kn\">from<\/span> <span class=\"nn\">mp3_player<\/span> <span class=\"kn\">import<\/span> <span class=\"n\">Mp3FilePlayer<\/span>\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"n\">p<\/span> <span class=\"o\">=<\/span> <span class=\"n\">Mp3FilePlayer<\/span><span class=\"p\">(<\/span><span class=\"s1\">&#39;\/tmp\/test.mp3&#39;<\/span><span class=\"p\">)<\/span>\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"n\">p<\/span><span class=\"o\">.<\/span><span class=\"n\">play<\/span><span class=\"p\">()<\/span>\n<span class=\"c1\"># The music should be started. The play method return immediately.<\/span>\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"n\">p<\/span><span class=\"o\">.<\/span><span class=\"n\">pause<\/span><span class=\"p\">()<\/span>\n<span class=\"c1\"># The music should be paused now. The pause method also return<\/span>\n<span class=\"c1\"># immediately.<\/span>\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"n\">p<\/span><span class=\"o\">.<\/span><span class=\"n\">play<\/span><span class=\"p\">()<\/span>\n<span class=\"c1\"># The playback should be resumed from where it was paused.<\/span>\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"n\">p<\/span><span class=\"o\">.<\/span><span class=\"n\">stop<\/span><span class=\"p\">()<\/span>\n<span class=\"c1\"># The music should be stopped now.<\/span>\n<span class=\"o\">&gt;&gt;&gt;<\/span> <span class=\"n\">p<\/span><span class=\"o\">.<\/span><span class=\"n\">play<\/span><span class=\"p\">()<\/span>\n<span class=\"c1\"># The music should be started from the beginning.<\/span>\n<\/pre><\/div>\n<\/div>\n<div class=\"section\" id=\"conclusion\">\n<h2>Conclusion<\/h2>\n<p>\u7d93\u904e\u9019\u5e7e\u5929\u7684\u7814\u7a76\u7e3d\u7b97\u7a0d\u5fae\u7406\u89e3\u4e86 TTY \u9019\u6771\u897f\uff0c\u4e5f\u7406\u89e3\u4e86\u5982\u4f55\u4f7f\u7528 Python \u7684 pty \u6a21\u7d44\u4f86\u63a7\u5236\u5176\u4ed6\u884c\u7a0b\u7684\u7d42\u7aef\u3002\u5e0c\u671b\u9019\u7bc7\u6587\u80fd\u5e6b\u52a9\u5927\u5bb6\ud83c\udf89<\/p>\n<\/div>\n<div class=\"section\" id=\"references\">\n<h2>References<\/h2>\n<ul class=\"simple\">\n<li><a class=\"reference external\" href=\"http:\/\/www.linusakesson.net\/programming\/tty\/\">The TTY demystified<\/a><\/li>\n<li><a class=\"reference external\" href=\"https:\/\/unix.stackexchange.com\/q\/117981\">What are the responsibilities of each Pseudo-Terminal (PTY) component\n(software, master side, slave side)?<\/a><\/li>\n<li><a class=\"reference external\" href=\"http:\/\/olvaffe.blogspot.tw\/2009\/01\/console-io.html\">\u4e00\u5343\u96f6\u4e00\u591c\u4e4b Console I\/O<\/a><\/li>\n<li><a class=\"reference external\" href=\"http:\/\/zwai.pixnet.net\/blog\/post\/24326951-linux-tty-driver---linux-tty-%E9%A9%85%E5%8B%95%E7%A8%8B%E5%BC%8F\">Linux TTY Driver\u200a\u2014\u200aLinux TTY \u9a45\u52d5\u7a0b\u5f0f<\/a><\/li>\n<li><a class=\"reference external\" href=\"https:\/\/utcc.utoronto.ca\/~cks\/space\/blog\/unix\/TypingEOFEffects\">What typing ^D really does on Unix<\/a><\/li>\n<li><a class=\"reference external\" href=\"http:\/\/www.wowotech.net\/tty_framework\/tty_concept.html\">Linux TTY framework(1)_ \u57fa\u672c\u6982\u5ff5<\/a><\/li>\n<li><a class=\"reference external\" href=\"http:\/\/www.wowotech.net\/tty_framework\/application_view.html\">Linux TTY framework(3)_ \u4ece\u5e94\u7528\u7684\u89d2\u5ea6\u770b TTY \u8bbe\u5907<\/a><\/li>\n<\/ul>\n<\/div>\n","category":[{"@attributes":{"term":"Python"}},{"@attributes":{"term":"Python"}},{"@attributes":{"term":"TTY"}},{"@attributes":{"term":"Linux"}}]}]}