<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>핑 보관 - 하우인포-IT·테크</title>
	<atom:link href="https://howinfo.kr/tag/%ed%95%91/feed/" rel="self" type="application/rss+xml" />
	<link>https://howinfo.kr/tag/핑/</link>
	<description>IT·AI 자동화 &#38; 인프라 전문 블로그 (하우인포)</description>
	<lastBuildDate>Wed, 04 Feb 2026 00:58:25 +0000</lastBuildDate>
	<language>ko-KR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://howinfo.kr/wp-content/uploads/2026/02/cropped-ChatGPT-Image-2026년-2월-12일-오후-05_39_40-32x32.png</url>
	<title>핑 보관 - 하우인포-IT·테크</title>
	<link>https://howinfo.kr/tag/핑/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>파이썬으로 네트워크 ping, portscan 웹 화면에서 체크 프로그램</title>
		<link>https://howinfo.kr/%ed%8c%8c%ec%9d%b4%ec%8d%ac%ec%9c%bc%eb%a1%9c-%eb%84%a4%ed%8a%b8%ec%9b%8c%ed%81%ac-ping-portscan-%ec%9b%b9-%ed%99%94%eb%a9%b4%ec%97%90%ec%84%9c-%ec%b2%b4%ed%81%ac-%ed%94%84%eb%a1%9c%ea%b7%b8/</link>
					<comments>https://howinfo.kr/%ed%8c%8c%ec%9d%b4%ec%8d%ac%ec%9c%bc%eb%a1%9c-%eb%84%a4%ed%8a%b8%ec%9b%8c%ed%81%ac-ping-portscan-%ec%9b%b9-%ed%99%94%eb%a9%b4%ec%97%90%ec%84%9c-%ec%b2%b4%ed%81%ac-%ed%94%84%eb%a1%9c%ea%b7%b8/#respond</comments>
		
		<dc:creator><![CDATA[hong]]></dc:creator>
		<pubDate>Tue, 03 Feb 2026 09:56:06 +0000</pubDate>
				<category><![CDATA[개발·코딩]]></category>
		<category><![CDATA[네트워크]]></category>
		<category><![CDATA[파이썬]]></category>
		<category><![CDATA[포트스캔]]></category>
		<category><![CDATA[프로그램]]></category>
		<category><![CDATA[핑]]></category>
		<guid isPermaLink="false">https://howinfo.kr/?p=1223</guid>

					<description><![CDATA[<p>파이썬으로 PING , PORTSCAN 웹 화면에서 테스트 하기 ​ 포트는 9999 FLASK를 설치 PIP INSTALL FLASK ​ 아래 파일. CSV,...</p>
<p>게시물 <a href="https://howinfo.kr/%ed%8c%8c%ec%9d%b4%ec%8d%ac%ec%9c%bc%eb%a1%9c-%eb%84%a4%ed%8a%b8%ec%9b%8c%ed%81%ac-ping-portscan-%ec%9b%b9-%ed%99%94%eb%a9%b4%ec%97%90%ec%84%9c-%ec%b2%b4%ed%81%ac-%ed%94%84%eb%a1%9c%ea%b7%b8/">파이썬으로 네트워크 ping, portscan 웹 화면에서 체크 프로그램</a>이 <a href="https://howinfo.kr">하우인포-IT·테크</a>에 처음 등장했습니다.</p>
]]></description>
										<content:encoded><![CDATA[
<p id="SE-6dce18c2-278e-47ab-939c-6c0720b30031">파이썬으로 PING , PORTSCAN 웹 화면에서 테스트 하기</p>



<p id="SE-8a0b1a42-16a6-4704-9426-3cc10c3814a3">​</p>



<p id="SE-aa2efb20-0f62-4d06-be6c-3f6d4e0b73f8">포트는 9999</p>



<p id="SE-0be077fa-e882-48c8-b86b-8541bf7433af">FLASK를 설치 PIP INSTALL FLASK</p>



<p id="SE-e758dacf-de70-4ede-89e9-a34dcfc2d17f">​</p>



<p id="SE-9a14dc4c-a7de-4153-a8e8-0aad41c6f0e1">아래 파일.</p>



<p id="SE-383ede7b-1dbb-40c5-a47b-617a6e0615a0">CSV, JSON 파일 다운로드 받기 .</p>



<p id="SE-1411fe46-919a-421d-8b46-22459c9a4673">1-포트 설정하여 스캔</p>



<p id="SE-2db68a39-9880-4059-9569-4eb424bc1c48">스캔도 멀티테스킹 처리 .</p>



<p></p>



<p>============ 소스 ===================</p>



<p id="SE-a52f7786-43ba-4ae3-8abb-af611b33ddf2">import subprocess</p>



<p id="SE-11e820d4-b6cb-4713-a5f3-7f820bb8843c">import platform</p>



<p id="SE-6183ba10-96fc-4560-b2c6-4053d277cb61">import re</p>



<p id="SE-d799e250-23ed-4d0f-9e31-c8b608912741">import time</p>



<p id="SE-a76baae9-4e71-4939-885e-5669b3df1abe">import socket</p>



<p id="SE-69875a97-4b82-4953-865c-f26d8443863c">from datetime import datetime</p>



<p id="SE-920646c9-7316-4e4e-873d-2338fe6df435">​</p>



<p id="SE-8ec8de03-0fda-4956-886b-61358148b369">from flask import Flask, request, render_template_string, jsonify</p>



<p id="SE-df3270e5-7d47-4771-ba6e-6c1c6eb28294">​</p>



<p id="SE-551aed4a-77ca-46d7-8f52-5ea399cbd95e">app = Flask(__name__)</p>



<p id="SE-1cee548b-6eda-4d5b-8a39-8d16557c0009">​</p>



<p id="SE-5b2f5b8f-18dc-4b81-9073-5ff2994416bd"># &#8212;&#8212;&#8212;&#8212;&#8212;- HTML 템플릿 &#8212;&#8212;&#8212;&#8212;&#8212;-</p>



<p id="SE-c5f49dfb-d8f4-46f8-95cc-b6a55b26d95d">PAGE_TEMPLATE = &#8220;&#8221;&#8221;</p>



<p id="SE-5b3cfa20-5950-4e55-8cdb-ad06ba98f6c5">&lt;!doctype html&gt;</p>



<p id="SE-77973741-61ff-4544-95f8-e053ebb14735">&lt;html lang=&#8221;ko&#8221;&gt;</p>



<p id="SE-8e873eda-277e-4c04-99a4-8b909d5e7cc8">&lt;head&gt;</p>



<p id="SE-2a39e35e-5568-4e7b-a305-4195575b6d6a">&lt;meta charset=&#8221;utf-8&#8243;&gt;</p>



<p id="SE-69f34426-6d93-4535-9415-f3792bdd43be">&lt;title&gt;Ping + Port Scan 테스트&lt;/title&gt;</p>



<p id="SE-902091d9-4f38-4209-9c9c-ad41e4a5ef29">&lt;style&gt;</p>



<p id="SE-a7ff6f3d-f1d8-4e35-b954-7f3b2ab6426d">body {</p>



<p id="SE-8667e2a6-02df-4c75-a1ee-b95cf4c07b27">font-family: Arial, sans-serif;</p>



<p id="SE-fd72f41f-2c42-46e3-a2d2-fd1e0d05cd62">max-width: 900px;</p>



<p id="SE-05f7017d-d6d8-4943-a851-49c43c3726d0">margin: 40px auto;</p>



<p id="SE-2e1664ab-cfbc-4b30-9c59-9eddde3cd8fa">}</p>



<p id="SE-35fd4063-0a94-4359-9ff3-bf52cc680320">h1 {</p>



<p id="SE-12adba14-f2d8-4e3c-bf0a-cfe7bc087e81">text-align: center;</p>



<p id="SE-914fc5f0-911d-448b-aeeb-c1ccb6974923">}</p>



<p id="SE-9a5bf6a9-136b-43b4-8f18-67ad14fc2626">form {</p>



<p id="SE-fb8a1042-23d7-4f5a-ae22-ec1f0de76999">margin-bottom: 20px;</p>



<p id="SE-55ad20e9-3fc3-41ed-bf79-eb0f1d3289a3">text-align: center;</p>



<p id="SE-03b521c8-60aa-4251-8102-fcfff912d6b0">}</p>



<p id="SE-5e3f48f1-2651-4350-a27e-8eb14d618d16">input[type=&#8221;text&#8221;] {</p>



<p id="SE-2a38d11d-762b-45dd-a6b1-8db409f0e082">width: 280px;</p>



<p id="SE-4ab53379-95b1-4e16-83cc-356d732bc81b">padding: 8px;</p>



<p id="SE-46bcb4d5-b624-4fe1-8398-95f555b90601">font-size: 14px;</p>



<p id="SE-b305015b-1ddc-4c61-8ac0-b75dbe5f0c93">}</p>



<p id="SE-febb3ea6-759d-41e6-8cd5-192354a99a4d">input[type=&#8221;number&#8221;] {</p>



<p id="SE-ca6ce5e8-4b32-46fe-a5b6-71e913802088">width: 80px;</p>



<p id="SE-7c11bfdf-b39c-4430-be7b-50878d904f95">padding: 6px;</p>



<p id="SE-dad76c20-8d92-4553-aca3-b27b10333c78">font-size: 14px;</p>



<p id="SE-d6573cd4-1b46-4f5e-b2d1-64fb5d356a86">}</p>



<p id="SE-a2070929-74d3-4c1b-afb9-8fbd2d67afde">input[type=&#8221;submit&#8221;], button {</p>



<p id="SE-dbb2add4-68b4-4cf4-9604-41c6de643cbc">padding: 8px 16px;</p>



<p id="SE-8b7413dc-dd29-4e80-9213-9f6253dea76a">font-size: 14px;</p>



<p id="SE-618a6bbc-3e89-4737-ae43-e3f2f4a6ae55">cursor: pointer;</p>



<p id="SE-7618669d-92f8-4e1e-aa41-0c23e082faf5">margin-left: 6px;</p>



<p id="SE-8941254b-1ad0-4bec-8f1a-a1a46bc35397">}</p>



<p id="SE-e9b7f56c-3398-49c0-bdac-6d3f0eafc0c9">.result-box {</p>



<p id="SE-44403d56-1d4c-4349-9492-23ef81b90a83">border: 1px solid #ddd;</p>



<p id="SE-125da1e8-8532-4e4d-b4f6-649d9413fc02">padding: 15px;</p>



<p id="SE-2da4677c-bf4a-4c49-b2ea-208f29ec6a90">border-radius: 8px;</p>



<p id="SE-fc1f2ee7-e26e-42f6-b6a7-e32f3cb9bb47">background: #f9f9f9;</p>



<p id="SE-dfcedd67-6b0a-4b54-abe2-c8383a53ffb8">margin-top: 20px;</p>



<p id="SE-533da813-9d96-44af-8f49-9ce4fa7e7664">}</p>



<p id="SE-87fff300-9986-4f19-94f0-29ceb9247017">.status-ok {</p>



<p id="SE-132e6b9f-9f96-4878-ab4f-f56682416fcc">color: green;</p>



<p id="SE-51cf2153-15e5-43d1-a162-bedcae874bcf">font-weight: bold;</p>



<p id="SE-6f3d8adc-1dea-4f06-ae43-4f3b7435b31d">}</p>



<p id="SE-52f89ca8-26cb-4915-a5a7-ef6252dec462">.status-fail {</p>



<p id="SE-d781c3e9-9be4-425a-b6ef-d5e858bdf718">color: red;</p>



<p id="SE-4c7c47f3-4fe4-4b67-a029-b625e00f9224">font-weight: bold;</p>



<p id="SE-6c9fdbd8-ab5d-4724-848c-f3b72043e970">}</p>



<p id="SE-4e08bd82-409b-41b2-a19f-e8c9f6e1e72b">pre {</p>



<p id="SE-be74e649-fdeb-44ba-bcd8-b2a3b905f727">background: #222;</p>



<p id="SE-e1a078db-a08f-4c90-91e5-c43264bf0e56">color: #eee;</p>



<p id="SE-edee00da-2b7d-4e77-ba33-a9b232a67d32">padding: 10px;</p>



<p id="SE-966fb216-f5b4-486f-b9a9-72c620f90c4a">overflow-x: auto;</p>



<p id="SE-57a3ff67-c09f-4e2f-81ca-9c6128d047ed">border-radius: 6px;</p>



<p id="SE-4637a0cb-7276-4003-b64a-d01c9fd4deaf">font-size: 12px;</p>



<p id="SE-9d483904-0dce-4ddd-a1b5-dfd586d34557">}</p>



<p id="SE-f6093cd0-67bb-445b-95bf-afec76de685e">table {</p>



<p id="SE-41f10c92-3715-49a5-a26f-5ca447229b5e">border-collapse: collapse;</p>



<p id="SE-043cae21-fdce-45a6-bc4a-2431f6d102f9">width: 100%;</p>



<p id="SE-d372efce-8811-456f-baa0-d5343cfc2998">margin-top: 10px;</p>



<p id="SE-9f05928a-a859-47b2-a748-cbdc86228045">}</p>



<p id="SE-4c716b83-ce87-4b59-86a2-c54630f0685a">table th, table td {</p>



<p id="SE-6c686e96-8138-45fc-99bf-9a8dc6b81f5b">border: 1px solid #ccc;</p>



<p id="SE-2c305b85-3e94-4da0-bd46-5e27a4df0a91">padding: 6px;</p>



<p id="SE-bd486711-d2a8-47a1-8add-2eab65f69381">text-align: center;</p>



<p id="SE-bc5f0998-732e-4c7a-bc30-ff8f18400e8b">font-size: 13px;</p>



<p id="SE-ff17bda9-314b-456b-a2e7-840dc153a418">}</p>



<p id="SE-7e8eb31d-5a66-472f-8059-71ee9b1e74eb">table th {</p>



<p id="SE-33e71083-ee26-41d3-8118-656025c73a94">background: #eee;</p>



<p id="SE-541a6fc7-bd95-4dac-934f-8a9796710206">}</p>



<p id="SE-6ff8a907-ecfb-4483-a533-ad6269c2833c">.small-text {</p>



<p id="SE-7f9db6a3-66ca-4a99-ab68-eda9ada1a18e">font-size: 12px;</p>



<p id="SE-079e5e7d-3d0a-49e0-a3c2-16b3080f2429">color: #666;</p>



<p id="SE-5f730e12-3850-457e-9c5c-9d04eac70899">}</p>



<p id="SE-212b737a-fdb9-4c91-b87e-a3c2f3ea7338">#continuous-area {</p>



<p id="SE-efa1204a-e569-4e60-aecf-70a5334bd99b">margin-top: 20px;</p>



<p id="SE-594194fb-5635-4108-b012-b549a9625919">}</p>



<p id="SE-24b4f26f-19ba-4916-bf8c-2c9717bb591c">#continuous-log {</p>



<p id="SE-a99b3ac7-496d-4936-8ade-2c81f139d751">max-height: 260px;</p>



<p id="SE-e80666ec-2ecb-415c-babb-68f95da06c38">overflow-y: auto;</p>



<p id="SE-a65a5f84-6acc-4833-9e1b-5bfe659d2429">border: 1px solid #ddd;</p>



<p id="SE-749f06b0-cff5-401c-9372-a055f4c38106">border-radius: 6px;</p>



<p id="SE-d463aa7e-906a-41a3-9607-29bb0dddc5e6">padding: 6px;</p>



<p id="SE-590a092d-03af-4d2e-bd52-023b4b50903d">background: #111;</p>



<p id="SE-9e603e08-053b-4a47-94c2-97319befc3f2">color: #eee;</p>



<p id="SE-e4514a1c-3493-4a1d-8d18-43a5d3a109ec">font-size: 12px;</p>



<p id="SE-743a51ab-e427-421b-afd1-439a3d7d5372">}</p>



<p id="SE-d5f4bbd7-51c5-41cc-b0ec-02b4ffd323ce">#continuous-log div {</p>



<p id="SE-738809f7-b7d3-48c9-880e-5c1dabda6850">margin-bottom: 2px;</p>



<p id="SE-4270def2-33c5-45f0-be16-8018df236c4d">}</p>



<p id="SE-53a5d5c1-6e4b-49c8-bfb6-d839ca3c2505">#port-scan-area {</p>



<p id="SE-53b197fe-bbb9-4945-9e62-8cb71aae2485">margin-top: 20px;</p>



<p id="SE-3e59f1e8-8732-485f-9279-65869cfcc6ee">}</p>



<p id="SE-7780f5cb-ffee-4e78-8557-32b1b08e2741">#port-scan-log {</p>



<p id="SE-11288146-7596-4d08-b03f-018a8ac7d8c4">font-size: 12px;</p>



<p id="SE-7dccbf37-c3d5-4c28-9768-3c9453083237">margin-top: 4px;</p>



<p id="SE-3f060763-e337-46a8-990a-05be339ff3fa">white-space: pre-wrap;</p>



<p id="SE-a377eed3-1cef-4445-8903-5338ecae51cc">}</p>



<p id="SE-6d4e8ef6-63b2-4429-b198-8445f6ea5e23">#port-progress {</p>



<p id="SE-0bdcf172-d464-4d13-9523-4b6e6afe6b33">font-size: 12px;</p>



<p id="SE-d30e5d5f-24c5-422b-86cf-eeab2d1813df">margin-top: 4px;</p>



<p id="SE-664b1d36-4d35-46b4-a4b4-76658e36d33b">}</p>



<p id="SE-6f042574-7698-4a45-b1d6-c73952515885">#open-port-summary {</p>



<p id="SE-90ab4282-26ef-4390-afab-0ac50e7c1822">font-size: 13px;</p>



<p id="SE-197b30d8-cc09-4bbe-8ae5-553393fc50b1">margin-top: 8px;</p>



<p id="SE-c288f9a0-816a-4021-b405-2bef854ccbdf">}</p>



<p id="SE-edca4d8e-027c-414c-98cd-40d968121abd">&lt;/style&gt;</p>



<p id="SE-05e6f5c0-d6f0-45bf-96c0-582e1bdb91dc">&lt;/head&gt;</p>



<p id="SE-0b94e45b-1f3a-45ae-960b-835d960765a1">&lt;body&gt;</p>



<p id="SE-a34490d9-16f3-4b96-a5a3-cde9680bcc4e">&lt;h1&gt;Ping + Port Scan 테스트 (포트 9999)&lt;/h1&gt;</p>



<p id="SE-c7ec814e-4944-47c5-b853-f65dbae4207a">​</p>



<p id="SE-93f86af4-c8ed-4364-b61c-ca1591065f8e">&lt;!&#8211; 폼: 타깃 + 실행횟수 + 계속모드 + 포트 문자열 &#8211;&gt;</p>



<p id="SE-90efd0c5-a2de-483b-b636-7e663270358c">&lt;form id=&#8221;ping-form&#8221; method=&#8221;POST&#8221; action=&#8221;/run_test&#8221;&gt;</p>



<p id="SE-643e973a-ae39-49b3-b0ca-174aa46bb249">&lt;div style=&#8221;margin-bottom: 8px;&#8221;&gt;</p>



<p id="SE-abafd461-3b60-4781-9279-7e4054a8186e">&lt;input type=&#8221;text&#8221; name=&#8221;target&#8221; id=&#8221;target&#8221;</p>



<p id="SE-4e2e8225-f68f-49c8-899a-d53cfb4d5e1a">placeholder=&#8221;도메인 또는 IP 입력 (예: 8.8.8.8)&#8221;</p>



<p id="SE-d0606ef1-b5f4-496a-bb00-69c4a9de1672">value=&#8221;{{ target or &#8221; }}&#8221;&gt;</p>



<p id="SE-9fe7bfdf-655f-448e-af63-f0f42c0e66b6">&lt;/div&gt;</p>



<p id="SE-b3a5ea2f-2344-4fb0-8ac4-6ce89f8bea9d">&lt;div style=&#8221;margin-bottom: 8px;&#8221;&gt;</p>



<p id="SE-107f3f6c-4926-4284-b867-f3af259b0eee">&lt;label&gt;</p>



<p id="SE-d04083a8-bf5b-4874-adba-cdf6fded27ac">실행 횟수 (Ping):</p>



<p id="SE-75605669-3f51-42b7-84dd-fbfe4ba239a6">&lt;input type=&#8221;number&#8221; name=&#8221;count&#8221; id=&#8221;count&#8221; min=&#8221;1&#8243; value=&#8221;{{ count or 4 }}&#8221;&gt;</p>



<p id="SE-86831644-b4fd-4f92-9e44-2d860dd22e7a">&lt;/label&gt;</p>



<p id="SE-53a7760c-6d94-4471-8653-7d863caaad6c">&lt;label style=&#8221;margin-left: 10px;&#8221;&gt;</p>



<p id="SE-09907890-4e17-4659-bb11-571353514dd1">&lt;input type=&#8221;checkbox&#8221; name=&#8221;continuous&#8221; id=&#8221;continuous&#8221;</p>



<p id="SE-06323be9-5a49-4ff7-88f6-04215afa9223">{% if continuous %}checked{% endif %}&gt;</p>



<p id="SE-8f5e20e4-8fb9-4e93-b7cb-5763b2c985f4">계속 (1초마다 Ping)</p>



<p id="SE-d4c59062-720b-421e-aa06-e05a5d47b284">&lt;/label&gt;</p>



<p id="SE-3cb6fb44-03bb-49e1-9953-8ed84a6f6dfe">&lt;/div&gt;</p>



<p id="SE-eca7f07e-2dc9-4f4e-b785-daec78a76122">&lt;div style=&#8221;margin-bottom: 10px;&#8221;&gt;</p>



<p id="SE-8fa36e6e-a212-4c27-88f8-f92a881c22e9">&lt;label&gt;</p>



<p id="SE-bf4a468a-ce28-4ddc-8bbc-22b905e5da84">포트 스캔 (예: 80,443 또는 1-100):</p>



<p id="SE-42352619-a977-41fa-9021-0b191145d5ea">&lt;input type=&#8221;text&#8221; name=&#8221;ports&#8221; id=&#8221;ports&#8221; value=&#8221;{{ ports or &#8221; }}&#8221;&gt;</p>



<p id="SE-081f86ba-4e1c-41cf-ab54-7fa281e2fda2">&lt;/label&gt;</p>



<p id="SE-f6d2424a-e456-48e6-b974-7f6dcea21d08">&lt;/div&gt;</p>



<p id="SE-7f625bd7-7967-4919-92d5-4f33e2bf92dd">&lt;input type=&#8221;submit&#8221; value=&#8221;Ping 실행&#8221;&gt;</p>



<p id="SE-019102c7-6a2f-45a0-81d0-3eb0d38543b0">&lt;button type=&#8221;button&#8221; id=&#8221;stop-btn&#8221; disabled&gt;계속 모드 정지&lt;/button&gt;</p>



<p id="SE-3c8580f6-7db0-4ae6-86a7-827ff0a2f2ea">&lt;/form&gt;</p>



<p id="SE-76a5d8c3-6a3c-4348-af5e-14e2d1b3f11a">​</p>



<p id="SE-0329adcc-1791-4be4-99c7-2a250ad54726">&lt;div class=&#8221;small-text&#8221;&gt;</p>



<p id="SE-0403c511-26df-4cd9-aae9-61ffa7ad4423">• Ping 실행: 지정한 횟수만큼 서버에서 ping 실행 후 결과 요약&lt;br&gt;</p>



<p id="SE-f9a2b384-6d46-41eb-a46d-a36ad5fbad8a">• 계속 모드: 브라우저에서 1초마다 /api/ping_once 호출하여 실시간 Ping 로그&lt;br&gt;</p>



<p id="SE-0096a036-f9b5-49da-b111-79f9a03c46b9">• 포트 스캔: 아래 별도 버튼으로 진행 (멀티스레드처럼 여러 포트를 동시에 스캔, 진행률/요약/다운로드 제공)</p>



<p id="SE-a050ea3b-53cc-4bc6-abd9-7f1464beda3f">&lt;/div&gt;</p>



<p id="SE-4e6d9c53-0ccd-4da7-9aaa-c73855cf0ff4">​</p>



<p id="SE-6bd96dc7-fb46-44d3-ae27-5b38d598a06c">{% if summary %}</p>



<p id="SE-445196e2-4033-4a3d-875b-73bb36cd65fe">&lt;!&#8211; Ping 실행 결과 &#8211;&gt;</p>



<p id="SE-8c120f8d-ca4d-41a5-bc1c-9ecd68c3db8a">&lt;div class=&#8221;result-box&#8221;&gt;</p>



<p id="SE-cbb8c65b-ae99-4963-a5bf-f81a9f4a0da0">&lt;h3&gt;Ping 실행 결과 요약 ({{ summary.total }}회)&lt;/h3&gt;</p>



<p id="SE-c50988eb-f435-415f-80ba-04de9beb3a55">&lt;p&gt;대상: &lt;b&gt;{{ target }}&lt;/b&gt;&lt;/p&gt;</p>



<p id="SE-94187760-f26f-4783-9a69-8aac4325cc8b">&lt;p&gt;</p>



<p id="SE-f8a97d15-5c85-4f9e-b005-24136290fef6">성공: &lt;b&gt;{{ summary.success }}&lt;/b&gt; 회,</p>



<p id="SE-1775297b-bbda-43a9-b289-e0c33cede98b">실패: &lt;b&gt;{{ summary.fail }}&lt;/b&gt; 회,</p>



<p id="SE-b7d3590b-3c1f-465c-a897-934137572fb1">패킷 손실: &lt;b&gt;{{ summary.loss_rate }}%&lt;/b&gt;</p>



<p id="SE-9e60d9f1-10aa-4eb9-ae03-bc6d1bb00df3">&lt;/p&gt;</p>



<p id="SE-8c7fa34b-32aa-4d87-8683-9e0978edcfac">&lt;p&gt;</p>



<p id="SE-80b08a40-b365-4bd8-a81c-c594d5566de1">핑 시간(ms) (성공 패킷 기준):&lt;br&gt;</p>



<p id="SE-48ae9f39-58c9-4d08-a421-c0f30226a90a">최소: &lt;b&gt;{{ summary.min_ms }}&lt;/b&gt;,</p>



<p id="SE-48c1f59a-a9fb-4e78-9cf8-76dd8b419877">최대: &lt;b&gt;{{ summary.max_ms }}&lt;/b&gt;,</p>



<p id="SE-072e767d-8488-4652-b065-6ea7b041ee7d">평균: &lt;b&gt;{{ summary.avg_ms }}&lt;/b&gt;</p>



<p id="SE-7971626a-e32e-4655-83eb-5db01d4618f1">&lt;/p&gt;</p>



<p id="SE-5e595626-ae8b-4a93-a247-bd6aed31c772">&lt;table&gt;</p>



<p id="SE-5f51bbac-b6dd-4503-92d3-f59ea1d6a7d1">&lt;thead&gt;</p>



<p id="SE-8e38f575-3954-4069-ad8e-130399d94543">&lt;tr&gt;</p>



<p id="SE-4febeb6a-eeb3-46d7-830e-83c8bd3e3fb9">&lt;th&gt;순번&lt;/th&gt;</p>



<p id="SE-cc979fb7-7487-46e9-a862-156f831c64bd">&lt;th&gt;시간&lt;/th&gt;</p>



<p id="SE-6c3c07c9-7ca7-41a5-8474-353a25e2b375">&lt;th&gt;상태&lt;/th&gt;</p>



<p id="SE-4782f2ec-e333-409a-8dbb-82f9fee554e1">&lt;th&gt;핑 시간(ms)&lt;/th&gt;</p>



<p id="SE-612f1626-77ef-4c6e-a5fc-90f1eb80743c">&lt;/tr&gt;</p>



<p id="SE-7ba8fb06-4e72-49f6-a4a7-b3ef80b75c19">&lt;/thead&gt;</p>



<p id="SE-53a670d2-3fca-4be4-aad8-522053983c80">&lt;tbody&gt;</p>



<p id="SE-1a1edb9d-526e-43fe-9143-826559c9d5f1">{% for row in detail %}</p>



<p id="SE-40928d93-8d36-4c44-976d-d55338e3e801">&lt;tr&gt;</p>



<p id="SE-a158de20-b98d-40ec-b286-5108bbacf954">&lt;td&gt;{{ loop.index }}&lt;/td&gt;</p>



<p id="SE-9677d53a-f301-4140-bd0e-ed6d10a15ba4">&lt;td&gt;{{ row.timestamp }}&lt;/td&gt;</p>



<p id="SE-b8927158-1b87-4e2e-b328-eba72fba5ddc">&lt;td&gt;</p>



<p id="SE-f8e306e9-cfe8-40ae-8613-9b74b58eafe6">{% if row.success %}</p>



<p id="SE-76795938-67af-41a3-b84d-4c38993aa4ca">&lt;span class=&#8221;status-ok&#8221;&gt;성공&lt;/span&gt;</p>



<p id="SE-d28ab588-eb3c-4225-90bb-c9fe653b7ef8">{% else %}</p>



<p id="SE-d7ea5be6-58b1-4c73-a7a7-d375ddd37efd">&lt;span class=&#8221;status-fail&#8221;&gt;실패&lt;/span&gt;</p>



<p id="SE-d8496b50-f56d-48a1-852a-b55cf487cd0d">{% endif %}</p>



<p id="SE-b1d9c611-0845-4d38-814c-00c2285ac4f6">&lt;/td&gt;</p>



<p id="SE-3c05d66d-7e76-4a60-a87e-bf3aef807ff4">&lt;td&gt;</p>



<p id="SE-4de889d3-f6ff-4c3a-ae30-da0c48eb27e0">{% if row.latency_ms is none %}</p>



<p id="SE-fb4863e9-041c-4f08-b046-29884ddf2198">PING 실패</p>



<p id="SE-2ca8ade8-4ec1-438f-aa8a-9a4d2355d4db">{% else %}</p>



<p id="SE-f7def097-f70c-4949-999a-da50bcac9cbd">{{ &#8220;%.1f&#8221;|format(row.latency_ms) }}</p>



<p id="SE-60d2351a-232f-4dc4-87e6-fb2399f4df09">{% endif %}</p>



<p id="SE-fa2a0b00-cce0-4ef9-b037-a08f2c819bb0">&lt;/td&gt;</p>



<p id="SE-6f22f03b-069a-4fb8-8682-438dea537636">&lt;/tr&gt;</p>



<p id="SE-28bcacbd-6911-48df-8fb4-41ee5d1934df">{% endfor %}</p>



<p id="SE-73196062-07ad-4ee1-b02b-145baacae9a2">&lt;/tbody&gt;</p>



<p id="SE-62e99194-ad91-4184-9377-f42159e7b1e6">&lt;/table&gt;</p>



<p id="SE-5e6ecd7a-aa5e-48db-ae33-659447a45c70">​</p>



<p id="SE-375d8168-60cc-4331-8767-680c4848ea51">{% if raw_output %}</p>



<p id="SE-54fcbf64-8c73-4fa2-aba8-82ce90ee5525">&lt;details&gt;</p>



<p id="SE-a257a28a-e0af-44d4-88f3-85a1d30218d9">&lt;summary&gt;마지막 ping 원본 출력 보기&lt;/summary&gt;</p>



<p id="SE-58cbe632-6c4e-45e5-94b9-eb15257214ed">&lt;pre&gt;{{ raw_output }}&lt;/pre&gt;</p>



<p id="SE-a1311cb0-70ac-4d1a-ae56-19e108ebc964">&lt;/details&gt;</p>



<p id="SE-caead6db-42e5-483b-bff2-b06c66f2bd88">{% endif %}</p>



<p id="SE-65d0822b-3c55-46bf-88ed-aff3a320632e">&lt;/div&gt;</p>



<p id="SE-353602e0-2abe-4d84-a75f-d7170ba932da">{% endif %}</p>



<p id="SE-ee6e473f-5162-4b93-8253-ea210f9ee7b2">​</p>



<p id="SE-a0464e21-23c7-4e33-b18f-1764ce5c422e">&lt;!&#8211; 포트 스캔 영역 &#8211;&gt;</p>



<p id="SE-50ab3969-88a9-4050-baba-7848ce73da2f">&lt;div id=&#8221;port-scan-area&#8221; class=&#8221;result-box&#8221;&gt;</p>



<p id="SE-fd708808-2743-40aa-a5e7-54ec2d142ef5">&lt;h3&gt;포트 스캔 (멀티스레드 진행상황 + 요약 + 다운로드)&lt;/h3&gt;</p>



<p id="SE-4d5c590b-be35-49e6-9a31-1c00c1257497">&lt;p class=&#8221;small-text&#8221;&gt;</p>



<p id="SE-3d49debf-e8eb-4e31-a347-793b4e14b135">• 타깃 / 포트 목록 입력 후 [포트 스캔 시작] 클릭&lt;br&gt;</p>



<p id="SE-e25e691a-176c-4203-b830-2243e4e9eaf5">• 여러 포트를 동시에 스캔하면서 진행률과 결과 테이블을 실시간 갱신합니다.&lt;br&gt;</p>



<p id="SE-2941f819-9a0a-4de8-9fe8-5928f975bd2b">• 스캔 완료 후 열린 포트 요약 + CSV / JSON 다운로드 가능</p>



<p id="SE-0fd42d1b-acbc-47d1-8d1d-955208a45056">&lt;/p&gt;</p>



<p id="SE-9929dc73-272c-4065-8dc9-41fee63f94bc">&lt;div style=&#8221;margin-bottom: 8px;&#8221;&gt;</p>



<p id="SE-c1520890-c24b-44aa-ba61-672151e50803">&lt;label&gt;</p>



<p id="SE-ccaba22b-8818-4b23-a3ea-7444fb58dfd5">동시 스캔 수:</p>



<p id="SE-ca9bd20a-33ab-47f2-89cb-5ecbab2fd226">&lt;input type=&#8221;number&#8221; id=&#8221;concurrency&#8221; min=&#8221;1&#8243; max=&#8221;200&#8243; value=&#8221;{{ concurrency or 20 }}&#8221;&gt;</p>



<p id="SE-577bc6c3-5796-44e4-b9fa-5ea2cd7f6b36">&lt;/label&gt;</p>



<p id="SE-8ace717a-c123-458e-830f-fab4bbb5491c">&lt;span class=&#8221;small-text&#8221;&gt; (한 번에 동시에 처리할 최대 포트 수, 기본 20 / 최대 200)&lt;/span&gt;</p>



<p id="SE-07fe2e85-638c-4824-a2e5-af680feab236">&lt;/div&gt;</p>



<p id="SE-465cbef6-3040-4f35-b3cf-efb8a7bb167f">&lt;button type=&#8221;button&#8221; id=&#8221;port-scan-start-btn&#8221;&gt;포트 스캔 시작&lt;/button&gt;</p>



<p id="SE-00c902a8-aadf-4e8a-8f85-bf7b4cc3d3b5">&lt;button type=&#8221;button&#8221; id=&#8221;port-scan-stop-btn&#8221; disabled&gt;포트 스캔 정지&lt;/button&gt;</p>



<p id="SE-32b47a7d-2613-4ec0-b775-23b98da68688">&lt;button type=&#8221;button&#8221; id=&#8221;download-csv-btn&#8221; disabled&gt;CSV 다운로드&lt;/button&gt;</p>



<p id="SE-606d3471-c2c9-4fed-9ffe-c9bb040adf3f">&lt;button type=&#8221;button&#8221; id=&#8221;download-json-btn&#8221; disabled&gt;JSON 다운로드&lt;/button&gt;</p>



<p id="SE-7da73aaf-257f-48aa-ad1b-b8d6e72975d1">&lt;div id=&#8221;port-progress&#8221;&gt;&lt;/div&gt;</p>



<p id="SE-e67acb56-872b-49dc-993e-ed535e49b931">&lt;div id=&#8221;port-scan-log&#8221;&gt;&lt;/div&gt;</p>



<p id="SE-f44b9cd1-73fa-4b11-bf0c-fab04bf60629">&lt;div id=&#8221;open-port-summary&#8221;&gt;&lt;/div&gt;</p>



<p id="SE-ce5664d0-2260-4550-8315-10d176cd563d">&lt;table&gt;</p>



<p id="SE-ffd88ab2-2210-4c2c-92f7-06376233ffd7">&lt;thead&gt;</p>



<p id="SE-d862ec8d-fb8c-4721-835f-e8a961630e9c">&lt;tr&gt;</p>



<p id="SE-6692fd15-b59a-4089-985f-23ead0482fa7">&lt;th&gt;포트&lt;/th&gt;</p>



<p id="SE-9fc0e847-c58a-43e7-9d29-3809c2ae7ec5">&lt;th&gt;상태&lt;/th&gt;</p>



<p id="SE-5f902014-0789-4f2e-9aaa-7ba77802902f">&lt;th&gt;비고&lt;/th&gt;</p>



<p id="SE-0b6c9c50-6460-49c4-9da4-ac51d41351cc">&lt;/tr&gt;</p>



<p id="SE-1cac92ba-e2e0-4110-95ab-16da1be5317c">&lt;/thead&gt;</p>



<p id="SE-65e1d3e9-9d42-4ae9-a607-74dcf36422cf">&lt;tbody id=&#8221;port-table-body&#8221;&gt;</p>



<p id="SE-a0f40491-0332-4333-9641-3d24d016ae3d">&lt;/tbody&gt;</p>



<p id="SE-738546be-554d-4596-85b1-2f414937e712">&lt;/table&gt;</p>



<p id="SE-731e4808-c81a-443a-b8ad-0d6e7b8201ed">&lt;/div&gt;</p>



<p id="SE-4bf24d01-3ee1-4683-b5fb-a74493af998a">​</p>



<p id="SE-122831eb-0cd9-4ab5-87b8-a81a0ddf0c06">&lt;!&#8211; 계속 모드용 영역 &#8211;&gt;</p>



<p id="SE-4f9bba71-04b3-4cea-9a12-56b0bec86e2d">&lt;div id=&#8221;continuous-area&#8221;&gt;</p>



<p id="SE-2eca8fa3-c8c7-4140-8323-387391c12e0d">&lt;h3&gt;계속 모드 실시간 Ping 로그&lt;/h3&gt;</p>



<p id="SE-416aaa03-c550-4ba4-9b9f-6927b0731fcb">&lt;div class=&#8221;small-text&#8221;&gt;</p>



<p id="SE-d07ac941-f057-44ea-91f6-5ae8ffc14db1">• 위에서 &#8220;계속&#8221; 체크 후 Ping 실행 버튼을 누르면 1초마다 자동으로 ping 결과가 아래에 추가됩니다.&lt;br&gt;</p>



<p id="SE-3cab55e6-92e1-4086-95c3-65d92f5e5f19">• &#8220;계속 모드 정지&#8221; 버튼을 누르면 중단됩니다.</p>



<p id="SE-5ce9b1f3-b883-4167-bfd4-7f6907745ece">&lt;/div&gt;</p>



<p id="SE-31df9d3f-ef64-4e6e-bc92-058a07d2d639">&lt;div id=&#8221;continuous-log&#8221;&gt;&lt;/div&gt;</p>



<p id="SE-9989cc4b-6ee5-43f4-919c-bc9a63fc1b38">&lt;/div&gt;</p>



<p id="SE-caa7a44f-9659-41ed-97de-a0279ae451b4">​</p>



<p id="SE-f7d3a642-2257-4e6b-978d-a617cb0a8eb6">&lt;script&gt;</p>



<p id="SE-a416147a-8f83-45be-a30b-7b0456fdd178">let pingIntervalId = null;</p>



<p id="SE-7e31edb4-8bde-4a6c-979a-a8af29939cb5">​</p>



<p id="SE-890188de-386f-4268-8063-b131aac6a05f">const form = document.getElementById(&#8216;ping-form&#8217;);</p>



<p id="SE-ed6ea4e9-1d0e-442f-9eee-1223c1ec4133">const continuousCheckbox = document.getElementById(&#8216;continuous&#8217;);</p>



<p id="SE-83b01ad4-e3fa-4163-9e13-8a9b814b152c">const stopBtn = document.getElementById(&#8216;stop-btn&#8217;);</p>



<p id="SE-c29cd080-36b2-49f3-aac0-889e2cd462fd">const targetInput = document.getElementById(&#8216;target&#8217;);</p>



<p id="SE-25391262-ec5e-4923-bc4e-5708cc56834e">const countInput = document.getElementById(&#8216;count&#8217;);</p>



<p id="SE-7ff4530c-43f4-4d5f-a7e7-c8da543bc5b1">const logDiv = document.getElementById(&#8216;continuous-log&#8217;);</p>



<p id="SE-6cfbb706-5ad4-4bba-b602-b04a4432048d">​</p>



<p id="SE-a4ceadd0-65dd-4dc6-a37d-14594fe15ce7">const portScanStartBtn = document.getElementById(&#8216;port-scan-start-btn&#8217;);</p>



<p id="SE-e5653f1a-f9b3-4dfe-bbe6-4c9ba839f438">const portScanStopBtn = document.getElementById(&#8216;port-scan-stop-btn&#8217;);</p>



<p id="SE-13438056-e891-4b9d-bee0-c034d098f8b1">const portsInput = document.getElementById(&#8216;ports&#8217;);</p>



<p id="SE-fa4f1b42-d29e-492d-b195-53b1f9507140">const portProgressDiv = document.getElementById(&#8216;port-progress&#8217;);</p>



<p id="SE-87299f04-2277-4416-9195-a15db7cb2acd">const portScanLogDiv = document.getElementById(&#8216;port-scan-log&#8217;);</p>



<p id="SE-a21d9f45-f978-4f76-a60a-e99ddbdc746d">const portTableBody = document.getElementById(&#8216;port-table-body&#8217;);</p>



<p id="SE-476509f8-aa5a-4fec-b94a-b1cf814edb74">const concurrencyInput = document.getElementById(&#8216;concurrency&#8217;);</p>



<p id="SE-cf4ffb35-3b57-459c-a499-b416b6556aac">const openPortSummaryDiv = document.getElementById(&#8216;open-port-summary&#8217;);</p>



<p id="SE-dfb50651-caf9-41b8-9b89-89efe08a0648">const downloadCsvBtn = document.getElementById(&#8216;download-csv-btn&#8217;);</p>



<p id="SE-686ab0c1-2346-4cc0-9213-1cb6b5c41a86">const downloadJsonBtn = document.getElementById(&#8216;download-json-btn&#8217;);</p>



<p id="SE-de351d7a-0dd8-4bfb-bba9-03651efff3ca">​</p>



<p id="SE-bed0c7ee-55ff-4787-9fb5-e1d83740d967">let portScanAbort = false;</p>



<p id="SE-94fd1a52-4d01-496e-b49d-05c26581d2c9">let portResults = []; // {port, open, note}</p>



<p id="SE-2d1496fd-0751-416b-9967-4c97099ff0bb">​</p>



<p id="SE-c0a4420d-153d-4aa6-b741-c150cddd8116">function addLogLine(text, isOk) {</p>



<p id="SE-e8f109d2-5c6b-4988-b0ef-7cd9aa4133a2">const line = document.createElement(&#8216;div&#8217;);</p>



<p id="SE-ddd938ca-519f-401c-94e9-1dda3df7ff32">if (isOk === true) {</p>



<p id="SE-34046e2e-86a0-40b5-8a25-a544075f01ec">line.style.color = &#8216;#7CFC00&#8217;;</p>



<p id="SE-c1e5d204-b18b-43d7-9cb4-727bd668d9c6">} else if (isOk === false) {</p>



<p id="SE-d34d58f5-856c-45fa-b256-f79117feabce">line.style.color = &#8216;#FF6347&#8217;;</p>



<p id="SE-a7196890-4054-4caa-bfae-63404d5b3ff4">} else {</p>



<p id="SE-a98b1e95-2a8d-40b6-bc64-3faa0ee7dc8c">line.style.color = &#8216;#FFFFFF&#8217;;</p>



<p id="SE-cfa6c9b8-fd5a-4222-a158-5e6a347d5d97">}</p>



<p id="SE-cf049fa5-8f12-49bc-b56c-034d1637aedf">line.textContent = text;</p>



<p id="SE-945b76e5-29c4-4cb4-ade2-bfbfe54ff230">logDiv.appendChild(line);</p>



<p id="SE-2225f87b-a068-49ec-9f51-79d11ef91761">logDiv.scrollTop = logDiv.scrollHeight;</p>



<p id="SE-9be8bbec-b052-4c5e-9d9c-d336a1cb824e">}</p>



<p id="SE-f6bd8de3-12be-4eb8-a344-7535f8169f3e">​</p>



<p id="SE-b9a8a8b0-8b27-469d-9fed-5e9c4a3b0297">function startContinuousPing(target) {</p>



<p id="SE-9eb15e14-7ce5-4fb7-bf45-fbedaeeb9f51">if (!target) {</p>



<p id="SE-6980d527-31da-433f-b38b-a193b872ee1d">alert(&#8216;타겟을 입력해주세요.&#8217;);</p>



<p id="SE-4bb9220f-81bb-4f2a-84a1-bd23286b41c8">continuousCheckbox.checked = false;</p>



<p id="SE-10ccbee3-caf5-478d-b5a1-8a996d401386">return;</p>



<p id="SE-f7160c6f-3b46-4080-ac30-9db34cc23f09">}</p>



<p id="SE-9b69fa32-0f04-4680-879e-d40ef4ae543d">​</p>



<p id="SE-5aed8ff9-8e6f-4e02-9611-1c47d2e78518">if (pingIntervalId !== null) {</p>



<p id="SE-2fcac262-33e0-4309-9984-6bb99415821b">clearInterval(pingIntervalId);</p>



<p id="SE-5a108fea-b4a2-4ff7-9435-6b8ae6ecd8c4">}</p>



<p id="SE-f554b98a-4fa8-4e0a-96cf-b399206fbb10">​</p>



<p id="SE-244bd90c-05e6-428c-b571-39f8234b2100">addLogLine(&#8216;&#8212; 계속 모드 시작: &#8216; + target + &#8216; &#8212;&#8216;, null);</p>



<p id="SE-1f1ca813-f31d-4edd-ab2a-69a858f9cb85">​</p>



<p id="SE-891ea92e-8ad1-4aa8-acc5-743be7b5ada3">pingIntervalId = setInterval(function() {</p>



<p id="SE-d5a9882d-926c-4065-85d8-d2d8b4e082be">fetch(&#8216;/api/ping_once?target=&#8217; + encodeURIComponent(target))</p>



<p id="SE-e8df6678-6602-48f7-9f37-f0468ad6a811">.then(function(response) { return response.json(); })</p>



<p id="SE-17653a95-8add-4e42-b62f-9a7f1c28cca9">.then(function(data) {</p>



<p id="SE-5491304b-fdb9-4a1a-89a6-f69cc0e01e40">const ts = data.timestamp || &#8221;;</p>



<p id="SE-8f664a87-ed28-4120-98c2-742d54b3c8fc">const ok = data.success;</p>



<p id="SE-4a164e34-1d65-4133-80e5-6fee6e913065">const latency = data.latency_ms;</p>



<p id="SE-9da14515-6adf-4ebb-b73b-44d5963dda98">let msg = &#8216;[&#8216; + ts + &#8216;] &#8216; + data.target + &#8216; -&gt; &#8216;;</p>



<p id="SE-4b838699-431d-4db0-be1a-c82b3fb76743">​</p>



<p id="SE-5d05d855-4f3b-418a-9920-e264bbddadfb">if (ok) {</p>



<p id="SE-2ef6dc54-583e-4d66-92af-7feec09ec183">if (latency === null) {</p>



<p id="SE-e77f2127-e68c-4ebe-83e0-e6d776ed0b4e">msg += &#8216;✅ 성공 | PING 시간: -&#8216;;</p>



<p id="SE-9cb2f5c6-8733-4de6-bc7b-90c4de5cbfd9">} else {</p>



<p id="SE-95fb8de2-16a9-4ba3-9a27-52cd82d3a700">msg += &#8216;✅ 성공 | PING 시간: &#8216; + latency.toFixed(1) + &#8216; ms&#8217;;</p>



<p id="SE-245c9fa9-545e-4430-8178-c2cd3295abc8">}</p>



<p id="SE-5dd0fdf1-99da-407e-a21d-229ca143829d">} else {</p>



<p id="SE-5fee3a87-fb83-41ba-b8f7-33a086511c1f">msg += &#8216;❌ 실패 | PING 시간: 알 수 없음&#8217;;</p>



<p id="SE-7c7a8173-ecf7-46a1-b052-8e1b61ca0a77">}</p>



<p id="SE-fa085b10-7e93-4231-ad62-ec13bb049c8c">addLogLine(msg, ok);</p>



<p id="SE-d938574f-6d32-47b7-ba15-c29974d72e7b">})</p>



<p id="SE-2e9d5bb4-fdf1-4f43-bc70-f130c761fd90">.catch(function(err) {</p>



<p id="SE-4d2bb5e5-0ee3-40b8-845d-d7d1c6c2699b">addLogLine(&#8216;에러: &#8216; + err, false);</p>



<p id="SE-f40c437f-8cce-49c9-9d85-bf191c36c047">});</p>



<p id="SE-5cd794cf-2f25-49de-b1be-d9bb5fb9a062">}, 1000);</p>



<p id="SE-afee2512-f882-4477-a301-b3588e830c40">​</p>



<p id="SE-6534b713-bc71-4456-9880-b120202cae56">stopBtn.disabled = false;</p>



<p id="SE-eacb0e1e-cd2a-4e47-bd74-dc351c2ec1d1">}</p>



<p id="SE-6357d0bd-b747-4f7f-90a6-391b81a7a206">​</p>



<p id="SE-b963f4fa-867c-4df5-852a-2ab02f0f7e12">function stopContinuousPing() {</p>



<p id="SE-df4a0ebd-c8b9-43b8-b6b5-44094e9b6e88">if (pingIntervalId !== null) {</p>



<p id="SE-917362fd-6c76-4f28-9daa-3cff1aae9c9f">clearInterval(pingIntervalId);</p>



<p id="SE-1bf9b4d7-af05-4763-9e72-5868c8eb9280">pingIntervalId = null;</p>



<p id="SE-7ac2dc49-62b3-45f8-8766-9e1e4cd2cdaa">addLogLine(&#8216;&#8212; 계속 모드 정지 &#8212;&#8216;, null);</p>



<p id="SE-b7649dbe-9989-424d-9484-1ad045ee0738">}</p>



<p id="SE-a0687009-9081-476a-a751-413b45c4ee1a">stopBtn.disabled = true;</p>



<p id="SE-f312f06b-cb94-4b7c-b02d-05fc1424ba26">}</p>



<p id="SE-cfe13b4d-f26b-4898-8bc2-97e258fc1322">​</p>



<p id="SE-52ac3012-83fe-451b-8f8f-8d804188a0f4">// Ping 폼 제출 시 동작</p>



<p id="SE-74ef2fa5-b9b1-491b-ad36-dde1292b9b90">form.addEventListener(&#8216;submit&#8217;, function(ev) {</p>



<p id="SE-604e33fd-bb0a-4d31-ad19-cc7f1a251a56">const isContinuous = continuousCheckbox.checked;</p>



<p id="SE-23f7af71-6111-4b09-bbb6-c4d79b053bf5">const target = targetInput.value.trim();</p>



<p id="SE-b25ef23b-53bc-404a-9652-28c5c6726485">​</p>



<p id="SE-67fb5942-c7ec-4992-9eb0-e877dab34ad2">if (isContinuous) {</p>



<p id="SE-5e210560-69c4-4f9d-8019-4cab64baadad">ev.preventDefault();</p>



<p id="SE-b8bef7d1-34b7-4c09-ad83-b05fafd18b8b">startContinuousPing(target);</p>



<p id="SE-6d15d1af-6d08-46f6-b418-38b78684cb13">} else {</p>



<p id="SE-88c216cd-70c1-48ed-802c-c22bf83caacf">stopContinuousPing();</p>



<p id="SE-714d15bc-6f5b-4679-bfe1-3b445433d9f1">if (!countInput.value) {</p>



<p id="SE-e0d653b9-9c11-4073-b5a1-acd6e69767b0">countInput.value = 4;</p>



<p id="SE-80346428-7da0-442b-9a0d-7d2567e8d11c">}</p>



<p id="SE-c918efd7-a61e-4320-a7e7-3541f01ee66e">}</p>



<p id="SE-f9c805e3-a0d3-40ed-98eb-48a3dad3c083">});</p>



<p id="SE-5bff3ab9-0d9a-4f0b-9007-a4357473e617">​</p>



<p id="SE-a7e4ccc6-b7ca-4040-941e-05790548e841">stopBtn.addEventListener(&#8216;click&#8217;, function() {</p>



<p id="SE-4c896451-ca71-4909-b8ef-dbc015a1b12c">stopContinuousPing();</p>



<p id="SE-c1c58d1f-d74f-4fa0-a4ec-2065efb25c33">});</p>



<p id="SE-f1aae170-90ff-4b30-abbf-9ce71285670f">​</p>



<p id="SE-78d7c17d-0e4d-4193-924c-c61ec8277201">// &#8212;- 포트 스캔 관련 JS &#8212;-</p>



<p id="SE-6c413c2a-53d7-4fa6-9813-11c6f9c650b5">function parsePortsLocal(portsStr) {</p>



<p id="SE-a2585748-2910-4968-a6f1-2e1cd6398bf9">portsStr = portsStr.trim();</p>



<p id="SE-7c800694-630c-4bf1-975b-7adcd85d3f9e">if (!portsStr) return [];</p>



<p id="SE-dfd9d347-f826-413c-9a65-4a19a62788e0">​</p>



<p id="SE-ce1f724b-944d-459b-9bce-4d93e9523833">const result = new Set();</p>



<p id="SE-4c169b9e-1aa1-43b3-be97-64fe2e88ef7c">const parts = portsStr.split(&#8216;,&#8217;);</p>



<p id="SE-be728fdb-bd2d-4d3e-a3d5-0b23206de151">for (let part of parts) {</p>



<p id="SE-abdb4a3e-524b-4724-91cf-b0f2d8c84218">part = part.trim();</p>



<p id="SE-64bf7ae3-719f-45ec-a23c-af907bded8eb">if (!part) continue;</p>



<p id="SE-8ba6fc68-aa08-459b-8e74-d9c2b0803075">​</p>



<p id="SE-f26d9c91-7620-40fa-9214-5de194168220">// 혹시 전각 대시(– — −) 들어올 경우 ASCII &#8216;-&#8216; 로 치환</p>



<p id="SE-13f5c1ac-c7e1-4d17-9202-ef3ae554271f">part = part.replace(/[–—−]/g, &#8216;-&#8216;);</p>



<p id="SE-32ea9193-ff14-45f0-a2c0-0641aafa2f01">​</p>



<p id="SE-9886fd7f-c67a-4dee-99d6-a64d25e01723">if (part.includes(&#8216;-&#8216;)) {</p>



<p id="SE-cf713900-4e88-4928-9e02-4a52ba58e1ec">const rangeParts = part.split(&#8216;-&#8216;);</p>



<p id="SE-1ab2d4a8-b6a9-4419-ae19-2bb50631b5e6">if (rangeParts.length !== 2) continue;</p>



<p id="SE-8267098a-2682-4759-b596-a8234201dd64">const startStr = rangeParts[0].trim();</p>



<p id="SE-d25b5128-63ad-4365-8b4d-26944674ba17">const endStr = rangeParts[1].trim();</p>



<p id="SE-fac1d2eb-2fe4-4262-8498-702009082261">const s = parseInt(startStr, 10);</p>



<p id="SE-fe6db5f2-9965-4fae-919d-acba68d49309">const e = parseInt(endStr, 10);</p>



<p id="SE-b8dcfd80-2c93-42d9-bf88-dde633f2b8b2">if (isNaN(s) || isNaN(e)) continue;</p>



<p id="SE-6ad799fb-ada4-4708-923b-2748257ce9ac">const start = Math.min(s, e);</p>



<p id="SE-d65d8d03-ceed-4efa-aa22-cebe06bb7e1a">const end = Math.max(s, e);</p>



<p id="SE-f5498071-bf56-43ba-9cde-a252a186543b">for (let p = start; p &lt;= end; p++) {</p>



<p id="SE-df6c242c-04b1-43e6-a3dc-379fa846148f">if (p &gt;= 1 &amp;&amp; p &lt;= 65535) result.add(p);</p>



<p id="SE-b662591c-c501-46a1-9896-c520f8c6a51e">}</p>



<p id="SE-d35d7ff6-d6e0-4bc0-b011-b063e5af459a">} else {</p>



<p id="SE-ba59939f-d035-4fa0-b085-f79e663fd81d">const p = parseInt(part, 10);</p>



<p id="SE-86364e2e-b623-41ea-89ca-cf92b18019e4">if (!isNaN(p) &amp;&amp; p &gt;= 1 &amp;&amp; p &lt;= 65535) result.add(p);</p>



<p id="SE-fe3fbae4-05c0-4b5e-86b3-be916b1d85a1">}</p>



<p id="SE-e1e6db27-84aa-4942-88bb-8bae931039d0">}</p>



<p id="SE-019a6150-04e3-487a-9b41-0d49dffad087">return Array.from(result).sort((a, b) =&gt; a &#8211; b);</p>



<p id="SE-15457b10-a725-465b-ae99-0166e1768fd5">}</p>



<p id="SE-8e2cfb8f-20c9-4e53-87a1-ef05b3634b13">​</p>



<p id="SE-c10352ef-eae3-483b-b50a-b25ec4f0fe63">async function scanPortOnce(target, port) {</p>



<p id="SE-a429400f-6791-4af5-9709-e4b31d588a97">const res = await fetch(</p>



<p id="SE-d072b001-44d7-4f57-86bd-77529d2cd677">&#8216;/api/scan_port?target=&#8217;</p>



<p id="SE-11f6e32d-aa5b-4b9e-9548-bd64113d4b36">+ encodeURIComponent(target)</p>



<p id="SE-029537ed-4ed8-4cc9-8b62-d163239951e7">+ &#8216;&amp;port=&#8217; + encodeURIComponent(port)</p>



<p id="SE-5d2f4030-29e6-4869-8ae7-df79951b1c7b">);</p>



<p id="SE-a13203d5-887e-4c44-ab83-d3b88bb3a842">return res.json();</p>



<p id="SE-14bad5e5-38ad-4fc4-9cbb-3d38ad64bc7e">}</p>



<p id="SE-96c625b4-2521-4edd-ab27-21f57dec51d4">​</p>



<p id="SE-fa8335e5-d228-4f8a-b60c-88db5d9a8eb0">function updateOpenPortSummary() {</p>



<p id="SE-f36be8a7-8be4-44e7-9693-80816fecaa2b">if (portResults.length === 0) {</p>



<p id="SE-f13e6f38-8208-4f46-af50-6ac38f90d381">openPortSummaryDiv.textContent = &#8220;&#8221;;</p>



<p id="SE-3ef8fb4c-36ce-4694-8101-7b4d421875d2">return;</p>



<p id="SE-4f52a592-3fac-44bf-91a4-19057e767216">}</p>



<p id="SE-60d071fd-3b4a-4945-a9fb-b073089ef192">const openPorts = portResults</p>



<p id="SE-ac4bd320-38da-4312-9a09-0691bb771014">.filter(r =&gt; r.open)</p>



<p id="SE-2fc5d24e-4905-4c3d-8d34-317ad8df545c">.map(r =&gt; r.port)</p>



<p id="SE-f5a6f80d-bee4-4edb-b7f6-eb3e0a3e872f">.sort((a, b) =&gt; a &#8211; b);</p>



<p id="SE-3e33f064-7520-4e99-9945-0853c7aa8f40">​</p>



<p id="SE-1871d3e3-e201-430b-914d-45eb5401d650">if (openPorts.length === 0) {</p>



<p id="SE-c71de5da-e1cb-48bc-9d62-e14b1fe74bea">openPortSummaryDiv.textContent = &#8220;열린 포트 없음.&#8221;;</p>



<p id="SE-eaab455a-8acd-4079-b5e8-37ae3bb02237">} else {</p>



<p id="SE-63781d2e-4b57-4a0b-b5d5-8ed51348a9af">openPortSummaryDiv.textContent =</p>



<p id="SE-5412028d-96ad-40b4-b52f-3fd52a96eb17">&#8220;열린 포트 (&#8221; + openPorts.length + &#8220;개): &#8221; + openPorts.join(&#8220;, &#8220;);</p>



<p id="SE-fc15bfdc-9d2d-4348-b6c8-4b271268a0d0">}</p>



<p id="SE-7372dade-7323-4277-998d-59f5c9e11c70">}</p>



<p id="SE-dfb8b845-8c93-41bc-834f-3eb9399f454a">​</p>



<p id="SE-033d7b3a-9130-44cc-ae01-e463ac60e34a">function downloadJSON() {</p>



<p id="SE-793bc98a-6f5a-448c-b4a5-e3de03c52312">if (portResults.length === 0) {</p>



<p id="SE-50112704-0248-46b2-8104-9cbfdd58cf93">alert(&#8220;다운로드할 스캔 결과가 없습니다.&#8221;);</p>



<p id="SE-90bb5a84-25ea-48ea-bfd9-a9839556a737">return;</p>



<p id="SE-832384ed-1e9e-4e9e-b9af-5933d04bf8d4">}</p>



<p id="SE-5c79dc24-9ae3-4941-9a27-e9487cb28e4b">const target = targetInput.value.trim() || &#8220;target&#8221;;</p>



<p id="SE-fd842b3d-ddd6-401d-a51f-31fd384caa19">const dataStr = JSON.stringify(portResults, null, 2);</p>



<p id="SE-dc95bdd9-7b59-4e8b-ae3d-c260607512be">const blob = new Blob([dataStr], { type: &#8220;application/json&#8221; });</p>



<p id="SE-a8ca936e-69b2-43b9-964b-6f158341f403">const url = URL.createObjectURL(blob);</p>



<p id="SE-4228b239-3e64-45d2-866b-e509b8716c4d">const a = document.createElement(&#8220;a&#8221;);</p>



<p id="SE-a4f826be-8569-41a8-a871-f3dfecf1f0fc">const ts = new Date().toISOString().replace(/[:.]/g, &#8220;-&#8220;);</p>



<p id="SE-2ae44b35-9fa1-4926-a77d-881fddd721e7">a.href = url;</p>



<p id="SE-d7c45456-c144-4c48-979a-0150a02c0e21">a.download = &#8220;port_scan_&#8221; + target + &#8220;_&#8221; + ts + &#8220;.json&#8221;;</p>



<p id="SE-b15d2246-d28d-47aa-8eae-c3da8e540d15">document.body.appendChild(a);</p>



<p id="SE-e38a0936-7194-4a90-9830-ca5f8634977b">a.click();</p>



<p id="SE-107273c0-cbbe-4b13-914d-a324bd5b3fe8">document.body.removeChild(a);</p>



<p id="SE-e7a7e3d8-a35f-4727-aae4-fcff26f48065">URL.revokeObjectURL(url);</p>



<p id="SE-66012696-bee1-459f-940a-f4b34fcebe79">}</p>



<p id="SE-25e893ac-0ad9-4773-8a49-bba9f8e5dbba">​</p>



<p id="SE-2aa6687e-ea9a-4877-987b-de1699c456d0">function downloadCSV() {</p>



<p id="SE-df86b0b6-5b77-4961-83c8-94ff973099be">if (portResults.length === 0) {</p>



<p id="SE-fbd80fbc-82c0-49f3-8745-eb478b35829d">alert(&#8220;다운로드할 스캔 결과가 없습니다.&#8221;);</p>



<p id="SE-8c257293-1d14-41bf-be9d-77aaceeb998f">return;</p>



<p id="SE-c4658066-806c-49b8-97a2-20d33c71e4c0">}</p>



<p id="SE-1b24e648-2b0e-43fb-99c5-85deb21a7810">const target = targetInput.value.trim() || &#8220;target&#8221;;</p>



<p id="SE-5ad3ef16-ff2e-4649-a9ce-06c68c0bda32">​</p>



<p id="SE-8ea5536a-baf3-404f-8049-c31374b1a584">function escapeCsv(value) {</p>



<p id="SE-58a0c788-5e47-494f-81c0-bbd7e1c08b02">if (value == null) return &#8220;&#8221;;</p>



<p id="SE-5b8d548b-f316-4f6c-a64c-dbb0c25ecf3d">const s = String(value);</p>



<p id="SE-33959af6-a670-41b7-a775-1707a5accb10">if (s.includes(&#8216;&#8221;&#8216;) || s.includes(&#8216;,&#8217;) || s.includes(&#8216;\\n&#8217;)) {</p>



<p id="SE-04f4ec13-4a28-4a05-a986-98d5baa60b9a">return &#8216;&#8221;&#8216; + s.replace(/&#8221;/g, &#8216;&#8221;&#8221;&#8216;) + &#8216;&#8221;&#8216;;</p>



<p id="SE-341e2010-7ca0-4f81-baa8-29a8e591b068">}</p>



<p id="SE-bff20802-42a9-4ab9-ad28-ff95eac1cd22">return s;</p>



<p id="SE-a346d77e-bac9-4c65-90b9-5e4ca1b83134">}</p>



<p id="SE-b8fc9e94-0715-46e5-abfb-1638b10e178d">​</p>



<p id="SE-c27eaff0-1ce5-4307-8696-3eadd29652bc">let csv = &#8220;port,status,note\\n&#8221;;</p>



<p id="SE-6232423e-5749-458c-8517-96cf92a42065">for (const r of portResults) {</p>



<p id="SE-040d75d4-7405-42dd-8984-062ab28d93b9">const status = r.open ? &#8220;OPEN&#8221; : &#8220;CLOSED&#8221;;</p>



<p id="SE-9347d305-109c-4876-8651-2899d6ddf1de">csv += [</p>



<p id="SE-78ff4779-52e5-49e6-b124-c53b46bf677f">escapeCsv(r.port),</p>



<p id="SE-4c24083c-48f1-4b5a-a8cb-2aa83bc9d88c">escapeCsv(status),</p>



<p id="SE-340cac94-877f-481f-8bee-3cdbcc2f913c">escapeCsv(r.note)</p>



<p id="SE-af1c551b-911e-4303-8396-d68ce731d393">].join(&#8220;,&#8221;) + &#8220;\\n&#8221;;</p>



<p id="SE-17d454a2-6786-4fcf-97bd-e72c9a59d6d6">}</p>



<p id="SE-a0a857c8-2350-4e73-98ce-9af5aedce9b9">​</p>



<p id="SE-ea96ca49-84aa-4467-a6f9-8648663dff66">// 👉 한글이 엑셀에서 안 깨지도록 UTF-8 BOM 추가</p>



<p id="SE-dd14a1ad-146b-46b7-9196-16ff340cdc83">const bom = &#8220;\\uFEFF&#8221;; // JS에서 문자열 리터럴로 해석되면서 실제 BOM 문자로 변환</p>



<p id="SE-da6516e6-cd50-46ef-94e7-219ba4142c04">const blob = new Blob([bom + csv], { type: &#8220;text/csv;charset=utf-8;&#8221; });</p>



<p id="SE-4e8a88ff-6557-4afc-9f99-471f6085ad1b">​</p>



<p id="SE-8c98dbdd-4664-40aa-af6a-ad7ac162562c">const url = URL.createObjectURL(blob);</p>



<p id="SE-43806f73-8072-4e0b-b66e-7e61417aa555">const a = document.createElement(&#8220;a&#8221;);</p>



<p id="SE-e5500455-2c29-4072-a710-e4d9797b3131">const ts = new Date().toISOString().replace(/[:.]/g, &#8220;-&#8220;);</p>



<p id="SE-190fbf22-20cc-44c5-b93a-617139ac1900">a.href = url;</p>



<p id="SE-23d715ce-ec87-45a8-b207-2a1d6d4fc621">a.download = &#8220;port_scan_&#8221; + target + &#8220;_&#8221; + ts + &#8220;.csv&#8221;;</p>



<p id="SE-37d0190f-eae1-4395-bd1d-cf5a46c87437">document.body.appendChild(a);</p>



<p id="SE-5918cb38-d639-4d24-93d9-32e0119022ac">a.click();</p>



<p id="SE-c2169fee-e013-4a4e-8a3b-2c56c0e3aa94">document.body.removeChild(a);</p>



<p id="SE-e9250656-9f97-4bd6-9a27-85605f91a2c9">URL.revokeObjectURL(url);</p>



<p id="SE-89368a7d-75a4-4ddc-a8ce-024233ba9359">}</p>



<p id="SE-0135af9c-a764-44b3-abcc-cd10a50d2a71">​</p>



<p id="SE-7a9ea432-c48c-496d-afd5-22fffc636c9d">async function startPortScan() {</p>



<p id="SE-3e9998c1-d980-4956-9721-21042d0cc421">const target = targetInput.value.trim();</p>



<p id="SE-c0df711d-ed67-4641-ba58-959b8bd324ae">const portsStr = portsInput.value.trim();</p>



<p id="SE-47f4fed8-b69f-4173-8e39-8a7594de61e5">​</p>



<p id="SE-ad24fe1e-d7a4-474a-9578-02332a68bddb">if (!target) {</p>



<p id="SE-3ecb83eb-6c44-4d88-a607-e4f36ce25f4a">alert(&#8220;타깃(도메인 또는 IP)을 입력하세요.&#8221;);</p>



<p id="SE-e7846cf8-0ef4-4a13-b4b8-57cba963da14">return;</p>



<p id="SE-76e17c67-e0a5-42a8-81d2-ccf7c4c4224f">}</p>



<p id="SE-58fe7a59-cbf9-4f2a-9eff-259fffa117ef">if (!portsStr) {</p>



<p id="SE-2e39b6ed-dcca-4db9-b0ef-f31495f94e5f">alert(&#8220;포트 목록을 입력하세요.&#8221;);</p>



<p id="SE-2d226abe-8722-4c76-ad97-00bbca1fef53">return;</p>



<p id="SE-f67b7fb9-0217-4f59-aa85-d3fe4b736f15">}</p>



<p id="SE-f7cda149-1c8f-460b-831c-caf310772476">​</p>



<p id="SE-75b0a059-e526-4e11-aa9e-58f3eb1af6fa">const ports = parsePortsLocal(portsStr);</p>



<p id="SE-44288f63-0952-4a17-a654-b7e128f96b60">if (ports.length === 0) {</p>



<p id="SE-95b0a759-e946-4f59-b2c0-3647ff3edab0">alert(&#8220;유효한 포트가 없습니다. (예: 80,443 또는 1-100)&#8221;);</p>



<p id="SE-a6c610a1-c2e2-4b1c-8c2e-93a51ab1d523">return;</p>



<p id="SE-6d7641d0-2218-47d7-a757-676124e9388b">}</p>



<p id="SE-714083da-1080-4bcb-b8ab-46fde1f2c1ab">​</p>



<p id="SE-3a4a7f4a-f190-4227-84a1-0e3673b0a06e">let concurrency = parseInt(concurrencyInput.value, 10);</p>



<p id="SE-e737b4d5-0d16-4f95-a810-5438712eb21a">if (isNaN(concurrency) || concurrency &lt;= 0) {</p>



<p id="SE-65fbd1fe-b28f-40e9-b863-18c28d094a9b">concurrency = 10;</p>



<p id="SE-9093e792-2e63-4084-b66a-21e538572a66">}</p>



<p id="SE-1e9ba9d7-87d1-4f14-b592-fc5639956f0c">if (concurrency &gt; 200) {</p>



<p id="SE-839f7d7f-7742-4b53-a74a-28c89c7a4be7">concurrency = 200;</p>



<p id="SE-6fe03b4a-1f6b-485b-8c18-c038d107c2bf">}</p>



<p id="SE-eb7fb56a-f0d0-4658-951d-865b6e0df257">concurrencyInput.value = concurrency;</p>



<p id="SE-d39a3f1e-83af-4fde-88f2-8347df889178">​</p>



<p id="SE-e494b86a-4ed9-424e-ae69-a4f5356c449d">// 초기화</p>



<p id="SE-3debee0b-3380-4c94-bfb1-e0bca90999c4">portScanAbort = false;</p>



<p id="SE-ff684e5d-17a2-44f8-886d-189dcf6a9717">portResults = [];</p>



<p id="SE-2ef78276-14d5-4c5f-b203-28897567da06">portTableBody.innerHTML = &#8220;&#8221;;</p>



<p id="SE-0f09af8a-5975-4359-9162-a7780beb4899">portProgressDiv.textContent = &#8220;&#8221;;</p>



<p id="SE-c842f6bd-8f3b-49f5-a09e-c4c375c6d9c5">portScanLogDiv.textContent = &#8220;&#8221;;</p>



<p id="SE-e3ff74f4-dbfe-4062-af51-4bc1ff3a7756">openPortSummaryDiv.textContent = &#8220;&#8221;;</p>



<p id="SE-04f02d72-5f1e-4ed5-8f46-bc37908db8c2">portScanStartBtn.disabled = true;</p>



<p id="SE-f3d3017f-8c43-4d7c-b7bd-ca74b37983c3">portScanStopBtn.disabled = false;</p>



<p id="SE-07d3975e-14e5-41d9-8c68-f30d2e892af6">downloadCsvBtn.disabled = true;</p>



<p id="SE-dbaf95ea-361b-4625-8ce3-6d02f855d0b6">downloadJsonBtn.disabled = true;</p>



<p id="SE-c9623637-a055-4d40-9216-05a839f43186">​</p>



<p id="SE-301086ad-a502-4cb5-9480-197b3b9d2ba4">const total = ports.length;</p>



<p id="SE-e65a81b4-47b9-4ce9-aa11-b49d532cee00">let done = 0;</p>



<p id="SE-ae6a4aa4-8c45-4638-ab7a-e631c2846286">let index = 0;</p>



<p id="SE-8e8d152b-7b84-4f98-a0eb-72eea4dedddf">​</p>



<p id="SE-2b6ebf6b-8064-4b93-8f00-9f5cc4b4b67e">portScanLogDiv.textContent =</p>



<p id="SE-6d6ec4c5-8e9c-4b7a-8ba0-aa045f9fb3e4">`스캔 시작: ${target}, 포트 ${total}개 (동시 스캔 수: ${concurrency})`;</p>



<p id="SE-aad73b58-3bbd-430e-a75a-b4a18e1e00f7">portScanLogDiv.textContent += &#8220;\\n포트 목록: &#8221; + ports.join(&#8220;, &#8220;);</p>



<p id="SE-a58a021f-31ba-4497-a2d5-78e856e8d4c6">​</p>



<p id="SE-c80884fd-817d-49b7-b109-d8898aa6b955">async function worker() {</p>



<p id="SE-300031be-1834-4724-80bf-aa8eb53d4d8c">while (!portScanAbort) {</p>



<p id="SE-1eb826b7-79e8-4443-aab0-02485516d479">let currentIndex;</p>



<p id="SE-eb7469e5-3d35-458e-8f51-e8d7586d8b10">if (index &gt;= total) break;</p>



<p id="SE-a7204e93-49d4-450f-8849-ccb1c6c72e6e">currentIndex = index++;</p>



<p id="SE-b82e621c-a780-4ab1-b881-15698dca39f2">const port = ports[currentIndex];</p>



<p id="SE-891a9e5d-b177-4519-bb42-9a41a2c42962">​</p>



<p id="SE-cb30df01-c329-4c2d-84ee-0cbeb29d2b87">portProgressDiv.textContent =</p>



<p id="SE-922b56ca-028c-4757-b7fe-1d10f453860b">`진행: ${done} / ${total} (현재 포트: ${port})`;</p>



<p id="SE-6b4eea01-f868-4299-be9a-865511a1c6f0">​</p>



<p id="SE-25024a7b-0248-4538-89b0-0852eae2e386">try {</p>



<p id="SE-67031a33-1dae-486a-9500-f84fe26aa26b">const data = await scanPortOnce(target, port);</p>



<p id="SE-5cda0556-5865-44ae-8aa8-123dae9a5ba0">​</p>



<p id="SE-05f81fb1-e138-4e6a-9ec7-cedbdb011220">const tr = document.createElement(&#8220;tr&#8221;);</p>



<p id="SE-f5afb984-66c8-4308-be81-1f204d8af233">const tdPort = document.createElement(&#8220;td&#8221;);</p>



<p id="SE-3bda6df2-8270-4a01-8703-945663da4b08">const tdStatus = document.createElement(&#8220;td&#8221;);</p>



<p id="SE-3faaeaf7-b06d-4546-9942-28e8cdc74cec">const tdNote = document.createElement(&#8220;td&#8221;);</p>



<p id="SE-5d449363-ab92-4465-bb14-59035f5996bd">​</p>



<p id="SE-4f52370c-db12-44f7-a539-6b22cd1299bf">tdPort.textContent = port;</p>



<p id="SE-27bb795d-2cd8-4f5f-91b1-5fd609c02b3c">if (data.open) {</p>



<p id="SE-b995f59c-4701-4ecf-9e70-2c7024a268ff">tdStatus.innerHTML = &#8216;&lt;span class=&#8221;status-ok&#8221;&gt;OPEN&lt;/span&gt;&#8217;;</p>



<p id="SE-4313363e-2e54-452c-a690-17febfc1cff4">} else {</p>



<p id="SE-d1b7ff55-8424-4cb2-8fe9-0425dfbe7d1a">tdStatus.innerHTML = &#8216;&lt;span class=&#8221;status-fail&#8221;&gt;CLOSED&lt;/span&gt;&#8217;;</p>



<p id="SE-871323a9-05ac-4e9b-98e6-b998e8253471">}</p>



<p id="SE-a0cdabb1-e090-4393-96f8-fdfa66a8d214">tdNote.textContent = data.note || &#8220;&#8221;;</p>



<p id="SE-dcbdb3e6-134f-4a22-b519-4b334babaf0b">​</p>



<p id="SE-f7d8b3ea-726b-4022-8e74-b4f668a53260">tr.appendChild(tdPort);</p>



<p id="SE-6eff6eef-1fba-4f6d-9b37-301a29b09b56">tr.appendChild(tdStatus);</p>



<p id="SE-c176d652-d123-4f4c-bc20-deca2a999501">tr.appendChild(tdNote);</p>



<p id="SE-bd56f892-2cba-48f2-87c3-4502c6d9d1c4">portTableBody.appendChild(tr);</p>



<p id="SE-7aa2eafb-68b6-4932-9480-2ffc33def4dd">​</p>



<p id="SE-2cb157a0-2655-4004-b7d1-c8cea445302c">portResults.push({</p>



<p id="SE-33a424f5-a15d-4d53-9733-d3ee88a14db9">port: port,</p>



<p id="SE-29f288b5-3b57-48f2-b42d-6ff4a28bde9b">open: !!data.open,</p>



<p id="SE-c07f2890-3a3e-41c1-acac-7ac6e110d45e">note: data.note || &#8220;&#8221;</p>



<p id="SE-e277865a-f2a3-40e8-9ab5-c551f7ca0f16">});</p>



<p id="SE-5a7f30ac-dec3-41d2-88d6-422859c5f8eb">​</p>



<p id="SE-af966d35-4521-4e2b-bcb2-026a1c4edf6d">} catch (e) {</p>



<p id="SE-12012075-91ae-49be-80bf-311d77254cc7">const tr = document.createElement(&#8220;tr&#8221;);</p>



<p id="SE-0972b68f-00ef-4817-ba88-4025f86d0e59">tr.innerHTML = &#8216;&lt;td&gt;&#8217; + port + &#8216;&lt;/td&gt;&#8217; +</p>



<p id="SE-7e02065c-ad55-4969-872d-c8900180f424">&#8216;&lt;td&gt;&lt;span class=&#8221;status-fail&#8221;&gt;ERROR&lt;/span&gt;&lt;/td&gt;&#8217; +</p>



<p id="SE-9b308082-ed6e-47bb-abd5-9851dfb15dbe">&#8216;&lt;td&gt;&#8217; + e + &#8216;&lt;/td&gt;&#8217;;</p>



<p id="SE-297f5dc4-8e1b-4076-9d27-970b83074659">portTableBody.appendChild(tr);</p>



<p id="SE-35aaea8b-ffe6-4b8e-ac19-c4e344b3d90b">​</p>



<p id="SE-462127be-a1a1-483c-a5ec-aeb36fddd234">portResults.push({</p>



<p id="SE-85ff8cf2-ae54-470c-bfc5-bb6a1aaef50f">port: port,</p>



<p id="SE-dc82b968-835a-4b2a-8815-4d51e6351711">open: false,</p>



<p id="SE-bc2aa2f3-481e-454b-8845-da3fe2f1d5ec">note: &#8220;에러: &#8221; + e</p>



<p id="SE-d0011099-9cde-4a43-b55f-867221db7eac">});</p>



<p id="SE-3b13024b-8866-4536-8e92-58a7ce1b49b4">}</p>



<p id="SE-cb38abec-0ece-4e0e-aa4b-c3484a459984">​</p>



<p id="SE-86cfdf89-fdf8-406a-bbfb-0ebbe76648cc">done += 1;</p>



<p id="SE-02f73428-7b2a-4038-a025-c02a508098d5">portProgressDiv.textContent = `진행: ${done} / ${total}`;</p>



<p id="SE-77d1bbc0-9080-4110-ab8f-a409c01e55d7">}</p>



<p id="SE-39d030b7-fcf6-4840-b62b-04d90b3c1e99">}</p>



<p id="SE-803fbec6-23c3-4455-9bd8-ebbc75880e3a">​</p>



<p id="SE-fcf07cca-d16c-4090-ac12-ab3bdb1cc124">const workers = [];</p>



<p id="SE-fd325045-3631-4d6b-97e2-a5644e4ad075">const numWorkers = Math.min(concurrency, total);</p>



<p id="SE-7de62744-9dc1-4d82-9497-2af9659c2390">for (let i = 0; i &lt; numWorkers; i++) {</p>



<p id="SE-2b256aa9-b7ac-4e03-be46-3a6b16f6ae33">workers.push(worker());</p>



<p id="SE-117941cc-c3ed-4e26-835d-135551c5ad88">}</p>



<p id="SE-f2edc15f-10d4-4142-96df-fbbf9ab81bbd">​</p>



<p id="SE-b1ded541-33d2-450e-a561-28e8d181f2d0">await Promise.all(workers);</p>



<p id="SE-7f070788-e701-4b33-a2f4-4f57fc3e00c6">​</p>



<p id="SE-78fd58b7-53b9-4bc7-a71b-42aa7dc96b58">updateOpenPortSummary();</p>



<p id="SE-cdbae70b-e774-4963-b210-f14063c0603e">​</p>



<p id="SE-6231e316-11c2-4cf9-bbb3-201f00d628fa">if (!portScanAbort) {</p>



<p id="SE-20592d3f-216a-490a-81a5-b9fb84431f34">portScanLogDiv.textContent += &#8220;\\n스캔 완료.&#8221;;</p>



<p id="SE-05d7d605-ba5b-47b1-b39c-c1a5bee36a64">} else {</p>



<p id="SE-e0ca1af9-7e1a-4a4e-be35-68174b540114">portScanLogDiv.textContent += &#8220;\\n사용자에 의해 스캔 중단됨.&#8221;;</p>



<p id="SE-b95e5e9b-2b89-4f77-a340-931c9e02024b">}</p>



<p id="SE-2a6de277-1df8-4e8a-ad35-08f308e37b25">​</p>



<p id="SE-854f6817-1607-4859-aef6-a5ae971ace04">portScanStartBtn.disabled = false;</p>



<p id="SE-2eeda5ca-0f37-491a-b5d2-1cd09e0b576f">portScanStopBtn.disabled = true;</p>



<p id="SE-8c2b7477-8a35-43ec-bfff-cfc2f1e255f5">if (portResults.length &gt; 0) {</p>



<p id="SE-9cdcea0d-74ec-4b8d-958c-9b20ae2f4ccd">downloadCsvBtn.disabled = false;</p>



<p id="SE-7f31ee8f-69c4-425b-b2fe-3ea24d8faddf">downloadJsonBtn.disabled = false;</p>



<p id="SE-2f00d6e4-557f-4819-baa3-afb8dd66e19c">}</p>



<p id="SE-65814443-cee6-46bf-b83b-75103e920db5">}</p>



<p id="SE-dad133ff-6a3d-43c0-abf7-6baa6883c32a">​</p>



<p id="SE-9ea129b7-24b6-4893-886d-69e895f4f782">function stopPortScan() {</p>



<p id="SE-9e7fe9e6-dac7-4095-805b-830d319f98e9">portScanAbort = true;</p>



<p id="SE-355e836d-d5a3-412f-9a0d-d20ecadc5b8f">portScanStopBtn.disabled = true;</p>



<p id="SE-d4caf592-5025-43ba-9ef6-fb3fd5037f0e">portScanLogDiv.textContent += &#8220;\\n정지 요청됨&#8230;&#8221;;</p>



<p id="SE-e00505bd-fe82-40b7-acda-91c8b4c8af82">}</p>



<p id="SE-40b520de-a761-4287-a870-140d7683b2fc">​</p>



<p id="SE-6ac9bb25-92a3-4edb-9a28-8e8326aeea83">portScanStartBtn.addEventListener(&#8220;click&#8221;, startPortScan);</p>



<p id="SE-eeebbdc4-567b-4e37-83ea-65ad551cda74">portScanStopBtn.addEventListener(&#8220;click&#8221;, stopPortScan);</p>



<p id="SE-e72b0097-99b6-49e0-bb00-fd7a048cd9d3">downloadCsvBtn.addEventListener(&#8220;click&#8221;, downloadCSV);</p>



<p id="SE-1b2abac2-8894-4cee-b5d5-8b62b71baeb2">downloadJsonBtn.addEventListener(&#8220;click&#8221;, downloadJSON);</p>



<p id="SE-594f91dc-c93f-4b91-b7e2-9a66378912d4">&lt;/script&gt;</p>



<p id="SE-5dbbb3c8-714b-4615-a723-f68f0ff40bb1">&lt;/body&gt;</p>



<p id="SE-fd0a1fec-cf52-4094-8069-59d5422162f4">&lt;/html&gt;</p>



<p id="SE-27618033-3491-4594-ab17-6bf0cc46ab29">&#8220;&#8221;&#8221;</p>



<p id="SE-0ffd78a1-8518-4786-9429-7a58f7287b7b">​</p>



<p id="SE-801b2a6a-58d2-4812-80a4-33b6bc01da53"># &#8212;&#8212;&#8212;&#8212;&#8212;- Ping 관련 함수 &#8212;&#8212;&#8212;&#8212;&#8212;-</p>



<p id="SE-c167bcc5-b0ea-4f04-8f85-fb94c28d0266">​</p>



<p id="SE-46f4ab94-967c-4411-b885-7f362398ca31">def parse_latency(output: str):</p>



<p id="SE-aa7e4c4b-35fc-43b0-9ddb-80d1ac7708bc">&#8220;&#8221;&#8221;</p>



<p id="SE-3e9f77c7-6d2b-425f-94eb-290e33f70291">ping 출력에서 time=XXms / time=XX ms 패턴 찾아서 ms 값 추출</p>



<p id="SE-436e59b9-88a8-42e1-a767-997a19fe4764">&#8220;&#8221;&#8221;</p>



<p id="SE-114cab54-3591-4237-aed3-6bc1487b46e4">pattern = r&#8221;time[=&lt;]\s*([0-9]+(?:\.[0-9]+)?)\s*ms&#8221;</p>



<p id="SE-602b6e35-daa8-4af6-8cea-878853a09427">match = re.search(pattern, output, re.IGNORECASE)</p>



<p id="SE-7a5ed771-2628-4015-9d1e-2ab3115846b8">if match:</p>



<p id="SE-cbbb2487-0b12-465b-ac77-757d7da398b3">try:</p>



<p id="SE-8a612baf-d3a3-4d10-816c-62ee1e89b24d">return float(match.group(1))</p>



<p id="SE-b73a92d0-191a-419c-970d-11337d7703fd">except ValueError:</p>



<p id="SE-2eaf27d7-84e9-49c4-b9a6-eeb30ba0b9bc">return None</p>



<p id="SE-0ce3cb37-ff6b-4948-ba5d-95d04ed9426d">return None</p>



<p id="SE-6f529187-dc39-4d3d-beaa-846fb5ea9c17">​</p>



<p id="SE-de3bbf3c-98de-4535-afde-07e4f00b97b1">​</p>



<p id="SE-3465f8d7-cbc8-4731-9075-106bfdcaa1d6">def ping_once(target: str, timeout_ms: int = 1000):</p>



<p id="SE-5bd13f0a-9d85-49f5-add3-28228329dd92">&#8220;&#8221;&#8221;</p>



<p id="SE-37b7ab3d-24ae-4d43-955b-fcd667a110b5">ping 1회 실행</p>



<p id="SE-2ecb191b-3067-46d0-beeb-c00c765622a2">&#8220;&#8221;&#8221;</p>



<p id="SE-79446186-a679-4b5e-a8cd-d8eb6cf4566b">system = platform.system().lower()</p>



<p id="SE-500f6aab-9547-46b6-9c48-2d0fdd1cacce">​</p>



<p id="SE-161e37a0-3e97-4602-9c34-2f3c497f884b">if system.startswith(&#8220;win&#8221;):</p>



<p id="SE-ca30656a-1523-401e-9203-20e263be39a4">cmd = [&#8220;ping&#8221;, &#8220;-n&#8221;, &#8220;1&#8221;, &#8220;-w&#8221;, str(timeout_ms), target]</p>



<p id="SE-887d44bc-08ba-4bd9-acc2-b9a76f89f98e">else:</p>



<p id="SE-1410a13c-34c8-4b5a-9175-553839608a6e">timeout_s = max(int(timeout_ms / 1000), 1)</p>



<p id="SE-083ffa5a-b050-4e5f-aa29-3b55ca73da77">cmd = [&#8220;ping&#8221;, &#8220;-c&#8221;, &#8220;1&#8221;, &#8220;-W&#8221;, str(timeout_s), target]</p>



<p id="SE-1ac12298-8dc0-43f6-872a-5a19a698d72a">​</p>



<p id="SE-36e5b062-4031-4ad6-9b0c-1985669dae6b">try:</p>



<p id="SE-ad0f5fd1-cc03-4377-9e3f-e5110dbff1ce">result = subprocess.run(</p>



<p id="SE-01d7f97a-52bf-422a-bfe7-464a10f4fada">cmd,</p>



<p id="SE-fc3480c3-341e-4d45-a24f-d1aaa429f462">stdout=subprocess.PIPE,</p>



<p id="SE-7b0b5ac1-d060-4f3f-a8be-8c82ef429771">stderr=subprocess.PIPE,</p>



<p id="SE-03daec3d-6473-464f-a0f2-af4d78dff87c">text=True</p>



<p id="SE-364a79fa-b8c7-4ac1-b4f2-3f7260dd111a">)</p>



<p id="SE-097e6442-b2a3-4768-8cd5-8ae57652ebbb">except Exception as e:</p>



<p id="SE-22670ac2-18c7-4f93-ac78-908594363336">return False, None, f&#8221;ping 실행 에러: {e}&#8221;</p>



<p id="SE-201cb634-1fbb-4097-bde3-0060f2468ba7">​</p>



<p id="SE-f1919f4d-176b-4eab-bc6a-e45f91bc239f">success = (result.returncode == 0)</p>



<p id="SE-0b4d69f9-4d27-4059-ae8c-43e19a3de440">latency = parse_latency(result.stdout)</p>



<p id="SE-d19c666b-03be-4e5b-96ea-ea79c558027b">raw_output = result.stdout if result.stdout.strip() else result.stderr</p>



<p id="SE-c9c03fff-8bb1-4203-b72d-43b67b3caa35">​</p>



<p id="SE-3e01c936-4aa4-48c1-b041-b2f1cb5b25ca">return success, latency, raw_output</p>



<p id="SE-d4b4f5fa-d32e-4904-ad7b-9d6fe66ae657">​</p>



<p id="SE-127738e2-beea-4479-8a29-e48fb4a79794">​</p>



<p id="SE-c8c87432-d366-49dd-9f58-d04cb373786b">def ping_multi(target: str, count: int, timeout_ms: int = 1000, interval_sec: float = 0.2):</p>



<p id="SE-90c01f5f-f4b5-4efc-9d2e-6f50ad948fbe">&#8220;&#8221;&#8221;</p>



<p id="SE-9bd50605-9874-40e7-a78d-7460b8d4bebc">같은 대상에 대해 여러 번 ping 실행</p>



<p id="SE-51647577-8d10-45c3-968a-4ba56234c937">&#8220;&#8221;&#8221;</p>



<p id="SE-52561ede-d6bd-4374-ac1d-b7b49397c22c">detail_rows = []</p>



<p id="SE-1a1014ff-a72c-481c-8d88-0d1eafb74398">last_output = &#8220;&#8221;</p>



<p id="SE-af939c6a-1c14-4670-808e-924d605d6ad3">for _ in range(count):</p>



<p id="SE-a034bbec-14c8-4382-b46f-dd53ea921bb7">success, latency_ms, raw_output = ping_once(target, timeout_ms=timeout_ms)</p>



<p id="SE-7f9f423b-d9b3-4f34-943c-5bd997b0efe0">last_output = raw_output</p>



<p id="SE-09cfe7f0-21fb-4fbb-ae70-370037691c58">detail_rows.append({</p>



<p id="SE-781ab639-150f-4e26-9d14-5b28f5292a48">&#8220;success&#8221;: success,</p>



<p id="SE-6c8de2e4-c74b-4cec-8d2b-95af1889fd57">&#8220;latency_ms&#8221;: latency_ms,</p>



<p id="SE-11f21ea0-33c4-44f9-ae22-c0bd3fa49402">&#8220;timestamp&#8221;: datetime.now().strftime(&#8220;%H:%M:%S&#8221;),</p>



<p id="SE-0ff60aef-ea47-43d3-9d9e-01b65ee5e540">})</p>



<p id="SE-07bae62b-ec0f-4d96-9783-b222c126fb71">if _ &lt; count &#8211; 1:</p>



<p id="SE-33e0653a-57d6-43c7-8d20-f26919b5039d">time.sleep(interval_sec)</p>



<p id="SE-8793ffb0-a975-40c3-aa5a-a82cb4a6c4f9">​</p>



<p id="SE-e2334cf9-4794-4104-b99b-f664d0609976">total = len(detail_rows)</p>



<p id="SE-f6574ff4-5b2f-416f-9e05-78e14f1839aa">success_cnt = sum(1 for r in detail_rows if r[&#8220;success&#8221;])</p>



<p id="SE-5bb55f33-d365-40a5-852a-460ecd858b25">fail_cnt = total &#8211; success_cnt</p>



<p id="SE-182f9657-e475-4ca8-97f4-c5b39a90331d">loss_rate = round(fail_cnt / total * 100, 1) if total &gt; 0 else 0.0</p>



<p id="SE-36ab1f9c-349c-4a28-a5fc-922dbda8fc81">​</p>



<p id="SE-6dc33cfe-da00-42d4-ae14-11b80cb663dd">latencies = [r[&#8220;latency_ms&#8221;] for r in detail_rows if r[&#8220;latency_ms&#8221;] is not None]</p>



<p id="SE-7a002cda-c1ba-4841-88ca-cf36200636b0">if latencies:</p>



<p id="SE-a35afced-0895-4111-9577-d8afa3207723">min_ms = round(min(latencies), 1)</p>



<p id="SE-86144303-6a5c-4365-8818-2afb55674069">max_ms = round(max(latencies), 1)</p>



<p id="SE-1f777c31-edd3-47d2-9377-7f8e8be85188">avg_ms = round(sum(latencies) / len(latencies), 1)</p>



<p id="SE-b3b07309-2407-49d2-ad44-aca402e7a9c0">else:</p>



<p id="SE-ca2dbc69-5b67-433c-b517-4a9e87e91ef1">min_ms = max_ms = avg_ms = &#8220;-&#8220;</p>



<p id="SE-95afc686-cfdc-4cf2-9529-2933688211b9">​</p>



<p id="SE-d811463f-c417-4039-9e75-9a2aeefc3796">summary = {</p>



<p id="SE-35ef8ca8-5f8d-4e7d-a5ba-a88b9d0f7571">&#8220;total&#8221;: total,</p>



<p id="SE-622bebf7-3928-4da1-8b7f-653ba070fa87">&#8220;success&#8221;: success_cnt,</p>



<p id="SE-93dccdf8-9715-49b6-a689-3f76e7f4e423">&#8220;fail&#8221;: fail_cnt,</p>



<p id="SE-05dd2d12-521d-42c2-8ce9-5ee21a1bb72f">&#8220;loss_rate&#8221;: loss_rate,</p>



<p id="SE-e16229b5-bd43-4490-ba19-c23703a0e99e">&#8220;min_ms&#8221;: min_ms,</p>



<p id="SE-4f20f249-a861-4a83-834c-e911ae974454">&#8220;max_ms&#8221;: max_ms,</p>



<p id="SE-dc505b02-5d3c-47d6-9cbc-22c369dedfb4">&#8220;avg_ms&#8221;: avg_ms,</p>



<p id="SE-1fe3d44c-2cc6-481a-9b14-ecad5a38c749">}</p>



<p id="SE-2d787dfd-290d-49e4-89f6-0ba8932396cd">​</p>



<p id="SE-0ad98fb0-1997-4bf2-adb0-f52741b38a38">return summary, detail_rows, last_output</p>



<p id="SE-ed32c3b8-4dd5-4b24-be82-df77c0c4be22">​</p>



<p id="SE-3afadcc7-1e54-43b1-8caa-ed4f575ed38e">​</p>



<p id="SE-202e6572-9a1e-461c-8d6b-db678218bd87"># &#8212;&#8212;&#8212;&#8212;&#8212;- 포트 스캔 관련 함수 &#8212;&#8212;&#8212;&#8212;&#8212;-</p>



<p id="SE-3d7c1558-9499-4d4e-80e8-3c7bd5f9611e">​</p>



<p id="SE-26af34e0-f5c1-47cd-b60e-17efcf5aad04">def scan_port(target: str, port: int, timeout: float = 1.0):</p>



<p id="SE-77d1fb0f-61c0-4c7c-8804-4287e6038c73">&#8220;&#8221;&#8221;</p>



<p id="SE-6a7bc5d8-34e0-4063-9cc9-97837778355b">TCP 포트 1개 스캔 (열림/닫힘)</p>



<p id="SE-1e12825e-6f0c-4854-867c-cef42c4dfab3">&#8220;&#8221;&#8221;</p>



<p id="SE-5eca775b-b2e0-4dcf-8934-fcea6b3f0769">sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</p>



<p id="SE-a208890e-ac4f-48cc-96ba-87a685b367a4">sock.settimeout(timeout)</p>



<p id="SE-c08943d7-d295-49d4-b3b4-62bf6a9c0367">try:</p>



<p id="SE-220845e6-1ad0-442b-a1dd-53f3f65db399">result = sock.connect_ex((target, port))</p>



<p id="SE-52e7c97e-3863-4ec6-8a5e-77bfc011ee60">if result == 0:</p>



<p id="SE-094aafeb-2077-43bc-be56-77c4706186a7">return True, &#8220;연결 성공&#8221;</p>



<p id="SE-d5acb821-b50b-414b-9690-f3bac5f6c163">else:</p>



<p id="SE-d2a80913-7284-41ab-83e5-f0b05d065395">return False, f&#8221;연결 실패 (코드: {result})&#8221;</p>



<p id="SE-3d0e0e31-0e58-46e4-b6e9-b6178fd73c0f">except Exception as e:</p>



<p id="SE-254e41db-4114-4ac3-be62-783543d4eacd">return False, f&#8221;예외 발생: {e}&#8221;</p>



<p id="SE-ea17cb2b-089a-4e4f-8245-eeba497aaba6">finally:</p>



<p id="SE-b2bdd5f7-313a-4f91-8c8d-14d33753b4cb">sock.close()</p>



<p id="SE-b6645d30-c9e5-4448-9612-973f2888235e">​</p>



<p id="SE-c7c159b5-7339-4cad-b693-8587a27b92ff">​</p>



<p id="SE-8f4ca71f-cf00-4f8a-90cc-f2fb52dd994d"># &#8212;&#8212;&#8212;&#8212;&#8212;- Flask 라우팅 &#8212;&#8212;&#8212;&#8212;&#8212;-</p>



<p id="SE-0539e5d2-2c7f-4864-8bce-642079ede83d">​</p>



<p id="SE-8bd9d394-ae68-496b-b0ce-71c83b5779e0">@app.route(&#8220;/&#8221;, methods=[&#8220;GET&#8221;])</p>



<p id="SE-ce4acac2-7e96-4fe4-9b6a-8dd3c8e7de59">def index():</p>



<p id="SE-481a55e3-bad3-4e94-9fff-86e8953d818d">return render_template_string(</p>



<p id="SE-30483b4e-a189-4417-bd7f-c1ad319524ae">PAGE_TEMPLATE,</p>



<p id="SE-9f9f9c32-576f-4fdc-bf6f-e7dfaeda9e17">target=&#8221;&#8221;,</p>



<p id="SE-bcab2cac-a490-4b10-b04b-c3f8bd1bb4a5">count=4,</p>



<p id="SE-95c85d9e-6b37-44dc-bbae-654bc2d5b0a8">continuous=False,</p>



<p id="SE-86f62eeb-ee4e-441a-8ea9-a1ee9ecbcb6d">ports=&#8221;&#8221;,</p>



<p id="SE-f6696879-715d-434b-b205-427885237c30">concurrency=20,</p>



<p id="SE-717edeb3-5fba-4168-978e-b521a9120f7f">summary=None,</p>



<p id="SE-34e9ac01-4820-48f4-befd-91d3e6559e60">detail=None,</p>



<p id="SE-25a05b19-8472-4474-9188-6bfc4b6bf906">raw_output=None,</p>



<p id="SE-98dae548-9ea2-4d49-a343-75a961863cde">)</p>



<p id="SE-44dc5ca7-707d-45ba-b9bf-cd91769a7f66">​</p>



<p id="SE-e608e72b-4d29-477f-ad48-b9b0d350a117">​</p>



<p id="SE-859975e7-d481-420c-860d-960f75b75426">@app.route(&#8220;/run_test&#8221;, methods=[&#8220;POST&#8221;])</p>



<p id="SE-8474d683-382e-4718-9ce6-10f1af064630">def run_test():</p>



<p id="SE-b0bae9f3-dc5f-43f6-9faf-41c936ef5fca">target = request.form.get(&#8220;target&#8221;, &#8220;&#8221;).strip()</p>



<p id="SE-c894b737-9ae6-4e40-8829-4fd4def8cf90">count_str = request.form.get(&#8220;count&#8221;, &#8220;4&#8221;).strip()</p>



<p id="SE-cd368ace-7967-40d8-bf5f-f9903f9983ab">continuous = bool(request.form.get(&#8220;continuous&#8221;))</p>



<p id="SE-e19ad8d4-42a0-44fe-91e6-011dda95e4fd">ports_input = request.form.get(&#8220;ports&#8221;, &#8220;&#8221;).strip()</p>



<p id="SE-6f7b1975-775c-4dd3-acf9-7a4935e5153c">​</p>



<p id="SE-1fe61d6b-657f-4e55-8918-3c738c21169b">summary = None</p>



<p id="SE-2adbd9c1-ca21-4a2f-b6d2-67cb097d02b4">detail = None</p>



<p id="SE-ce10e609-5ab2-4c7d-8c7b-0038c54f8a00">raw_output = None</p>



<p id="SE-c5413bf6-191e-4890-84db-0f5d9e856934">​</p>



<p id="SE-a51ca242-a4d8-4e6d-867c-f161bda12ecd">if not target:</p>



<p id="SE-b47e8803-7eb2-405b-9781-f8eaa2694575">return render_template_string(</p>



<p id="SE-d2f42c89-2c6e-4278-b270-33a5c667f0b8">PAGE_TEMPLATE,</p>



<p id="SE-433f6990-b262-4165-8594-8b61ee1ecdeb">target=target,</p>



<p id="SE-43084af5-6f2a-4717-9151-94fd0646e2de">count=count_str,</p>



<p id="SE-7a567e17-d28a-4f23-9228-2ea1aca0e13e">continuous=continuous,</p>



<p id="SE-cc355c7f-fa75-44d3-920e-8cf7acc33f8b">ports=ports_input,</p>



<p id="SE-1023926b-3ef5-47dd-ba95-4fdd6f6059d8">concurrency=20,</p>



<p id="SE-37c3e7a8-d81f-43f5-9ca3-0aeff9b60b3e">summary=None,</p>



<p id="SE-459530a7-a24c-4298-a52c-6331a9a8cb89">detail=None,</p>



<p id="SE-00a940cf-e35e-47eb-9411-4fa2109d3889">raw_output=None,</p>



<p id="SE-3cac88fe-1b50-40f7-b7c7-5669dd503183">)</p>



<p id="SE-c7d0cb62-b46e-48c4-8aef-ca3c6f0e7bc7">​</p>



<p id="SE-bc6a8b49-ceff-428a-b065-6d0f1c18f88f">try:</p>



<p id="SE-a15002c7-693a-4bd4-b65f-d6b6d8961c1d">count = int(count_str)</p>



<p id="SE-fa89d925-6174-4c5d-8e71-1f1105f16a8d">if count &lt;= 0:</p>



<p id="SE-e11854a1-54ed-44e2-a2f9-c0fb16f4d1a8">count = 1</p>



<p id="SE-49602e74-9ac0-4e28-9c0f-da4e805aa21a">except ValueError:</p>



<p id="SE-e21678e3-609a-4b9b-a759-7456f2b16651">count = 4</p>



<p id="SE-f2992d2c-f67b-44d2-967c-009c93ec3450">​</p>



<p id="SE-4c6e4606-51ea-433c-ba3d-1cd827c881e7">summary, detail, raw_output = ping_multi(target, count=count, timeout_ms=1000)</p>



<p id="SE-1db365b4-1ac8-4780-8785-ade3bbed9700">​</p>



<p id="SE-463fc1e7-e5ce-4cdb-b458-511cdd135d73">return render_template_string(</p>



<p id="SE-d9ceb947-e2e0-4a70-ae2f-8d8293ed158a">PAGE_TEMPLATE,</p>



<p id="SE-221d955d-3eec-46c4-b97c-bc60d864642c">target=target,</p>



<p id="SE-1c888fd6-39ea-4980-aef8-5848c8a5d1d6">count=count,</p>



<p id="SE-a12ce0c0-616b-4386-9a26-de9a85aa5548">continuous=False,</p>



<p id="SE-c6af5b4d-158b-4c55-815a-a306d52fa5d6">ports=ports_input,</p>



<p id="SE-d290af3e-bb23-4213-bf43-7d9490961a18">concurrency=20,</p>



<p id="SE-d4ad8655-fc91-4158-a732-6de9c1fffaed">summary=summary,</p>



<p id="SE-2bbe6942-7d7e-43c3-8607-fbec27b19cbc">detail=detail,</p>



<p id="SE-62337ee0-96e5-4922-a3c2-b71c49cf79cc">raw_output=raw_output,</p>



<p id="SE-987aaf34-3666-48e7-b4de-b189d199344e">)</p>



<p id="SE-c1c4de05-84dc-45a0-817f-61a4eef192b0">​</p>



<p id="SE-f21c220e-b06e-44b4-bc70-c19caa984866">​</p>



<p id="SE-1a61ad19-c6b2-4212-84bd-27a6bf24bdbc">@app.route(&#8220;/api/ping_once&#8221;, methods=[&#8220;GET&#8221;])</p>



<p id="SE-cbf5d576-a2ba-4652-9aef-6dbfb2a35403">def api_ping_once():</p>



<p id="SE-806ae674-819a-464e-a52b-b4255cc8e511">target = request.args.get(&#8220;target&#8221;, &#8220;&#8221;).strip()</p>



<p id="SE-30276c71-9a7d-4ede-afa2-0e5113ca390a">if not target:</p>



<p id="SE-b0ffc277-d783-4ce1-b623-cf9435b897e5">return jsonify({</p>



<p id="SE-b1d1fb7c-d382-4d88-8579-8bb8891e7525">&#8220;success&#8221;: False,</p>



<p id="SE-d460e3f6-26c7-4a14-9ada-a4790eb9d48b">&#8220;latency_ms&#8221;: None,</p>



<p id="SE-ae18021f-5869-4900-9b55-e8865ed060fb">&#8220;target&#8221;: &#8220;&#8221;,</p>



<p id="SE-91b5843d-8cde-4e09-bd42-c9401592934f">&#8220;timestamp&#8221;: datetime.now().strftime(&#8220;%H:%M:%S&#8221;),</p>



<p id="SE-5c123d31-2fcf-4bab-94f6-5e8b553688a3">&#8220;error&#8221;: &#8220;target parameter is required&#8221;,</p>



<p id="SE-85f1db72-3fac-4109-a7f7-c5a450ca00e3">})</p>



<p id="SE-58bccd60-23a7-4ae4-8a4a-0417484b79fe">​</p>



<p id="SE-80143e19-af64-470b-96ab-0d4dcdc6aa79">success, latency_ms, raw_output = ping_once(target, timeout_ms=1000)</p>



<p id="SE-ca62ead7-a49d-4d5a-bec0-58f424bd7c48">return jsonify({</p>



<p id="SE-8aa943c2-7d16-4171-9fe4-b2252ba53bee">&#8220;success&#8221;: success,</p>



<p id="SE-1889ff5e-23ce-4a5a-9bea-f0d77de102ea">&#8220;latency_ms&#8221;: latency_ms,</p>



<p id="SE-a9eb4cd8-d7ee-4ed0-9ca8-fd153a00fd89">&#8220;target&#8221;: target,</p>



<p id="SE-8a65c5fc-2c90-427a-87ca-c209ad5b6294">&#8220;timestamp&#8221;: datetime.now().strftime(&#8220;%H:%M:%S&#8221;),</p>



<p id="SE-1451b6ec-aba1-490d-8d9f-f10ab9815ab5">&#8220;raw_output&#8221;: raw_output,</p>



<p id="SE-a3b2d472-554a-4a43-b0ba-9532d1f36323">})</p>



<p id="SE-bf3f6682-392a-4698-adbc-0137457ae8e8">​</p>



<p id="SE-8a1a9e63-699b-4eb2-8f78-98942921cf9d">​</p>



<p id="SE-d5227bbb-8a73-4863-9a12-43dd5479e085">@app.route(&#8220;/api/scan_port&#8221;, methods=[&#8220;GET&#8221;])</p>



<p id="SE-e5b8b59a-62cf-47ea-ace8-5a4441b268f3">def api_scan_port():</p>



<p id="SE-e96ddd6a-b2bb-4365-9187-acd983cfc62b">target = request.args.get(&#8220;target&#8221;, &#8220;&#8221;).strip()</p>



<p id="SE-135b2828-d1a5-4dfb-9f36-b45b39e9263d">port_str = request.args.get(&#8220;port&#8221;, &#8220;&#8221;).strip()</p>



<p id="SE-f96c0c32-4868-4cdd-bfb5-45cf3a01fc20">​</p>



<p id="SE-b66649a8-36fe-497a-a910-6fde4a33c8b2">if not target or not port_str:</p>



<p id="SE-1c4914ab-9ac7-4dc0-aa8d-7aa879eaca06">return jsonify({</p>



<p id="SE-e9b67b76-aa46-44b4-91f2-a3d9c581470f">&#8220;open&#8221;: False,</p>



<p id="SE-e0917b76-a977-4571-a9ba-d11add9d7785">&#8220;note&#8221;: &#8220;target 또는 port 파라미터가 없습니다.&#8221;,</p>



<p id="SE-d25e4764-af95-4c41-b236-0386fa7b1a41">})</p>



<p id="SE-acaa5219-e54e-432d-a71d-4be3131aa62d">​</p>



<p id="SE-308ae8d2-995d-49de-abc9-4cee4d709bae">try:</p>



<p id="SE-220e8fb9-0c14-4415-bc4b-5b30eeac9ef5">port = int(port_str)</p>



<p id="SE-98c2196e-2875-44e3-99cd-5bd7c89ae29b">except ValueError:</p>



<p id="SE-42e4f772-07e6-41cd-ac48-0c8557486eea">return jsonify({</p>



<p id="SE-3f99d16d-2cec-444f-ba46-cdb3541da2a3">&#8220;open&#8221;: False,</p>



<p id="SE-daa42912-eab4-490d-b7db-04905e384d21">&#8220;note&#8221;: &#8220;포트 번호가 잘못되었습니다.&#8221;,</p>



<p id="SE-a909f95d-1144-48e3-8718-4287978dd543">})</p>



<p id="SE-df5c2b99-fbb3-4837-b259-066cf3fbc689">​</p>



<p id="SE-624a9d8f-56ad-4b78-a045-b6755cf9f4d3">is_open, note = scan_port(target, port, timeout=1.0)</p>



<p id="SE-346c08fe-0098-4bf9-8091-7ea4a9cbe68a">return jsonify({</p>



<p id="SE-d826bb42-d477-4962-b61c-5b20df2ecacc">&#8220;open&#8221;: is_open,</p>



<p id="SE-25d9f803-dea9-46d8-9ed7-defebed346b1">&#8220;note&#8221;: note,</p>



<p id="SE-4293e1ca-8a85-4cb6-9f0b-05cd19b42f1d">})</p>



<p id="SE-99191ca2-6ed8-4e00-baa5-a3b705f8af76">​</p>



<p id="SE-e18ecaa1-96c2-4bcc-8cd2-af3cb45c3972">​</p>



<p id="SE-dbafe63b-4adf-4e01-a252-1b67788c9543">if __name__ == &#8220;__main__&#8221;:</p>



<p id="SE-2296f39e-2bb9-4507-afb9-528550ea0406"># threaded=True: 동시에 여러 /api/scan_port 요청 처리</p>



<p id="SE-b15f8126-8448-4dfc-b81d-6205827e28f4">app.run(host=&#8221;0.0.0.0&#8243;, port=9999, debug=True, threaded=True)</p>



<p>========== end =====================</p>


<p>게시물 <a href="https://howinfo.kr/%ed%8c%8c%ec%9d%b4%ec%8d%ac%ec%9c%bc%eb%a1%9c-%eb%84%a4%ed%8a%b8%ec%9b%8c%ed%81%ac-ping-portscan-%ec%9b%b9-%ed%99%94%eb%a9%b4%ec%97%90%ec%84%9c-%ec%b2%b4%ed%81%ac-%ed%94%84%eb%a1%9c%ea%b7%b8/">파이썬으로 네트워크 ping, portscan 웹 화면에서 체크 프로그램</a>이 <a href="https://howinfo.kr">하우인포-IT·테크</a>에 처음 등장했습니다.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://howinfo.kr/%ed%8c%8c%ec%9d%b4%ec%8d%ac%ec%9c%bc%eb%a1%9c-%eb%84%a4%ed%8a%b8%ec%9b%8c%ed%81%ac-ping-portscan-%ec%9b%b9-%ed%99%94%eb%a9%b4%ec%97%90%ec%84%9c-%ec%b2%b4%ed%81%ac-%ed%94%84%eb%a1%9c%ea%b7%b8/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
