อ่าน 10 นาที
ซ็อกเก็ตเบิร์กลีย์
Berkeley socketsคืออินเทอร์เฟซการเขียนโปรแกรมแอปพลิเคชัน (API) สำหรับInternet domain socketsและUnix domain socketsซึ่งใช้สำหรับการสื่อสารระหว่างกระบวนการ (IPC)...
ซ็อกเก็ตเบิร์กลีย์
Berkeley socketsคืออินเทอร์เฟซการเขียนโปรแกรมแอปพลิเคชัน (API) สำหรับInternet domain socketsและUnix domain socketsซึ่งใช้สำหรับการสื่อสารระหว่างกระบวนการ (IPC) โดยทั่วไปแล้วจะถูกนำไปใช้ใน รูปแบบของ ไลบรารีของโมดูลที่สามารถเชื่อมโยงกันได้ Berkeley sockets มีต้นกำเนิดมาจาก ระบบปฏิบัติการ Unix 4.2BSDซึ่งเปิดตัวในปี 1983
ซ็อกเก็ตคือการแสดงแทนเชิงนามธรรม ( แฮนด์เดิล ) สำหรับจุดสิ้นสุดภายในของเส้นทางการสื่อสารเครือข่าย API ซ็อกเก็ตของ Berkeley แสดงซ็อกเก็ตในรูปแบบของตัวระบุไฟล์ตามหลักการของ Unixซึ่งเป็นอินเทอร์เฟซทั่วไปสำหรับการรับและส่งข้อมูลไปยังสตรีมข้อมูล
ซ็อกเก็ต Berkeley พัฒนาขึ้นโดยมีการปรับเปลี่ยนเพียงเล็กน้อยจากมาตรฐานที่ใช้กันโดยทั่วไปไปเป็นส่วนประกอบหนึ่งของ ข้อกำหนด POSIXคำว่าซ็อกเก็ต POSIX นั้น มีความหมายเหมือนกับซ็อกเก็ต Berkeley แทบทุกประการ แต่ก็ยังเป็นที่รู้จักกันในชื่อซ็อกเก็ต BSDเพื่อเป็นการยอมรับว่ามีการใช้งานครั้งแรกใน Berkeley Software Distribution
ประวัติและการนำไปใช้
ซ็อกเก็ตเบิร์กลีย์มีต้นกำเนิดมาจาก ระบบปฏิบัติการBSD Unix 4.2 ซึ่งเปิดตัวในปี 1983 ในฐานะอินเทอร์เฟซการเขียนโปรแกรม อย่างไรก็ตาม จนกระทั่งปี 1989 มหาวิทยาลัยแคลิฟอร์เนีย เบิร์กลีย์ จึงสามารถ เผยแพร่ระบบปฏิบัติการและไลบรารีเครือข่ายเวอร์ชันที่ปราศจากข้อจำกัดด้านลิขสิทธิ์ของUnix ที่เป็นกรรมสิทธิ์ของ บริษัท AT&T ได้
ระบบปฏิบัติการสมัยใหม่ทั้งหมดใช้เวอร์ชันหนึ่งของอินเทอร์เฟซซ็อกเก็ตเบิร์กลีย์ ซึ่งกลายเป็นอินเทอร์เฟซมาตรฐานสำหรับแอปพลิเคชันที่ทำงานบนอินเทอร์เน็ตแม้แต่ การใช้งาน Winsockสำหรับ MS Windows ซึ่งสร้างโดยนักพัฒนาอิสระ ก็ยังปฏิบัติตามมาตรฐานนี้อย่างใกล้ชิด
API ซ็อกเก็ต BSD เขียนด้วยภาษาโปรแกรม Cภาษาโปรแกรมอื่นๆ ส่วนใหญ่มีอินเทอร์เฟซที่คล้ายกัน โดยทั่วไปจะเขียนเป็นไลบรารีห่อหุ้มตาม API ของ C [ 1 ]
ซ็อกเก็ต BSD และ POSIX
เมื่อ Berkeley socket API พัฒนาขึ้นและในที่สุดก็กลายเป็น POSIX socket API [ 2 ]ฟังก์ชันบางอย่างก็ถูกยกเลิกหรือลบออกและแทนที่ด้วยฟังก์ชันอื่น POSIX API ยังได้รับการออกแบบให้สามารถเรียกซ้ำได้และรองรับ IPv6
| การกระทำ | บีเอสดี | โพสิกซ์ |
|---|---|---|
| การแปลงที่อยู่แบบข้อความเป็นที่อยู่แบบแพ็ค | อินเน็ต_อาตัน | อินเน็ต_พตัน |
| การแปลงที่อยู่แบบบรรจุข้อความให้เป็นที่อยู่แบบข้อความ | อินเน็ต_เอ็นโทอา | อินเน็ต_เอ็นท็อป |
| การค้นหาล่วงหน้าสำหรับชื่อโฮสต์/บริการ | รับชื่อโฮสต์, รับที่อยู่โฮสต์, รับชื่อเซิร์ฟเวอร์, รับพอร์ตเซิร์ฟเวอร์ | รับข้อมูลที่อยู่ |
| การค้นหาแบบย้อนกลับสำหรับชื่อโฮสต์/บริการ | รับที่อยู่โฮสต์, รับพอร์ตเซิร์ฟเวอร์ | ข้อมูลชื่อ |
ทางเลือกอื่นๆ
API Transport Layer Interface (TLI) ที่ใช้STREAMSเป็นทางเลือกแทน API ซ็อกเก็ต ระบบหลายระบบที่ให้บริการ API TLI ก็ให้บริการ API ซ็อกเก็ตของ Berkeley ด้วยเช่นกัน
ระบบที่ไม่ใช่ Unix มักจะเปิดเผย Berkeley socket API พร้อมเลเยอร์การแปลไปยัง API เครือข่ายดั้งเดิมPlan 9 [ 3 ]และGenode [ 4 ]ใช้ API ระบบไฟล์ที่มีไฟล์ควบคุมแทนตัวอธิบายไฟล์
ไฟล์ส่วนหัว
อินเทอร์เฟซซ็อกเก็ต Berkeley ถูกกำหนดไว้ในไฟล์ส่วนหัวหลายไฟล์ ชื่อและเนื้อหาของไฟล์เหล่านี้จะแตกต่างกันเล็กน้อยในแต่ละการใช้งาน โดยทั่วไปแล้วจะประกอบด้วย:
| ไฟล์ | คำอธิบาย |
|---|---|
<arpa/inet.h> | ฟังก์ชันสำหรับจัดการที่อยู่ IP ตัวเลข |
<netinet/in.h> | AF_INETและAF_INET6คือตระกูลที่อยู่ IP และตระกูลโปรโตคอลที่เกี่ยวข้อง ได้แก่PF_INETและPF_INET6ซึ่งรวมถึงที่อยู่ IP มาตรฐาน และหมายเลขพอร์ต TCP และ UDP |
<netdb.h> | ฟังก์ชันสำหรับแปลงชื่อโปรโตคอลและชื่อโฮสต์เป็นที่อยู่ตัวเลข ค้นหาข้อมูลทั้งในฐานข้อมูลภายในและบริการชื่อโดเมน |
<sys/socket.h> | ฟังก์ชันหลักของซ็อกเก็ตและโครงสร้างข้อมูล |
<sys/un.h> | ตระกูลแอดเดรส PF_UNIXและPF_LOCALใช้สำหรับการสื่อสารภายในเครื่องระหว่างโปรแกรมที่ทำงานอยู่บนคอมพิวเตอร์เครื่องเดียวกัน |
ฟังก์ชัน Socket API

โดยทั่วไปแล้ว Berkeley Socket API จะมีฟังก์ชันดังต่อไปนี้:
socket()สร้างซ็อกเก็ตใหม่ประเภทหนึ่งที่ระบุด้วยหมายเลขจำนวนเต็ม และจัดสรรทรัพยากรระบบให้กับซ็อกเก็ตนั้นbind()โดยทั่วไปจะใช้ในฝั่งเซิร์ฟเวอร์ และเชื่อมโยงซ็อกเก็ตกับโครงสร้างที่อยู่ซ็อกเก็ต เช่น ที่อยู่ IP ภายในเครื่องที่ระบุและหมายเลขพอร์ตlisten()คำสั่งนี้ใช้ในฝั่งเซิร์ฟเวอร์ และทำให้ซ็อกเก็ต TCP ที่ผูกไว้เข้าสู่สถานะรอรับการเชื่อมต่อconnect()คำสั่งนี้ใช้ฝั่งไคลเอ็นต์ และกำหนดหมายเลขพอร์ตท้องถิ่นที่ว่างให้กับซ็อกเก็ต ในกรณีของซ็อกเก็ต TCP คำสั่งนี้จะทำให้เกิดความพยายามในการสร้างการเชื่อมต่อ TCP ใหม่accept()ฟังก์ชันนี้ใช้ในฝั่งเซิร์ฟเวอร์ โดยจะรับคำขอสร้างการเชื่อมต่อ TCP ใหม่จากไคลเอ็นต์ระยะไกล และสร้างซ็อกเก็ตใหม่ที่เชื่อมโยงกับคู่ที่อยู่ซ็อกเก็ตของการเชื่อมต่อนี้send(),recv(),sendto(), และrecvfrom()ใช้สำหรับส่งและรับข้อมูล นอกจากนี้ยังสามารถใช้ ฟังก์ชันมาตรฐานwrite()และ ได้อีกด้วยread()close()การกระทำดังกล่าวจะทำให้ระบบปล่อยทรัพยากรที่จัดสรรให้กับซ็อกเก็ต ในกรณีของ TCP การเชื่อมต่อจะถูกยุติลงgethostbyname()และgethostbyaddr()ใช้ในการแปลงชื่อโฮสต์และที่อยู่ เฉพาะ IPv4 เท่านั้นgetaddrinfo()และfreeaddrinfo()ใช้ในการแปลงชื่อโฮสต์และที่อยู่ IPv4, IPv6select()ใช้สำหรับระงับการทำงาน โดยรอให้ซ็อกเก็ตอย่างน้อยหนึ่งรายการจากรายการที่ระบุไว้พร้อมสำหรับการอ่าน พร้อมสำหรับการเขียน หรือมีข้อผิดพลาดเกิดขึ้นpoll()ใช้สำหรับตรวจสอบสถานะของซ็อกเก็ตในชุดซ็อกเก็ต สามารถตรวจสอบชุดซ็อกเก็ตได้ว่าสามารถเขียนข้อมูลหรืออ่านข้อมูลจากซ็อกเก็ตใดได้บ้าง หรือเกิดข้อผิดพลาดขึ้นหรือไม่getsockopt()ใช้เพื่อเรียกค่าปัจจุบันของตัวเลือกซ็อกเก็ตเฉพาะสำหรับซ็อกเก็ตที่ระบุsetsockopt()ใช้สำหรับตั้งค่าตัวเลือกซ็อกเก็ตเฉพาะสำหรับซ็อกเก็ตที่ระบุ
ซ็อกเก็ต
ฟังก์ชันนี้socket()สร้างเอนด์พอยต์สำหรับการสื่อสารและส่งคืนตัวระบุไฟล์สำหรับซ็อกเก็ต โดยมีอาร์กิวเมนต์สามตัว:
domainซึ่งระบุตระกูลโปรโตคอลของซ็อกเก็ตที่สร้างขึ้น ตัวอย่างเช่น:- PF_INETสำหรับโปรโตคอลเครือข่ายIPv4 (IPv4-only)
- PF_INET6สำหรับIPv6 (และในบางกรณีสามารถใช้งานร่วมกับ IPv4 ได้)
- PF_UNIXสำหรับซ็อกเก็ตภายในเครื่อง (โดยใช้โหนดระบบไฟล์พิเศษ)
typeหนึ่งใน:- SOCK_STREAM (บริการแบบสตรีมที่เชื่อถือได้ หรือซ็อกเก็ตแบบสตรีม )
- SOCK_DGRAM (บริการดาตาแกรม หรือซ็อกเก็ตดาตาแกรม )
- SOCK_SEQPACKET (บริการแพ็กเก็ตแบบเรียงลำดับที่เชื่อถือได้)
- SOCK_RAW (โปรโตคอลดิบที่อยู่เหนือเลเยอร์เครือข่าย)
protocolระบุโปรโตคอลการขนส่งที่ใช้จริง โปรโตคอลที่พบบ่อยที่สุด ได้แก่IPPROTO_TCP , IPPROTO_SCTP , IPPROTO_UDPและIPPROTO_DCCPโปรโตคอลเหล่านี้ระบุไว้ในไฟล์ สามารถใช้ ค่า0เพื่อเลือกโปรโตคอลเริ่มต้นจากโดเมนและประเภทที่เลือกไว้ได้<netinet/in.h>
ฟังก์ชันจะส่งคืนค่า-1หากเกิดข้อผิดพลาด มิเช่นนั้นจะส่งคืนค่าจำนวนเต็มที่แสดงถึงตัวอธิบายที่กำหนดใหม่
ผูก
bind()ฟังก์ชันนี้ ใช้สำหรับเชื่อมโยงซ็อกเก็ตกับที่อยู่ เมื่อสร้างซ็อกเก็ตด้วยsocket()ฟังก์ชันนี้ จะได้รับเพียงตระกูลโปรโตคอลเท่านั้น แต่จะไม่ได้รับที่อยู่ การเชื่อมโยงนี้ต้องดำเนินการก่อนที่ซ็อกเก็ตจะสามารถรับการเชื่อมต่อจากโฮสต์อื่นได้ ฟังก์ชันนี้มีอาร์กิวเมนต์สามตัว:
sockfdตัวอธิบายที่แสดงถึงซ็อกเก็ตmy_addrตัวชี้ไปยังsockaddrโครงสร้างที่แสดงที่อยู่ที่จะผูกเข้าด้วยกันaddrlenฟิลด์ประเภทsocklen_tที่ระบุขนาดของsockaddrโครงสร้าง
bind()ส่งคืนค่า0หากสำเร็จ และ-1หากเกิดข้อผิดพลาด
ฟัง
หลังจากที่ซ็อกเก็ตได้รับการเชื่อมโยงกับที่อยู่แล้ว ระบบlisten()จะเตรียมซ็อกเก็ตนั้นสำหรับการรับการเชื่อมต่อขาเข้า อย่างไรก็ตาม ขั้นตอนนี้จำเป็นเฉพาะสำหรับโหมดข้อมูลแบบสตรีม (แบบเชื่อมต่อ) เท่านั้น กล่าวคือ สำหรับประเภทซ็อกเก็ต ( SOCK_STREAM, SOCK_SEQPACKET) listen()ต้องใช้สองอาร์กิวเมนต์:
sockfdตัวระบุซ็อกเก็ตที่ถูกต้องbacklogเป็นจำนวนเต็มที่แสดงถึงจำนวนการเชื่อมต่อที่รอคิวอยู่ซึ่งสามารถจัดคิวได้ในเวลาใดเวลาหนึ่ง โดยปกติระบบปฏิบัติการจะกำหนดค่าสูงสุดให้กับค่านี้
เมื่อการเชื่อมต่อได้รับการยอมรับแล้ว ระบบจะนำการเชื่อมต่อออกจากคิว หากสำเร็จจะส่งคืนค่า 0 หากเกิดข้อผิดพลาด จะส่งคืนค่า -1
ยอมรับ
เมื่อแอปพลิเคชันกำลังรอรับการเชื่อมต่อแบบสตรีมจากโฮสต์อื่น แอปพลิเคชันจะได้รับการแจ้งเตือนเกี่ยวกับเหตุการณ์ดังกล่าว (ดูselect()ฟังก์ชัน) และต้องเริ่มต้นการเชื่อมต่อโดยใช้ฟังก์ชัน ฟังก์ชันaccept()นี้จะสร้างซ็อกเก็ตใหม่สำหรับแต่ละการเชื่อมต่อและลบการเชื่อมต่อออกจากคิวการรอรับการเชื่อมต่อ ฟังก์ชันนี้มีอาร์กิวเมนต์ดังต่อไปนี้:
sockfdตัวอธิบายของซ็อกเก็ตการฟังที่มีการเชื่อมต่ออยู่ในคิวcliaddrตัวชี้ไปยังโครงสร้าง sockaddr เพื่อรับข้อมูลที่อยู่ของไคลเอ็นต์addrlenตัวชี้ไปยังsocklen_tตำแหน่งที่ระบุขนาดของโครงสร้างที่อยู่ไคลเอ็นต์ที่ส่งผ่านไปยังaccept()เมื่อaccept()ฟังก์ชันส่งค่ากลับ ตำแหน่งนี้จะมีขนาด (เป็นไบต์) ของโครงสร้างนั้น
accept()ฟังก์ชันนี้จะส่งคืนตัวระบุซ็อกเก็ตใหม่สำหรับการเชื่อมต่อที่ได้รับการยอมรับ หรือค่า-1หากเกิดข้อผิดพลาด การสื่อสารทั้งหมดกับโฮสต์ระยะไกลหลังจากนี้จะเกิดขึ้นผ่านซ็อกเก็ตใหม่นี้
ซ็อกเก็ตดาตาแกรมไม่จำเป็นต้องมีการประมวลผลaccept()เนื่องจากผู้รับสามารถตอบสนองต่อคำขอได้ทันทีโดยใช้ซ็อกเก็ตที่รอรับฟัง
เชื่อมต่อ
connect()สร้างการเชื่อมต่อสื่อสารโดยตรงไปยังโฮสต์ระยะไกลที่ระบุโดยที่อยู่ของมัน ผ่านทางซ็อกเก็ต ซึ่งระบุโดยตัวระบุไฟล์
เมื่อใช้ โปรโตคอล แบบเชื่อมต่อ (connection-oriented protocol) ฟังก์ชันนี้จะสร้างการเชื่อมต่อขึ้น โปรโตคอลบางประเภทเป็นแบบไม่เชื่อมต่อ (connectionless) โดยเฉพาะอย่างยิ่งUser Datagram Protocol (UDP ) เมื่อใช้กับโปรโตคอลแบบไม่เชื่อมต่อ ฟังก์ชันนี้connectจะกำหนดที่อยู่ระยะไกลสำหรับการส่งและรับข้อมูล ทำให้สามารถใช้ฟังก์ชันต่างๆ เช่นsendและrecvได้ ในกรณีเหล่านี้ ฟังก์ชัน connect จะป้องกันการรับดาตาแกรมจากแหล่งอื่นๆ
connect()ส่งคืนค่าจำนวนเต็มที่แสดงรหัสข้อผิดพลาด: 0หมายถึงความสำเร็จ ในขณะที่–1หมายถึงข้อผิดพลาด ในอดีต ในระบบที่สืบทอดมาจาก BSD สถานะของตัวอธิบายซ็อกเก็ตจะไม่ถูกกำหนดหากการเรียกใช้ล้มเหลว (ตามที่ระบุไว้ในข้อกำหนด Single Unix Specification ) ดังนั้น แอปพลิเคชันแบบพกพาควรปิดตัวอธิบายซ็อกเก็ตทันทีและรับตัวอธิบายใหม่ด้วยในกรณีที่การเรียกใช้ล้มเหลว[ 5 ]connectsocket()connect()
gethostbyname และ gethostbyaddr
ฟังก์ชันgethostbyname()และgethostbyaddr()ใช้สำหรับแปลงชื่อโฮสต์และที่อยู่ในระบบชื่อโดเมนหรือกลไกการแปลงชื่อโฮสต์อื่นๆ ของเครื่องโลคัลโฮสต์ (เช่น/etc/hostsการค้นหา) โดยจะส่งคืนพอยเตอร์ไปยังอ็อบเจ็กต์ประเภทstruct hostentซึ่งอธิบายถึง โฮสต์ โปรโตคอลอินเทอร์เน็ตฟังก์ชันเหล่านี้ใช้พารามิเตอร์ต่อไปนี้:
nameระบุชื่อของโฮสต์addrระบุตัวชี้ไปยังอstruct in_addrอบเจ็กต์ที่มีที่อยู่ของโฮสต์lenระบุความยาวเป็นไบต์ของaddrข้อมูลtypeระบุประเภทตระกูลที่อยู่ (เช่นAF_INET) ของที่อยู่โฮสต์
ฟังก์ชันจะส่งคืนค่าNULLเมื่อเกิดข้อผิดพลาด ซึ่งในกรณีนี้อาจมีการตรวจสอบค่าจำนวนเต็มภายนอกเพื่อดูว่าเป็นความล้มเหลวชั่วคราวหรือโฮสต์ที่ไม่ถูกต้องหรือไม่รู้จักหรือไม่ มิฉะนั้นจะส่งคืน ค่าที่ถูกต้องh_errnostruct hostent*
ฟังก์ชันเหล่านี้ไม่ได้เป็นส่วนประกอบของ BSD socket API อย่างเคร่งครัด แต่โดยทั่วไปจะใช้ร่วมกับฟังก์ชัน API สำหรับการค้นหาโฮสต์ ฟังก์ชันเหล่านี้ถือเป็นอินเทอร์เฟซแบบเก่าสำหรับการสอบถามระบบชื่อโดเมน ฟังก์ชันใหม่ที่ไม่ขึ้นกับโปรโตคอลโดยสิ้นเชิง (รองรับ IPv6) ได้ถูกกำหนดขึ้น ฟังก์ชันใหม่เหล่านี้คือgetaddrinfo()และgetnameinfo()และอิงตามโครงสร้างข้อมูลใหม่[ 6 ][[addrinfo]]
ฟังก์ชันทั้งสองนี้ปรากฏขึ้นพร้อมกับ API ซ็อกเก็ต BSD ใน 4.2BSD (1983) [ 7 ]ซึ่งเป็นปีเดียวกับที่ DNS ถูกสร้างขึ้นเป็นครั้งแรก เวอร์ชันแรกๆ ไม่ได้สอบถาม DNS และทำการค้นหา /etc/hosts เท่านั้น เวอร์ชัน 4.3BSD (1984) ได้เพิ่ม DNS เข้ามาในลักษณะที่หยาบๆ การใช้งานในปัจจุบันที่ใช้Name Service Switchมาจาก Solaris และต่อมาคือNetBSD 1.4 (1999) [ 8 ] NSS ซึ่งเดิมทีถูกกำหนดไว้สำหรับNIS+ทำให้ DNS เป็นเพียงหนึ่งในหลายตัวเลือกสำหรับการค้นหาโดยฟังก์ชันเหล่านี้ และสามารถปิดใช้งานการใช้งานได้แม้ในปัจจุบัน[ 9 ]
พิธีการและคำกล่าวทักทายครอบครัว
Berkeley Socket API เป็นอินเทอร์เฟซทั่วไปสำหรับการเชื่อมต่อเครือข่ายและการสื่อสารระหว่างกระบวนการ และรองรับการใช้งานโปรโตคอลเครือข่ายและสถาปัตยกรรมที่อยู่ต่างๆ
ต่อไปนี้คือตัวอย่างตระกูลโปรโตคอล (โดยมีตัวระบุเชิงสัญลักษณ์มาตรฐานนำหน้า) ที่กำหนดไว้ใน การใช้งาน LinuxหรือBSD รุ่นใหม่ :
| ตัวระบุ | ฟังก์ชันหรือการใช้งาน |
|---|---|
| PF_APPLETALK | แอปเปิลทอล์ค |
| PF_ATMPVC | โหมดถ่ายโอนแบบอะซิงโครนัส วงจรเสมือนถาวร |
| PF_ATMSVC | วงจรเสมือนแบบสลับโหมดการถ่ายโอนแบบอะซิงโครนัส |
| PF_AX25 | วิทยุสมัครเล่นAX.25 |
| PF_CAN | เครือข่ายควบคุมพื้นที่ |
| PF_บลูทูธ | ปลั๊กไฟ บลู ทูธ |
| PF_BRIDGE | บริดจ์มัลติโปรโตคอล |
| PF_DECnet | สงวนไว้สำหรับโครงการ DECnet |
| PF_ECONET | เอคอร์น อีโคเน็ต |
| PF_INET | โปรโตคอลอินเทอร์เน็ตเวอร์ชัน 4 |
| PF_INET6 | โปรโตคอลอินเทอร์เน็ตเวอร์ชัน 6 |
| PF_IPX | ระบบแลกเปลี่ยนแพ็กเก็ตเครือข่ายอินเทอร์เน็ตของโนเวลล์ |
| PF_IRDA | ซ็อกเก็ต IrDA |
| PF_KEY | API การจัดการคีย์ PF_KEY |
| PF_LOCAL , PF_UNIX , PF_FILE | ในเครื่องโฮสต์ (pipes และ file-domain) |
| PF_NETROM | วิทยุสมัครเล่น NET/ROM (เกี่ยวข้องกับ AX.25) [ 10 ] |
| PF_NETBEUI | สงวนไว้สำหรับโครงการ 802.2LLC |
| PF_SECURITY | การเรียกกลับด้านความปลอดภัยปลอม AF |
| PF_NETLINK , PF_ROUTE | API การกำหนดเส้นทาง |
| แพ็กเก็ต PF | ซ็อกเก็ตจับแพ็กเก็ต |
| PF_PPPOX | PPP ผ่านซ็อกเก็ต X |
| PF_SNA | โครงการ สถาปัตยกรรมเครือข่ายระบบลินุก ซ์ (SNA) |
| PF_WANPIPE | ซ็อกเก็ต API ของ Sangoma Wanpipe |
ฟังก์ชัน นี้จะสร้างซ็อกเก็ตสำหรับการสื่อสารsocket()โดยระบุตระกูลโปรโตคอลที่ต้องการ ( PF_ -identifier) เป็นอาร์กิวเมนต์
แนวคิดการออกแบบดั้งเดิมของอินเทอร์เฟซซ็อกเก็ตแยกความแตกต่างระหว่างประเภทโปรโตคอล (ตระกูล) และประเภทที่อยู่เฉพาะที่แต่ละประเภทอาจใช้ โดยคาดการณ์ว่าตระกูลโปรโตคอลอาจมีประเภทที่อยู่หลายประเภท ประเภทที่อยู่ถูกกำหนดโดยค่าคงที่เชิงสัญลักษณ์เพิ่มเติม โดยใช้คำนำหน้าAF แทน PF ตัวระบุ AF มีไว้สำหรับโครงสร้างข้อมูลทั้งหมดที่เกี่ยวข้องกับประเภทที่อยู่โดยเฉพาะ ไม่ใช่ตระกูลโปรโตคอล อย่างไรก็ตาม แนวคิดการแยกโปรโตคอลและประเภทที่อยู่นี้ไม่ได้รับการสนับสนุนในการใช้งาน และ ค่าคงที่ AFถูกกำหนดโดยตัวระบุโปรโตคอลที่เกี่ยวข้อง ทำให้ความแตกต่างระหว่าง ค่าคงที่ AFและPF กลาย เป็นข้อโต้แย้งทางเทคนิคที่ไม่มีผลในทางปฏิบัติ อันที่จริง ความสับสนมากมายเกิดขึ้นในการใช้งานที่ถูกต้องของทั้งสองรูปแบบ[ 11 ]
ข้อกำหนด POSIX.1—2008 ไม่ได้ระบุ ค่าคงที่ PF ใดๆ แต่ระบุเฉพาะค่าคงที่AF เท่านั้น [ 12 ]
ซ็อกเก็ตดิบ
ซ็อกเก็ตดิบมีอินเทอร์เฟซที่เรียบง่ายซึ่งข้ามการประมวลผลโดยสแต็ก TCP/IP ของโฮสต์ อนุญาตให้ใช้งานโปรโตคอลเครือข่ายในพื้นที่ผู้ใช้และช่วยในการดีบักสแต็กโปรโตคอล[ 13 ]ซ็อกเก็ตดิบถูกใช้โดยบริการบางอย่าง เช่นICMPซึ่งทำงานที่เลเยอร์อินเทอร์เน็ตของโมเดล TCP/IP
โหมดบล็อกและโหมดไม่บล็อก
ปลั๊กไฟ Berkeley สามารถทำงานได้สองโหมด คือโหมดบล็อกและโหมดไม่บล็อก
ซ็อกเก็ตแบบบล็อกจะไม่ส่งการควบคุมกลับจนกว่าจะส่ง (หรือรับ) ข้อมูลบางส่วนหรือทั้งหมดที่ระบุไว้สำหรับการดำเนินการนั้น เป็นเรื่องปกติที่ซ็อกเก็ตแบบบล็อกจะไม่ส่งข้อมูลทั้งหมด แอปพลิเคชันต้องตรวจสอบค่าที่ส่งคืนเพื่อพิจารณาว่าส่งหรือรับข้อมูลไปกี่ไบต์ และต้องส่งข้อมูลใดๆ ที่ยังไม่ได้ประมวลผลอีกครั้ง[ 14 ]เมื่อใช้ซ็อกเก็ตแบบบล็อก ควรพิจารณาเป็นพิเศษเกี่ยวกับ accept() เนื่องจากอาจยังคงบล็อกหลังจากระบุความสามารถในการอ่าน หากไคลเอนต์ตัดการเชื่อมต่อระหว่างขั้นตอนการเชื่อมต่อ
ซ็อกเก็ตแบบไม่บล็อกจะส่งข้อมูลที่อยู่ในบัฟเฟอร์รับกลับมาและดำเนินการต่อทันที หากเขียนโปรแกรมไม่ถูกต้อง โปรแกรมที่ใช้ซ็อกเก็ตแบบไม่บล็อกจะมีความเสี่ยงต่อสภาวะการแข่งขัน (race condition ) เป็นพิเศษ เนื่องจากความเร็วของลิงก์เครือข่ายมีความแปรปรวน
โดยทั่วไปแล้ว ซ็อกเก็ตจะถูกตั้งค่าให้อยู่ในโหมดบล็อกกิ้งหรือโหมดไม่บล็อกกิ้ง โดย ใช้ฟังก์ชันfcntlและioctl
เต้ารับปลายสาย
ระบบปฏิบัติการจะไม่ปล่อยทรัพยากรที่จัดสรรให้กับซ็อกเก็ตจนกว่าซ็อกเก็ตจะถูกปิด เรื่องนี้สำคัญอย่างยิ่งหากการ เรียก เชื่อมต่อล้มเหลวและจะต้องลองเชื่อมต่อใหม่อีกครั้ง
เมื่อแอปพลิเคชันปิดซ็อกเก็ต เฉพาะอินเทอร์เฟซไปยังซ็อกเก็ตเท่านั้นที่จะถูกทำลาย เคอร์เนลมีหน้าที่ทำลายซ็อกเก็ตภายใน บางครั้ง ซ็อกเก็ตอาจเข้าสู่ สถานะ TIME_WAITทางฝั่งเซิร์ฟเวอร์ได้นานถึง 4 นาที[ 15 ]
ใน ระบบ SVR4การใช้close()อาจทำให้ข้อมูลสูญหาย การใช้shutdown()หรือ SO_LINGER อาจจำเป็นในระบบเหล่านี้เพื่อรับประกันการส่งมอบข้อมูลทั้งหมด[ 16 ]
ตัวอย่างการใช้งานไคลเอนต์-เซิร์ฟเวอร์โดยใช้ TCP
โปรโตคอลควบคุมการส่งข้อมูล (TCP) เป็น โปรโตคอล แบบเชื่อมต่อที่ให้คุณสมบัติการแก้ไขข้อผิดพลาดและประสิทธิภาพที่หลากหลายสำหรับการส่งสตรีมไบต์ กระบวนการจะสร้างซ็อกเก็ต TCP โดยการเรียกใช้socket()ฟังก์ชันด้วยพารามิเตอร์สำหรับตระกูลโปรโตคอล ( PF_INET , PF_INET6 ) โหมดซ็อกเก็ตสำหรับซ็อกเก็ตสตรีม ( SOCK_STREAM ) และตัวระบุโปรโตคอล IP สำหรับ TCP ( IPPROTO_TCP )
เซิร์ฟเวอร์
การตั้งค่าเซิร์ฟเวอร์ TCP ประกอบด้วยขั้นตอนพื้นฐานดังต่อไปนี้:
- การสร้างซ็อกเก็ต TCP ด้วยการเรียกใช้
socket(). - ผูกซ็อกเก็ตเข้ากับพอร์ตรับฟัง
bind()หลังจากตั้งค่าหมายเลขพอร์ตแล้ว - เตรียมซ็อกเก็ตให้พร้อมรับฟังการเชื่อมต่อ (ทำให้เป็นซ็อกเก็ตรับฟัง) ด้วยการเรียกใช้
listen(). - ยอมรับการเชื่อมต่อขาเข้า (
accept()) การดำเนินการนี้จะหยุดกระบวนการจนกว่าจะได้รับการเชื่อมต่อขาเข้า และส่งคืนตัวอธิบายซ็อกเก็ตสำหรับการเชื่อมต่อที่ยอมรับ ตัวอธิบายเริ่มต้นยังคงเป็นตัวอธิบายที่รอรับการเชื่อมต่อ และaccept()สามารถเรียกซ้ำได้อีกครั้งเมื่อใดก็ได้กับซ็อกเก็ตนี้ จนกว่าจะปิดลง - การสื่อสารกับโฮสต์ระยะไกลโดยใช้ฟังก์ชัน API
send()และฟังก์ชันrecv()อเนกประสงค์ทั่วไปwrite()read() - ปิดปลั๊กไฟแต่ละอันที่เปิดไว้หลังใช้งานเรียบร้อยแล้ว
close()
โปรแกรมต่อไปนี้สร้างเซิร์ฟเวอร์ TCP ที่รับฟังการทำงานบนพอร์ตหมายเลข 1100:
#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h>#include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> int main ( void ) { int sockfd = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( sockfd == -1 ) { fprintf ( stderr , "Failed to create socket! \n " ); return EXIT_FAILURE ; } struct sockaddr_in sa = { . sin_family = AF_INET , . sin_port = htons ( 1100 ), . sin_addr . s_addr = htonl ( INADDR_ANY ) }; ถ้า( bind ( sockfd , ( struct sockaddr * ) & sa , sizeof ( sa )) == -1 ) { fprintf ( stderr , "Failed to bind socket! \n " ); close ( sockfd ); return EXIT_FAILURE ; } ถ้า( listen ( sockfd , 10 ) == -1 ) { fprintf ( stderr , "Failed to listen on socket! \n " ); close ( sockfd ); return EXIT_FAILURE ; } while ( true ) { int connfd = accept ( sockfd , NULL , NULL ); if ( connfd == -1 ) { fprintf ( stderr , "Failed to accept connection! \n " ); close ( sockfd ) ; return EXIT_FAILURE; }EXIT_FAILURE ; } // ดำเนินการอ่านและเขียนข้อมูล... // read(connfd, buff, size) if ( shutdown ( connfd , SHUT_RDWR ) == -1 ) { fprintf ( stderr , "ไม่สามารถปิดการเชื่อมต่อได้! \n " ); close ( connfd ); close ( sockfd ); return EXIT_FAILURE ; } close ( connfd ); }ปิด( sockfd ); ส่งคืนEXIT_SUCCESS ; }ลูกค้า
การเขียนโปรแกรมแอปพลิเคชันไคลเอ็นต์ TCP ประกอบด้วยขั้นตอนต่อไปนี้:
- กำลังสร้างซ็อกเก็ต TCP
- การเชื่อมต่อกับเซิร์ฟเวอร์ (
connect()) โดยการส่งsockaddr_inโครงสร้างที่มีsin_familyการตั้งค่าเป็นAF_INETโดยตั้งค่าเป็นพอร์ตที่ปลายทางกำลังรับฟัง (ในลำดับไบต์เครือข่าย) และตั้งค่าเป็นที่อยู่ IP ของเซิร์ฟเวอร์ที่กำลังรับฟัง (ในลำดับไบต์เครือข่ายเช่นกัน)sin_portsin_addr - การสื่อสารกับโฮสต์ระยะไกลโดยใช้ฟังก์ชัน API
send()และฟังก์ชันrecv()อเนกประสงค์ทั่วไปwrite()read() - ปิดปลั๊กไฟทุกอันที่เปิดไว้หลังใช้งานเสร็จเรียบร้อย
close()แล้ว
#include <stdio.h> #include <stdlib.h> #include <string.h>#include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> int main ( void ) { int sockfd = socket ( PF_INET , SOCK_STREAM , IPPROTO_TCP ); if ( sockfd == -1 ) { fprintf ( stderr , "Failed to create socket! \n " ); return EXIT_FAILURE ; } struct sockaddr_in sa = { . sin_family = AF_INET , . sin_port = htons ( 1100 ), }; int res = inet_pton ( AF_INET , "192.168.1.3" , & sa . sin_addr );ถ้า( connect ( sockfd , ( struct sockaddr * ) & sa , sizeof ( sa )) == -1 ) { fprintf ( stderr , "Failed to establish connection! \n " ); close ( sockfd ); return EXIT_FAILURE ; } // ดำเนินการอ่านเขียน... close ( sockfd ); return EXIT_SUCCESS ; }ตัวอย่างการใช้งานไคลเอ็นต์เซิร์ฟเวอร์โดยใช้ UDP
โปรโตคอลUser Datagram Protocol (UDP) เป็น โปรโตคอล แบบไร้การเชื่อมต่อที่ไม่มีการรับประกันการส่งมอบ แพ็กเก็ต UDP อาจมาถึงไม่เรียงลำดับ หลายครั้ง หรืออาจไม่มาถึงเลยก็ได้ ด้วยการออกแบบที่เรียบง่ายเช่นนี้ UDP จึงมีค่าใช้จ่ายน้อยกว่า TCP อย่างมาก การเป็นแบบไร้การเชื่อมต่อหมายความว่าไม่มีแนวคิดเรื่องสตรีมหรือการเชื่อมต่อถาวรระหว่างโฮสต์สองตัว ข้อมูลดังกล่าวเรียกว่าดาตาแกรม ( ซ็อกเก็ตดาตาแกรม )
พื้นที่แอดเดรสของ UDP ซึ่งเป็นพื้นที่ของหมายเลขพอร์ต UDP (ในศัพท์เฉพาะของ ISO เรียกว่าTSAPs ) นั้นแยกออกจากพื้นที่แอดเดรสของพอร์ต TCP อย่างสิ้นเชิง
เซิร์ฟเวอร์
แอปพลิเคชันอาจตั้งค่าเซิร์ฟเวอร์ UDP บนพอร์ตหมายเลข7654ดังต่อไปนี้ โปรแกรมประกอบด้วยลูปอนันต์ที่รับดาตาแกรม UDP ด้วยrecvfrom()ฟังก์ชัน
#include <stdbool.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h>#include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h>int main ( void ) { struct sockaddr_in sa = { . sin_family = AF_INET , . sin_addr . s_addr = htonl ( INADDR_ANY ), . sin_port = htons ( 7654 ) }; char buffer [ 1024 ]; ssize_t recsize ; socklen_t fromlen = sizeof ( sa );int sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); if ( bind ( sock , ( struct sockaddr * ) & sa , sizeof ( sa )) == -1 ) { fprintf ( stderr , "Failed to bind socket! \n " ); close ( sock ); return EXIT_FAILURE ; }ในขณะที่( true ) { recsize = recvfrom ( sock , ( void * ) buffer , sizeof buffer , 0 , ( struct sockaddr * ) & sa , & fromlen ); ถ้าrecsize < 0 { fprintf ( stderr , " %s \n " , strerror ( errno ) ); ส่งคืนEXIT_FAILURE ; } printf ( "recsize: %d \n " , ( int ) recsize ); sleep ( 1 ); printf ( "datagram: %.*s \n " , ( int ) recsize , buffer ); } }ลูกค้า
ต่อไปนี้เป็นโปรแกรมไคลเอ็นต์สำหรับส่งแพ็กเก็ต UDP ที่มีข้อความ "Hello, world!" ไปยังที่อยู่127.0.0.1และหมายเลข7654พอร์ต
#include <errno.h> #include <stdlib.h> #include <stdio.h> #include <string.h>#include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h>int main ( void ) { struct sockaddr_in sa = { // ที่อยู่เป็น IPv4 sin_family = AF_INET , // ที่อยู่ IPv4 เป็น uint32_t แปลงสตริงที่แทนอ็อกเท็ตเป็นค่าที่เหมาะสมsin_addr . s_addr = inet_addr ( " 127.0.0.1" ), // ซ็อกเก็ตเป็น unsigned short, htons(x) ช่วยให้มั่นใจได้ว่า x อยู่ในลำดับไบต์เครือข่าย ตั้งค่าพอร์ตเป็น 7654 sin_port = htons ( 7654 ) } ; char buffer [ 200 ]; strcpy ( buffer , "Hello, world!" );// สร้างซ็อกเก็ตอินเทอร์เน็ตแบบดาตาแกรมโดยใช้ UDP int sock = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ); if ( sock == -1 ) { // หากซ็อกเก็ตเริ่มต้นไม่สำเร็จ ให้จบการทำงานfprintf ( stderr , "Failed to create socket! \n " ); return EXIT_FAILURE ; }// ส่งข้อความโดยใช้ sendto() int bytes_sent = sendto ( sock , buffer , strlen ( buffer ), 0 , ( struct sockaddr * ) & sa , sizeof ( sa ));ถ้า( bytes_sent < 0 ) { fprintf ( stderr , "เกิดข้อผิดพลาดในการส่งแพ็กเก็ต: %s \n " , strerror ( errno )); return EXIT_FAILURE ; } close ( sock ); // ปิดซ็อกเก็ตreturn 0 ; }ในโค้ดนี้bufferเป็นตัวชี้ไปยังข้อมูลที่จะส่ง และbuffer_lengthระบุขนาดของข้อมูล
ลิงก์ภายนอก
- เอกสารเสริมสำหรับโปรแกรมเมอร์ UNIX (PSD: 20-1)
- คู่มือการเขียนโปรแกรมเครือข่ายของบีเจ - 2007
- การแปลงโปรแกรม Berkeley Socket ไปยัง Winsock - เอกสารประกอบจาก Microsoft
- การเขียนโปรแกรมซ็อกเก็ต UNIX ด้วยภาษา C - คำถามที่พบบ่อย - ปี 1996
- การเขียนโปรแกรมเครือข่าย Linux - Linux Journal , 1998
สรุปเนื้อหา
ข้อมูลสำคัญจากบทความ
ข้อมูลสำคัญเกี่ยวกับ ซ็อกเก็ตเบิร์กลีย์
Berkeley socketsคืออินเทอร์เฟซการเขียนโปรแกรมแอปพลิเคชัน (API) สำหรับInternet domain socketsและUnix domain socketsซึ่งใช้สำหรับการสื่อสารระหว่างกระบวนการ (IPC)...
ประวัติและการนำไปใช้
ซ็อกเก็ตเบิร์กลีย์มีต้นกำเนิดมาจาก ระบบปฏิบัติการ BSD Unix 4.
ซ็อกเก็ต BSD และ POSIX
เมื่อ Berkeley socket API พัฒนาขึ้นและในที่สุดก็กลายเป็น POSIX socket API [ 2 ] ฟังก์ชันบางอย่างก็ถูกยกเลิกหรือลบออกและแทนที่ด้วยฟังก์ชันอื่น POSIX API ยังได้รับการออกแบบให้ สามารถเรียกซ้ำได้ และรองรับ IPv6
ทางเลือกอื่นๆ
API Transport Layer Interface (TLI) ที่ใช้ STREAMS เป็นทางเลือกแทน API ซ็อกเก็ต ระบบหลายระบบที่ให้บริการ API TLI ก็ให้บริการ API ซ็อกเก็ตของ Berkeley ด้วยเช่นกัน