เซิร์ฟเวอร์ค้นหาว่าพอร์ตลูกค้าใดที่จะส่งไป?


26

ตามที่ฉันเข้าใจแล้วนี่เป็นสิ่งที่เกิดขึ้นเมื่อไคลเอนต์ทำการร้องขอการเชื่อมต่อ:

  1. เซิร์ฟเวอร์จะถูกผูกไว้กับหมายเลขพอร์ตเฉพาะ หมายเลขพอร์ตนั้นจะเชื่อมโยงกับกระบวนการรับฟังเสมอ เนื่องจากมีเพียงเซิร์ฟเวอร์เท่านั้นที่กำลังรับฟังการเชื่อมต่อขาเข้าเราจึงไม่จำเป็นต้องเชื่อมโยงฝั่งไคลเอ็นต์
  2. เซิร์ฟเวอร์จะคอยฟังหมายเลขพอร์ตนั้นต่อไป
  3. ลูกค้าจะส่งconnect()คำขอ
  4. accept()เซิร์ฟเวอร์จะยอมรับคำขอใช้ ทันทีที่เซิร์ฟเวอร์ยอมรับคำขอของลูกค้าเคอร์เนลจะจัดสรรหมายเลขพอร์ตแบบสุ่มสำหรับเซิร์ฟเวอร์เพิ่มเติมsend()และreceive()เนื่องจากหมายเลขพอร์ตเดียวกันบนเซิร์ฟเวอร์ไม่สามารถใช้สำหรับการส่งและฟังได้และพอร์ตก่อนหน้ายังคงอยู่ กำลังฟังการเชื่อมต่อใหม่

จากทั้งหมดนั้นเซิร์ฟเวอร์จะหาพอร์ตที่ไคลเอ็นต์ได้รับอย่างไร ฉันรู้ว่าไคลเอนต์จะส่งเซกเมนต์ TCP พร้อมพอร์ตต้นทางและพอร์ตปลายทางดังนั้นเซิร์ฟเวอร์จะใช้พอร์ตต้นทางของเซ็กเมนต์นั้นเป็นพอร์ตปลายทาง แต่เซิร์ฟเวอร์เรียกใช้ฟังก์ชันใดเพื่อค้นหาข้อมูลเกี่ยวกับพอร์ตนั้น มันคือaccept()อะไร


ที่เกี่ยวข้อง: stackoverflow.com/questions/14388706/…
Pacerier

คำตอบ:


33

เป็นส่วนหนึ่งของส่วนหัว TCP (หรือ UDP ฯลฯ ) ในแพ็คเก็ต ดังนั้นเซิร์ฟเวอร์ค้นหาเพราะลูกค้าบอก ซึ่งคล้ายกับวิธีค้นหาที่อยู่ IP ของลูกค้า (ซึ่งเป็นส่วนหนึ่งของส่วนหัว IP)

เช่นทุกแพ็คเก็ต TCP มีส่วนหัว IP (ที่มี IP ต้นทาง IP ปลายทางและโปรโตคอล [TCP] อย่างน้อย) จากนั้นมีส่วนหัว TCP (พร้อมพอร์ตต้นทางและปลายทางและอีกมากมาย)

เมื่อเคอร์เนลได้รับแพ็คเก็ต SYN (จุดเริ่มต้นของการเชื่อมต่อ TCP) ด้วย IP ระยะไกลที่ 10.11.12.13 (ในส่วนหัว IP) และพอร์ตระยะไกล 12345 (ในส่วนหัว TCP) จากนั้นจะรู้ IP ระยะไกลและพอร์ต . มันส่งกลับ SYN | ACK หากได้รับ ACK กลับมาการlistenโทรจะส่งคืนซ็อกเก็ตใหม่ให้ตั้งค่าสำหรับการเชื่อมต่อนั้น

ซ็อกเก็ต TCP มีการระบุอย่างไม่ซ้ำกันโดยค่าสี่ค่า (IP ระยะไกล IP ท้องถิ่นพอร์ตระยะไกลพอร์ตท้องถิ่น) คุณสามารถเชื่อมต่อ / ซ็อกเก็ตได้หลายตัวตราบใดที่มีอย่างน้อยหนึ่งตัวที่แตกต่างกัน

โดยทั่วไปแล้วพอร์ตโลคัลและโลคัล IP จะเหมือนกันสำหรับการเชื่อมต่อทั้งหมดไปยังกระบวนการเซิร์ฟเวอร์ (เช่นการเชื่อมต่อทั้งหมดไปยัง sshd จะอยู่บน local-ip: 22) หากเครื่องระยะไกลหนึ่งเครื่องทำการเชื่อมต่อหลายเครื่องแต่ละเครื่องจะใช้พอร์ตระยะไกลที่แตกต่างกัน ดังนั้นทุกอย่างยกเว้นรีโมตพอร์ตจะเหมือนกัน แต่ก็ไม่เป็นไร - หนึ่งในสี่เท่านั้นที่จะแตกต่างกัน

คุณสามารถใช้เช่น wirehsark เพื่อดูแพ็คเก็ตและมันจะติดป้ายกำกับข้อมูลทั้งหมดสำหรับคุณ นี่คือพอร์ตต้นทางที่ไฮไลต์ (สังเกตว่ามันถูกเน้นอยู่ในแพ็กเก็ตถอดรหัสเช่นเดียวกับฐานสิบหกที่ด้านล่าง):

Wireshark แสดงแพ็กเก็ต TCP SYN


> ขอบคุณสำหรับคำอธิบายดังนั้นคุณตั้งใจจะบอกว่าตัวบอกซ็อกเก็ตเซิร์ฟเวอร์ใหม่ (เช่น tuple) ที่ได้รับหลังจาก accept () จะมีรายละเอียดพอร์ตไคลเอ็นต์และที่อยู่ไคลเอนต์ของไคลเอ็นต์และการใช้เซิร์ฟเวอร์ตัวอธิบายซ็อกเก็ตใหม่กำลังส่งและรับข้อมูล และจากไคลเอนต์ตัวอธิบายไฟล์ซ็อกเก็ตใหม่จะมีหมายเลขพอร์ตเซิร์ฟเวอร์ใหม่ที่กำหนดโดยเคอร์เนล, เซิร์ฟเวอร์ ip, ไคลเอนต์ IP และพอร์ตไคลเอ็นต์ฉันใช่ไหม?
Subi Suresh

@SubiSuresh ใช่ tuple จะถูกเก็บไว้ภายในเคอร์เนลซึ่งเกี่ยวข้องกับไฟล์ descriptor นั้น
derobert

> ขอบคุณ derobert ดังนั้นฉันจึงสรุปว่าตัวบอกซ็อกเก็ตเซิร์ฟเวอร์ใหม่จะมีพอร์ตไคลเอ็นต์และที่อยู่ไคลเอนต์ซึ่งเซิร์ฟเวอร์ได้รับจาก accept () ความเข้าใจของฉันถูกต้องหรือไม่
Subi Suresh

@SubiSuresh ใช่ถูกต้องแล้ว จากมุมมองของแอปพลิเคชันคุณมักจะไม่สนใจ (ยกเว้นการบันทึก) เคอร์เนลทำให้แน่ใจว่าข้อมูลที่คุณwrite(ฯลฯ ) ไปถูกที่แล้ว
Derobert

> ขอบคุณสำหรับความช่วยเหลือของคุณและฉันคิดว่าฉันได้รับประเด็น ;-)
Subi Suresh

2

"การร้องขอการเชื่อมต่อ (การconnect()เรียกระบบของโปรแกรมไคลเอนต์) ทำให้เกิดการจับมือสามทางแพ็คเก็ตแรกของการจับมือ 3 ทาง (จากไคลเอนต์ไปยังเซิร์ฟเวอร์) มีการตั้งค่าสถานะ SYN และรวมหมายเลขพอร์ต TCP ของโปรแกรมไคลเอนต์ เคอร์เนลกำหนดให้มัน

คุณสามารถดูนี้ในบทความเกี่ยวกับ Nmap เทียบกับแพ็คเก็ต การถอดรหัสแพ็กเก็ต Nmap SYN มีวลี "source.60058> dest.22" การถอดรหัส "แพ็คเก็ต SYN ที่ถูกต้อง" มีวลี "source.35970> dest.80" อยู่ในนั้น แพ็คเก็ต SYN สองตัวบอกเคอร์เนลระยะไกลว่าแพ็คเก็ตนั้นมาจากพอร์ต TCP 60058 และพอร์ต 35970 ตามลำดับ


> แต่ Bruce ที่เกิดขึ้นที่ส่วนท้าย แต่เซิร์ฟเวอร์ของฉันดึงรายละเอียดเช่นหมายเลขพอร์ตเพราะปกติในโปรแกรมเซิร์ฟเวอร์ลูกค้าฉันไม่เคยเห็นฟังก์ชั่นใด ๆ สำหรับดึงพอร์ตไคลเอ็นต์และที่อยู่ไคลเอนต์
Subi Suresh

การเรียกระบบgetpeername()ควรให้คุณทำเช่นนั้นกับซ็อกเก็ตที่เปิดอยู่ การaccept()เรียกระบบที่รหัสเซิร์ฟเวอร์ต้องใช้เพื่อรับตัวอธิบายไฟล์ซ็อกเก็ตเพื่อสื่อสารกลับไปยังไคลเอ็นต์มีพารามิเตอร์ ("sockaddr" ในหน้าคนของฉัน) ที่มีที่อยู่ IP ของลูกค้าในอนาคตและหมายเลขพอร์ต TCP
Bruce Ediger

> โปรดอย่าคิดว่าถ้าฉันได้รับข้อมูลทั้งหมดที่ฉันได้รับฉันเข้าใจว่า accept () กำลังมีโครงสร้าง sockaddr_in เต็มไปด้วยรายละเอียดลูกค้าและตัวบอกสถานะซ็อกเก็ตเซิร์ฟเวอร์ใหม่ที่ส่งคืนหลังจาก accept () จะมีพอร์ตไคลเอ็นต์และที่อยู่โดยอัตโนมัติ ทำไมเราสามารถส่งโดยใช้ send (ตัวอธิบายซ็อกเก็ตเซิร์ฟเวอร์ใหม่) ฉันหวังว่าจะถึงจุดหรือไม่นี่เป็นเพียงเพื่อให้แน่ใจว่าสิ่งที่ฉันเข้าใจถูกต้องดีใช่ไหม
Subi Suresh

@SubiSuresh - ฉันเชื่อว่าคุณเขียนความจริง
Bruce Ediger

1

ซ็อกเก็ต TCP เป็นซ็อกเก็ตที่มุ่งเน้นกระแส ตัวอธิบายซ็อกเก็ตสองตัว (เป็นของคุณและเพียร์ของคุณ) เชื่อมต่อได้อย่างน่าเชื่อถือ ดังนั้นคุณไม่ต้องกังวลเกี่ยวกับพอร์ตของลูกค้า - เพียงแค่เขียนตัวอธิบายซ็อกเก็ตของคุณ!

นอกจากนี้อย่าลังเลที่จะรับชื่อ (2) ถ้าคุณต้องการที่จะรู้ว่าจริงๆ (สำหรับการบันทึกอาจ)


0

การเชื่อมต่อถูกกำหนดโดย tuple (IP ต้นทาง, พอร์ตต้นทาง, IP ปลายทาง, พอร์ตปลายทาง) คำตอบไปในสิ่งที่ตรงกันข้าม


@ vondrand จุดนั้นฉันเข้าใจ von. แต่ฟังก์ชั่นใดที่เซิร์ฟเวอร์รู้เกี่ยวกับหมายเลขพอร์ตไคลเอ็นต์หรือไม่โดยไม่ทราบหมายเลขพอร์ตไคลเอ็นต์ว่าจะส่งไปอย่างไรเซิร์ฟเวอร์ใช้โครงสร้างใน accept () เพื่อดึงข้อมูลไคลเอ็นต์ ท่าเรือ ?
Subi Suresh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.