TCP / HTTP Listening บนพอร์ต: ผู้ใช้หลายคนสามารถแชร์พอร์ตเดียวกันได้อย่างไร
จะเกิดอะไรขึ้นเมื่อเซิร์ฟเวอร์รับฟังการเชื่อมต่อขาเข้าบนพอร์ต TCP ตัวอย่างเช่นสมมติว่าคุณมีเว็บเซิร์ฟเวอร์บนพอร์ต 80 สมมติว่าคอมพิวเตอร์ของคุณมีที่อยู่ IP สาธารณะเป็น 24.14.181.229 และบุคคลที่พยายามเชื่อมต่อกับคุณมีที่อยู่ IP 10.1.2.3 บุคคลนี้สามารถเชื่อมต่อกับคุณได้โดยเปิดซ็อกเก็ต TCP ไปที่ 24.14.181.229:80 ง่ายพอ
โดยสัญชาตญาณ (และผิด) คนส่วนใหญ่คิดว่ามันมีลักษณะดังนี้:
Local Computer | Remote Computer
--------------------------------
<local_ip>:80 | <foreign_ip>:80
^^ not actually what happens, but this is the conceptual model a lot of people have in mind.
นี่เป็นเรื่องง่ายเพราะจากมุมมองของไคลเอนต์เขามีที่อยู่ IP และเชื่อมต่อกับเซิร์ฟเวอร์ที่ IP: PORT เนื่องจากไคลเอนต์เชื่อมต่อกับพอร์ต 80 แล้วพอร์ตของเขาก็ต้องเป็น 80 ด้วย? นี่เป็นสิ่งที่สมเหตุสมผลที่จะคิด แต่จริงๆแล้วไม่ใช่สิ่งที่เกิดขึ้น หากถูกต้องเราสามารถให้บริการผู้ใช้เพียงคนเดียวต่อที่อยู่ IP ต่างประเทศ เมื่อคอมพิวเตอร์ระยะไกลเชื่อมต่อเขาจะเชื่อมต่อพอร์ต 80 ถึงพอร์ต 80 และไม่มีใครสามารถเชื่อมต่อได้
ต้องเข้าใจสามสิ่ง:
1. ) บนเซิร์ฟเวอร์กระบวนการกำลังฟังบนพอร์ต เมื่อได้รับการเชื่อมต่อแล้วจะส่งต่อไปยังเธรดอื่น การสื่อสารไม่เคยทำให้พอร์ตการฟังติดขัด
2. ) การเชื่อมต่อถูกระบุโดยเฉพาะโดย OS โดย 5-tuple ต่อไปนี้: (local-IP, local-port, remote-IP, remote-port, protocol) หากองค์ประกอบใด ๆ ในทูเปิลแตกต่างกันแสดงว่าเป็นการเชื่อมต่อที่เป็นอิสระอย่างสมบูรณ์
3. ) เมื่อมีการเชื่อมต่อไปยังเซิร์ฟเวอร์ของลูกค้าก็หยิบสุ่มพอร์ตแหล่งที่ไม่ได้ใช้สูงใบสั่ง ด้วยวิธีนี้ไคลเอนต์เดียวสามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้ถึง 64k สำหรับพอร์ตปลายทางเดียวกัน
ดังนั้นนี่คือสิ่งที่สร้างขึ้นเมื่อไคลเอนต์เชื่อมต่อกับเซิร์ฟเวอร์:
Local Computer | Remote Computer | Role
-----------------------------------------------------------
0.0.0.0:80 | <none> | LISTENING
127.0.0.1:80 | 10.1.2.3:<random_port> | ESTABLISHED
มองไปที่สิ่งที่เกิดขึ้นจริง
ขั้นแรกให้ใช้ netstat เพื่อดูว่าเกิดอะไรขึ้นกับคอมพิวเตอร์เครื่องนี้ เราจะใช้พอร์ต 500 แทน 80 (เนื่องจากมีหลายสิ่งเกิดขึ้นบนพอร์ต 80 เนื่องจากเป็นพอร์ตทั่วไป แต่ใช้งานได้จริงไม่ได้สร้างความแตกต่าง)
netstat -atnp | grep -i ":500 "
ตามที่คาดไว้เอาต์พุตจะว่างเปล่า ตอนนี้มาเริ่มเว็บเซิร์ฟเวอร์:
sudo python3 -m http.server 500
ตอนนี้นี่คือผลลัพธ์ของการรัน netstat อีกครั้ง:
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
ตอนนี้มีกระบวนการหนึ่งที่กำลังฟังอยู่ (สถานะ: LISTEN) บนพอร์ต 500 ที่อยู่ในเครื่องคือ 0.0.0.0 ซึ่งเป็นรหัสสำหรับ "การรับฟังที่อยู่ IP ทั้งหมด" ข้อผิดพลาดง่ายๆในการทำคือฟังเฉพาะพอร์ต 127.0.0.1 ซึ่งจะยอมรับการเชื่อมต่อจากคอมพิวเตอร์ปัจจุบันเท่านั้น ดังนั้นนี่ไม่ใช่การเชื่อมต่อ แต่หมายความว่ากระบวนการที่ขอผูก () กับพอร์ต IP และกระบวนการนั้นมีหน้าที่จัดการการเชื่อมต่อทั้งหมดไปยังพอร์ตนั้น สิ่งนี้บ่งบอกถึงข้อ จำกัด ที่ว่าสามารถมีได้เพียงหนึ่งกระบวนการต่อคอมพิวเตอร์ที่ฟังบนพอร์ต (มีหลายวิธีในการหลีกเลี่ยงการใช้มัลติเพล็กซ์ แต่นี่เป็นหัวข้อที่ซับซ้อนกว่ามาก) หากเว็บเซิร์ฟเวอร์กำลังรับฟังบนพอร์ต 80 จะไม่สามารถแชร์พอร์ตนั้นกับเว็บเซิร์ฟเวอร์อื่นได้
ตอนนี้เรามาเชื่อมต่อผู้ใช้กับเครื่องของเรา:
quicknet -m tcp -t localhost:500 -p Test payload.
นี่คือสคริปต์ง่ายๆ ( https://github.com/grokit/quickweb ) ที่เปิดซ็อกเก็ต TCP ส่ง payload ("Test payload." ในกรณีนี้) รอสักครู่แล้วตัดการเชื่อมต่อ การทำ netstat อีกครั้งในขณะที่เกิดขึ้นจะแสดงสิ่งต่อไปนี้:
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
tcp 0 0 192.168.1.10:500 192.168.1.13:54240 ESTABLISHED -
หากคุณเชื่อมต่อกับไคลเอนต์อื่นและทำ netstat อีกครั้งคุณจะเห็นสิ่งต่อไปนี้:
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:500 0.0.0.0:* LISTEN -
tcp 0 0 192.168.1.10:500 192.168.1.13:26813 ESTABLISHED -
... นั่นคือไคลเอนต์ใช้พอร์ตสุ่มอื่นสำหรับการเชื่อมต่อ ดังนั้นจึงไม่มีความสับสนระหว่างที่อยู่ IP