Unix Socket - คู่มือฉบับย่อ
ซ็อกเก็ตอนุญาตให้สื่อสารระหว่างสองกระบวนการที่แตกต่างกันบนเครื่องเดียวกันหรือต่างกัน เพื่อให้แม่นยำยิ่งขึ้นเป็นวิธีการพูดคุยกับคอมพิวเตอร์เครื่องอื่นโดยใช้ตัวอธิบายไฟล์ Unix มาตรฐาน ใน Unix การดำเนินการ I / O ทุกอย่างทำได้โดยการเขียนหรืออ่าน file descriptor ตัวอธิบายไฟล์เป็นเพียงจำนวนเต็มที่เชื่อมโยงกับไฟล์ที่เปิดอยู่และอาจเป็นการเชื่อมต่อเครือข่ายไฟล์ข้อความเทอร์มินัลหรืออย่างอื่นก็ได้
สำหรับโปรแกรมเมอร์ซ็อกเก็ตจะมีลักษณะและทำงานเหมือนกับตัวอธิบายไฟล์ระดับต่ำ เนื่องจากคำสั่งเช่น read () และ write () ทำงานกับซ็อกเก็ตในลักษณะเดียวกับที่ทำกับไฟล์และไปป์
ซ็อกเก็ตเปิดตัวครั้งแรกใน 2.1BSD และต่อมาได้รับการปรับปรุงให้เป็นรูปแบบปัจจุบันด้วย 4.2BSD ขณะนี้คุณลักษณะซ็อกเก็ตสามารถใช้ได้กับระบบ UNIX ล่าสุด
Socket ใช้ที่ไหน?
Unix Socket ถูกใช้ในเฟรมเวิร์กแอ็พพลิเคชันไคลเอนต์เซิร์ฟเวอร์ เซิร์ฟเวอร์คือกระบวนการที่ทำหน้าที่บางอย่างตามคำขอจากไคลเอนต์ โปรโตคอลระดับแอปพลิเคชันส่วนใหญ่เช่น FTP, SMTP และ POP3 ใช้ซ็อกเก็ตเพื่อสร้างการเชื่อมต่อระหว่างไคลเอนต์และเซิร์ฟเวอร์จากนั้นสำหรับการแลกเปลี่ยนข้อมูล
ประเภทซ็อกเก็ต
ผู้ใช้มีซ็อกเก็ตสี่ประเภท สองตัวแรกมักใช้กันมากที่สุดและสองตัวสุดท้ายแทบไม่ได้ใช้
สันนิษฐานว่ากระบวนการสื่อสารระหว่างซ็อกเก็ตประเภทเดียวกันเท่านั้น แต่ไม่มีข้อ จำกัด ใดที่ขัดขวางการสื่อสารระหว่างซ็อกเก็ตประเภทต่างๆ
Stream Sockets- รับประกันการจัดส่งในสภาพแวดล้อมเครือข่าย หากคุณส่งผ่านซ็อกเก็ตสตรีมสามรายการ "A, B, C" รายการเหล่านี้จะมาถึงในลำดับเดียวกัน - "A, B, C" ซ็อกเก็ตเหล่านี้ใช้ TCP (Transmission Control Protocol) สำหรับการส่งข้อมูล หากไม่สามารถจัดส่งได้ผู้ส่งจะได้รับตัวบ่งชี้ข้อผิดพลาด บันทึกข้อมูลไม่มีขอบเขตใด ๆ
Datagram Sockets- ไม่รับประกันการจัดส่งในสภาพแวดล้อมเครือข่าย ไม่มีการเชื่อมต่อเนื่องจากคุณไม่จำเป็นต้องมีการเชื่อมต่อแบบเปิดเช่นเดียวกับใน Stream Sockets - คุณสร้างแพ็กเก็ตที่มีข้อมูลปลายทางและส่งออกไป พวกเขาใช้ UDP (User Datagram Protocol)
Raw Sockets- สิ่งเหล่านี้ช่วยให้ผู้ใช้สามารถเข้าถึงโปรโตคอลการสื่อสารพื้นฐานซึ่งรองรับซ็อกเก็ต abstractions โดยปกติแล้วซ็อกเก็ตเหล่านี้จะเน้นดาต้าแกรมแม้ว่าลักษณะที่แน่นอนจะขึ้นอยู่กับอินเทอร์เฟซที่จัดเตรียมโดยโปรโตคอล ซ็อกเก็ตดิบไม่ได้มีไว้สำหรับผู้ใช้ทั่วไป พวกเขาได้รับการจัดเตรียมไว้สำหรับผู้ที่สนใจในการพัฒนาโปรโตคอลการสื่อสารใหม่ ๆ เป็นหลักหรือเพื่อเข้าถึงสิ่งอำนวยความสะดวกที่เป็นความลับของโปรโตคอลที่มีอยู่
Sequenced Packet Sockets- คล้ายกับซ็อกเก็ตสตรีมยกเว้นขอบเขตของบันทึกจะถูกเก็บรักษาไว้ อินเทอร์เฟซนี้จัดเตรียมไว้เป็นส่วนหนึ่งของแอ็บสแตรกต์ซ็อกเก็ต Network Systems (NS) เท่านั้นและมีความสำคัญมากในแอปพลิเคชัน NS ที่ร้ายแรง ซ็อกเก็ตลำดับแพ็คเก็ตอนุญาตให้ผู้ใช้จัดการส่วนหัว Sequence Packet Protocol (SPP) หรือ Internet Datagram Protocol (IDP) บนแพ็กเก็ตหรือกลุ่มของแพ็กเก็ตไม่ว่าจะโดยการเขียนส่วนหัวต้นแบบพร้อมกับข้อมูลที่จะส่งหรือ การระบุส่วนหัวเริ่มต้นที่จะใช้กับข้อมูลขาออกทั้งหมดและอนุญาตให้ผู้ใช้รับส่วนหัวของแพ็กเก็ตขาเข้า
Next คืออะไร?
สองสามบทต่อไปจะหมายถึงการเสริมสร้างพื้นฐานของคุณและเตรียมรากฐานก่อนที่คุณจะสามารถเขียนเซิร์ฟเวอร์และไคลเอ็นต์โปรแกรมใช้ซ็อกเก็ต หากคุณต้องการข้ามไปดูวิธีการเขียนโปรแกรมไคลเอนต์และเซิร์ฟเวอร์โดยตรงคุณสามารถทำได้ แต่ไม่แนะนำ ขอแนะนำอย่างยิ่งให้คุณทำทีละขั้นตอนและทำตามบทเริ่มต้นสองสามบทเหล่านี้เพื่อสร้างพื้นฐานของคุณก่อนที่จะดำเนินการเขียนโปรแกรม
ก่อนที่เราจะดำเนินการกับเนื้อหาจริงขอให้เราพูดคุยเกี่ยวกับที่อยู่เครือข่าย - ที่อยู่ IP
ที่อยู่โฮสต์ IP หรือที่อยู่ IP โดยทั่วไปใช้เพื่อระบุโฮสต์ที่เชื่อมต่อกับอินเทอร์เน็ต IP ย่อมาจาก Internet Protocol และหมายถึง Internet Layer ของสถาปัตยกรรมเครือข่ายโดยรวมของอินเทอร์เน็ต
ที่อยู่ IP เป็นปริมาณ 32 บิตที่ตีความเป็นตัวเลข 8 บิตหรืออ็อกเต็ตสี่ตัว ที่อยู่ IP แต่ละแห่งจะระบุเครือข่ายผู้ใช้ที่เข้าร่วมโฮสต์บนเครือข่ายและคลาสของเครือข่ายผู้ใช้โดยไม่ซ้ำกัน
โดยปกติที่อยู่ IP จะเขียนด้วยเครื่องหมายจุด - ทศนิยมของรูปแบบ N1.N2.N3.N4 โดยแต่ละ Ni เป็นเลขฐานสิบระหว่าง 0 ถึง 255 ทศนิยม (00 ถึง FF ฐานสิบหก)
ที่อยู่คลาส
ที่อยู่ IP ได้รับการจัดการและสร้างโดยInternet Assigned Numbers Authority (IANA) มีห้าคลาสที่อยู่ที่แตกต่างกัน คุณสามารถระบุได้ว่าที่อยู่ IP อยู่ในคลาสใดโดยการตรวจสอบสี่บิตแรกของที่อยู่ IP
Class A ที่อยู่ขึ้นต้นด้วย 0xxx, หรือ 1 to 126 ทศนิยม.
Class B ที่อยู่ขึ้นต้นด้วย 10xx, หรือ 128 to 191 ทศนิยม.
Class C ที่อยู่ขึ้นต้นด้วย 110x, หรือ 192 to 223 ทศนิยม.
Class D ที่อยู่ขึ้นต้นด้วย 1110, หรือ 224 to 239 ทศนิยม.
Class E ที่อยู่ขึ้นต้นด้วย 1111, หรือ 240 to 254 ทศนิยม.
ที่อยู่ที่ขึ้นต้นด้วย 01111111, หรือ 127 ทศนิยมสงวนไว้สำหรับการวนกลับและสำหรับการทดสอบภายในบนเครื่องท้องถิ่น [คุณสามารถทดสอบสิ่งนี้: คุณควรจะสามารถ ping 127.0.0.1ซึ่งชี้ไปที่ตัวคุณเอง]; ที่อยู่คลาส D สงวนไว้สำหรับการทำมัลติคาสติ้ง ที่อยู่คลาส E สงวนไว้สำหรับการใช้งานในอนาคต ไม่ควรใช้สำหรับที่อยู่โฮสต์
ตัวอย่าง
Class | Leftmost bits | Start address | Finish address |
ก | 0xxx | 0.0.0.0 | 127.255.255.255 |
ข | 10xx | 128.0.0.0 | 191.255.255.255 |
ค | 110x | 192.0.0.0 | 223.255.255.255 |
ง | 1110 | 224.0.0.0 | 239.255.255.255 |
จ | 1111 | 240.0.0.0 | 255.255.255.255 |
ซับเน็ต
ซับเน็ตหรือเครือข่ายย่อยโดยทั่วไปหมายถึงการแตกแขนงออกจากเครือข่าย สามารถทำได้ด้วยเหตุผลหลายประการเช่นเครือข่ายในองค์กรการใช้สื่อทางกายภาพที่แตกต่างกัน (เช่นอีเธอร์เน็ต FDDI WAN เป็นต้น) การรักษาพื้นที่ที่อยู่และความปลอดภัย สาเหตุส่วนใหญ่คือการควบคุมการรับส่งข้อมูลเครือข่าย
แนวคิดพื้นฐานในการซับเน็ตคือการแบ่งส่วนตัวระบุโฮสต์ของที่อยู่ IP ออกเป็นสองส่วน -
- ที่อยู่เครือข่ายย่อยภายในที่อยู่เครือข่ายเอง และ
- ที่อยู่โฮสต์บนซับเน็ต
ตัวอย่างเช่นรูปแบบแอดเดรสคลาส B ทั่วไปคือ N1.N2.SH โดยที่ N1.N2 ระบุเครือข่ายคลาส B ฟิลด์ 8 บิต S ระบุซับเน็ตและฟิลด์ 8 บิต H ระบุโฮสต์บนซับเน็ต
ชื่อโฮสต์ในรูปของตัวเลขนั้นยากที่จะจดจำและด้วยเหตุนี้จึงเรียกด้วยชื่อสามัญเช่น Takshila หรือ Nalanda เราเขียนแอพพลิเคชั่นซอฟต์แวร์เพื่อค้นหาที่อยู่ IP แบบประตรงกับชื่อที่กำหนด
กระบวนการค้นหาที่อยู่ IP แบบประตามชื่อโฮสต์ที่เป็นตัวเลขและตัวอักษรที่กำหนดเรียกว่า hostname resolution.
การแก้ปัญหาชื่อโฮสต์ทำได้โดยซอฟต์แวร์พิเศษที่อยู่ในระบบความจุสูง ระบบเหล่านี้เรียกว่าระบบชื่อโดเมน (DNS) ซึ่งจะทำการแมปที่อยู่ IP และชื่อสามัญที่เกี่ยวข้อง
ไฟล์ / etc / hosts
จดหมายระหว่างชื่อโฮสต์และที่อยู่ IP จะถูกเก็บไว้ในไฟล์ที่เรียกว่าโฮสต์ ในระบบส่วนใหญ่ไฟล์นี้จะอยู่ใน/etc ไดเรกทอรี
รายการในไฟล์นี้มีลักษณะดังต่อไปนี้ -
# This represents a comments in /etc/hosts file.
127.0.0.1 localhost
192.217.44.207 nalanda metro
153.110.31.18 netserve
153.110.31.19 mainserver centeral
153.110.31.20 samsonite
64.202.167.10 ns3.secureserver.net
64.202.167.97 ns4.secureserver.net
66.249.89.104 www.google.com
68.178.157.132 services.amrood.com
โปรดทราบว่าอาจมีชื่อมากกว่าหนึ่งชื่อที่เชื่อมโยงกับที่อยู่ IP ที่ระบุ ไฟล์นี้ใช้ขณะแปลงจากที่อยู่ IP เป็นชื่อโฮสต์และในทางกลับกัน
คุณจะไม่มีสิทธิ์แก้ไขไฟล์นี้ดังนั้นหากคุณต้องการใส่ชื่อโฮสต์ใด ๆ พร้อมกับที่อยู่ IP คุณจะต้องได้รับอนุญาตจากรูท
Net Applications ส่วนใหญ่ใช้สถาปัตยกรรมไคลเอนต์เซิร์ฟเวอร์ซึ่งหมายถึงสองกระบวนการหรือสองแอปพลิเคชันที่สื่อสารกันเพื่อแลกเปลี่ยนข้อมูลบางอย่าง หนึ่งในสองกระบวนการทำหน้าที่เป็นกระบวนการไคลเอ็นต์และอีกกระบวนการหนึ่งทำหน้าที่เป็นเซิร์ฟเวอร์
กระบวนการลูกค้า
นี่คือกระบวนการซึ่งโดยทั่วไปจะทำการร้องขอข้อมูล หลังจากได้รับคำตอบแล้วกระบวนการนี้อาจยุติหรืออาจดำเนินการอื่น ๆ
Exampleอินเทอร์เน็ตเบราว์เซอร์ทำงานเป็นแอปพลิเคชันไคลเอนต์ซึ่งส่งคำขอไปยังเว็บเซิร์ฟเวอร์เพื่อรับเว็บเพจ HTML หนึ่งหน้า
กระบวนการเซิร์ฟเวอร์
นี่คือกระบวนการที่รับคำขอจากลูกค้า หลังจากได้รับคำขอจากไคลเอ็นต์แล้วกระบวนการนี้จะดำเนินการประมวลผลที่จำเป็นรวบรวมข้อมูลที่ร้องขอและส่งไปยังไคลเอ็นต์ผู้ร้องขอ เมื่อเสร็จแล้วก็พร้อมให้บริการลูกค้ารายอื่น กระบวนการของเซิร์ฟเวอร์จะแจ้งเตือนและพร้อมให้บริการตามคำขอที่เข้ามาเสมอ
Example - เว็บเซิร์ฟเวอร์รอคำขอจากเบราว์เซอร์อินเทอร์เน็ตและทันทีที่ได้รับคำขอใด ๆ จากเบราว์เซอร์ระบบจะดึงหน้า HTML ที่ร้องขอและส่งกลับไปยังเบราว์เซอร์นั้น
โปรดทราบว่าไคลเอนต์จำเป็นต้องทราบที่อยู่ของเซิร์ฟเวอร์ แต่เซิร์ฟเวอร์ไม่จำเป็นต้องทราบที่อยู่หรือแม้แต่การมีอยู่ของไคลเอ็นต์ก่อนที่จะมีการสร้างการเชื่อมต่อ เมื่อสร้างการเชื่อมต่อแล้วทั้งสองฝ่ายจะสามารถส่งและรับข้อมูลได้
สถาปัตยกรรม 2 ชั้นและ 3 ชั้น
สถาปัตยกรรมไคลเอนต์เซิร์ฟเวอร์มีสองประเภท -
2-tier architecture- ในสถาปัตยกรรมนี้ไคลเอนต์โต้ตอบกับเซิร์ฟเวอร์โดยตรง สถาปัตยกรรมประเภทนี้อาจมีช่องโหว่ด้านความปลอดภัยและปัญหาด้านประสิทธิภาพ Internet Explorer และ Web Server ทำงานบนสถาปัตยกรรมสองชั้น ปัญหาด้านความปลอดภัยได้รับการแก้ไขโดยใช้ Secure Socket Layer (SSL)
3-tier architectures- ในสถาปัตยกรรมนี้มีซอฟต์แวร์อีกหนึ่งตัวที่อยู่ระหว่างไคลเอนต์และเซิร์ฟเวอร์ ซอฟต์แวร์กลางนี้เรียกว่า 'มิดเดิลแวร์' มิดเดิลแวร์ใช้ในการตรวจสอบความปลอดภัยและการทำโหลดบาลานซ์ทั้งหมดในกรณีที่มีภาระงานหนัก มิดเดิลแวร์รับคำขอทั้งหมดจากไคลเอ็นต์และหลังจากดำเนินการตรวจสอบสิทธิ์ที่จำเป็นแล้วจะส่งคำขอนั้นไปยังเซิร์ฟเวอร์ จากนั้นเซิร์ฟเวอร์จะทำการประมวลผลที่จำเป็นและส่งการตอบกลับกลับไปที่มิดเดิลแวร์และในที่สุดมิดเดิลแวร์ก็ส่งการตอบกลับนี้กลับไปยังไคลเอนต์ หากคุณต้องการใช้สถาปัตยกรรม 3 ชั้นคุณสามารถเก็บมิดเดิลแวร์ใด ๆ เช่นซอฟต์แวร์ Web Logic หรือ WebSphere ระหว่างเว็บเซิร์ฟเวอร์และเว็บเบราว์เซอร์ของคุณ
ประเภทของเซิร์ฟเวอร์
มีเซิร์ฟเวอร์สองประเภทที่คุณสามารถมีได้ -
Iterative Server- นี่เป็นรูปแบบเซิร์ฟเวอร์ที่ง่ายที่สุดที่กระบวนการของเซิร์ฟเวอร์ให้บริการไคลเอ็นต์หนึ่งเครื่องและหลังจากดำเนินการตามคำขอแรกเสร็จแล้วจะได้รับคำขอจากไคลเอนต์อื่น ในขณะเดียวกันลูกค้ารายอื่นยังคงรอ
Concurrent Servers- เซิร์ฟเวอร์ประเภทนี้รันหลายกระบวนการพร้อมกันเพื่อตอบสนองคำขอจำนวนมากในคราวเดียวเนื่องจากกระบวนการหนึ่งอาจใช้เวลานานกว่าและไคลเอนต์อื่นไม่สามารถรอได้นาน วิธีที่ง่ายที่สุดในการเขียนเซิร์ฟเวอร์พร้อมกันภายใต้ Unix คือการแยกกระบวนการย่อยเพื่อจัดการกับไคลเอ็นต์แต่ละตัวแยกกัน
วิธีสร้างลูกค้า
ระบบเรียกร้องให้สร้างการเชื่อมต่อแตกต่างกันบ้างสำหรับไคลเอนต์และเซิร์ฟเวอร์ แต่ทั้งสองอย่างเกี่ยวข้องกับโครงสร้างพื้นฐานของซ็อกเก็ต ทั้งสองกระบวนการสร้างซ็อกเก็ตของตนเอง
ขั้นตอนที่เกี่ยวข้องในการสร้างซ็อกเก็ตบนฝั่งไคลเอ็นต์มีดังต่อไปนี้ -
สร้างซ็อกเก็ตด้วยไฟล์ socket() ระบบโทร.
เชื่อมต่อซ็อกเก็ตกับที่อยู่ของเซิร์ฟเวอร์โดยใช้ไฟล์ connect() ระบบโทร.
ส่งและรับข้อมูล มีหลายวิธีในการดำเนินการนี้ แต่วิธีที่ง่ายที่สุดคือใช้ไฟล์read() และ write() การโทรของระบบ
วิธีการสร้างเซิร์ฟเวอร์
ขั้นตอนที่เกี่ยวข้องในการสร้างซ็อกเก็ตบนฝั่งเซิร์ฟเวอร์มีดังต่อไปนี้ -
สร้างซ็อกเก็ตด้วยไฟล์ socket() ระบบโทร.
ผูกซ็อกเก็ตกับที่อยู่โดยใช้ไฟล์ bind()ระบบโทร. สำหรับซ็อกเก็ตเซิร์ฟเวอร์บนอินเทอร์เน็ตแอดเดรสประกอบด้วยหมายเลขพอร์ตบนเครื่องโฮสต์
ฟังเพื่อเชื่อมต่อกับไฟล์ listen() ระบบโทร.
ยอมรับการเชื่อมต่อกับไฟล์ accept()ระบบโทร. โดยทั่วไปสายนี้จะบล็อกการเชื่อมต่อจนกว่าไคลเอ็นต์จะเชื่อมต่อกับเซิร์ฟเวอร์
ส่งและรับข้อมูลโดยใช้ไฟล์ read() และ write() การโทรของระบบ
การโต้ตอบกับลูกค้าและเซิร์ฟเวอร์
ต่อไปนี้เป็นแผนภาพแสดงการโต้ตอบกับไคลเอนต์และเซิร์ฟเวอร์ที่สมบูรณ์ -
โครงสร้างต่างๆถูกใช้ใน Unix Socket Programming เพื่อเก็บข้อมูลเกี่ยวกับที่อยู่และพอร์ตและข้อมูลอื่น ๆ ฟังก์ชันซ็อกเก็ตส่วนใหญ่ต้องการตัวชี้ไปยังโครงสร้างที่อยู่ของซ็อกเก็ตเป็นอาร์กิวเมนต์ โครงสร้างที่กำหนดในบทนี้เกี่ยวข้องกับ Internet Protocol Family
ถุงเท้า
โครงสร้างแรกคือsockaddrที่เก็บข้อมูลซ็อกเก็ต -
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
นี่คือโครงสร้างที่อยู่ซ็อกเก็ตทั่วไปซึ่งจะถูกส่งผ่านในการเรียกฟังก์ชันซ็อกเก็ตส่วนใหญ่ ตารางต่อไปนี้ให้คำอธิบายของฟิลด์สมาชิก -
แอตทริบิวต์ | ค่า | คำอธิบาย |
---|---|---|
sa_family | AF_INET AF_UNIX AF_NS AF_IMPLINK |
แสดงถึงครอบครัวที่อยู่ ในแอปพลิเคชันบนอินเทอร์เน็ตส่วนใหญ่เราใช้ AF_INET |
sa_data | ที่อยู่เฉพาะโปรโตคอล | เนื้อหาของที่อยู่เฉพาะโปรโตคอล 14 ไบต์ถูกตีความตามประเภทของที่อยู่ สำหรับตระกูลอินเทอร์เน็ตเราจะใช้หมายเลขพอร์ตที่อยู่ IP ซึ่งแสดงโดยโครงสร้างsockaddr_in ที่กำหนดไว้ด้านล่าง |
sockaddr ใน
โครงสร้างที่สองที่ช่วยให้คุณอ้างอิงถึงองค์ประกอบของซ็อกเก็ตมีดังนี้ -
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
นี่คือคำอธิบายของช่องสมาชิก -
แอตทริบิวต์ | ค่า | คำอธิบาย |
---|---|---|
sa_family | AF_INET AF_UNIX AF_NS AF_IMPLINK |
แสดงถึงครอบครัวที่อยู่ ในแอปพลิเคชันบนอินเทอร์เน็ตส่วนใหญ่เราใช้ AF_INET |
sin_port | พอร์ตบริการ | หมายเลขพอร์ต 16 บิตในลำดับไบต์เครือข่าย |
sin_addr | ที่อยู่ IP | ที่อยู่ IP 32 บิตในลำดับไบต์เครือข่าย |
sin_zero | ไม่ได้ใช้ | คุณเพียงแค่ตั้งค่านี้เป็น NULL เนื่องจากไม่ได้ใช้ |
ใน addr
โครงสร้างนี้ใช้เฉพาะในโครงสร้างด้านบนเป็นฟิลด์โครงสร้างและเก็บ 32 บิต netid / hostid
struct in_addr {
unsigned long s_addr;
};
นี่คือคำอธิบายของช่องสมาชิก -
แอตทริบิวต์ | ค่า | คำอธิบาย |
---|---|---|
s_addr | พอร์ตบริการ | ที่อยู่ IP 32 บิตในลำดับไบต์เครือข่าย |
โฮสต์
โครงสร้างนี้ใช้เพื่อเก็บข้อมูลที่เกี่ยวข้องกับโฮสต์
struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list
#define h_addr h_addr_list[0]
};
นี่คือคำอธิบายของช่องสมาชิก -
แอตทริบิวต์ | ค่า | คำอธิบาย |
---|---|---|
h_name | ti.com เป็นต้น | เป็นชื่อทางการของโฮสต์ ตัวอย่างเช่น tutorialspoint.com, google.com เป็นต้น |
h_aliases | Ti | มีรายการนามแฝงชื่อโฮสต์ |
h_addrtype | AF_INET | ประกอบด้วยตระกูลที่อยู่และในกรณีของแอปพลิเคชันบนอินเทอร์เน็ตจะเป็น AF_INET เสมอ |
h_length | 4 | ถือความยาวของที่อยู่ IP ซึ่งเป็น 4 สำหรับที่อยู่อินเทอร์เน็ต |
h_addr_list | in_addr | สำหรับที่อยู่อินเทอร์เน็ตอาร์เรย์ของพอยน์เตอร์ h_addr_list [0], h_addr_list [1] และอื่น ๆ เป็นจุดที่จัดโครงสร้าง in_addr |
NOTE - h_addr ถูกกำหนดให้เป็น h_addr_list [0] เพื่อรักษาความเข้ากันได้แบบย้อนหลัง
เซอเวนต์
โครงสร้างเฉพาะนี้ใช้เพื่อเก็บข้อมูลที่เกี่ยวข้องกับบริการและพอร์ตที่เกี่ยวข้อง
struct servent {
char *s_name;
char **s_aliases;
int s_port;
char *s_proto;
};
นี่คือคำอธิบายของช่องสมาชิก -
แอตทริบิวต์ | ค่า | คำอธิบาย |
---|---|---|
s_name | http | นี่คือชื่อทางการของบริการ ตัวอย่างเช่น SMTP, FTP POP3 เป็นต้น |
s_aliases | ALIAS | มันมีรายการนามแฝงบริการ เวลาส่วนใหญ่จะถูกตั้งค่าเป็น NULL |
กีฬา | 80 | จะมีหมายเลขพอร์ตที่เกี่ยวข้อง ตัวอย่างเช่นสำหรับ HTTP จะเท่ากับ 80 |
s_proto | TCP UDP |
ตั้งค่าเป็นโปรโตคอลที่ใช้ บริการอินเทอร์เน็ตมีให้โดยใช้ TCP หรือ UDP |
เคล็ดลับเกี่ยวกับโครงสร้างซ็อกเก็ต
โครงสร้างที่อยู่ซ็อกเก็ตเป็นส่วนสำคัญของทุกโปรแกรมเครือข่าย เราจัดสรรพวกเขาเติมเต็มและส่งผ่านตัวชี้ไปยังฟังก์ชันซ็อกเก็ตต่างๆ บางครั้งเราส่งตัวชี้ไปยังโครงสร้างเหล่านี้ไปยังฟังก์ชันซ็อกเก็ตและเติมลงในเนื้อหา
เราส่งผ่านโครงสร้างเหล่านี้โดยการอ้างอิงเสมอ (กล่าวคือเราส่งตัวชี้ไปที่โครงสร้างไม่ใช่โครงสร้างเอง) และเราส่งผ่านขนาดของโครงสร้างเป็นอาร์กิวเมนต์อื่นเสมอ
เมื่อฟังก์ชันซ็อกเก็ตเติมในโครงสร้างความยาวจะถูกส่งผ่านโดยการอ้างอิงด้วยเพื่อให้ฟังก์ชันสามารถอัปเดตค่าได้ เราเรียกสิ่งเหล่านี้ว่าอาร์กิวเมนต์มูลค่า - ผลลัพธ์
เสมอตั้งค่าตัวแปรโครงสร้างเป็น NULL (เช่น '\ 0') โดยใช้ memset () สำหรับฟังก์ชัน bzero () มิฉะนั้นอาจได้รับค่าขยะที่ไม่คาดคิดในโครงสร้างของคุณ
เมื่อกระบวนการไคลเอ็นต์ต้องการเชื่อมต่อเซิร์ฟเวอร์ไคลเอ็นต์ต้องมีวิธีการระบุเซิร์ฟเวอร์ที่ต้องการเชื่อมต่อ หากไคลเอนต์ทราบที่อยู่อินเทอร์เน็ต 32 บิตของโฮสต์ที่เซิร์ฟเวอร์นั้นอาศัยอยู่ก็สามารถติดต่อโฮสต์นั้นได้ แต่ไคลเอนต์ระบุกระบวนการเซิร์ฟเวอร์เฉพาะที่รันบนโฮสต์นั้นได้อย่างไร
เพื่อแก้ไขปัญหาในการระบุกระบวนการเซิร์ฟเวอร์เฉพาะที่รันบนโฮสต์ทั้ง TCP และ UDP ได้กำหนดกลุ่มของพอร์ตที่รู้จักกันดี
สำหรับจุดประสงค์ของเราพอร์ตจะถูกกำหนดเป็นหมายเลขจำนวนเต็มระหว่าง 1024 ถึง 65535 เนื่องจากหมายเลขพอร์ตทั้งหมดที่มีขนาดเล็กกว่า 1024 ถือว่าเป็นที่รู้จักกันดีตัวอย่างเช่น telnet ใช้พอร์ต 23, http ใช้ 80, ftp ใช้ 21, และอื่น ๆ
การกำหนดพอร์ตให้กับบริการเครือข่ายสามารถพบได้ในไฟล์ / etc / services หากคุณกำลังเขียนเซิร์ฟเวอร์ของคุณเองคุณต้องระมัดระวังในการกำหนดพอร์ตให้กับเซิร์ฟเวอร์ของคุณ คุณควรตรวจสอบให้แน่ใจว่าไม่ควรกำหนดพอร์ตนี้ให้กับเซิร์ฟเวอร์อื่นใด
โดยปกติการกำหนดหมายเลขพอร์ตใด ๆ มากกว่า 5,000 เป็นวิธีปฏิบัติ แต่มีหลายองค์กรที่เขียนเซิร์ฟเวอร์ที่มีหมายเลขพอร์ตมากกว่า 5,000 ตัวอย่างเช่น Yahoo Messenger ทำงานบน 5050, SIP Server ทำงานบน 5060 เป็นต้น
ตัวอย่างพอร์ตและบริการ
นี่คือรายการบริการเล็ก ๆ และพอร์ตที่เกี่ยวข้อง คุณสามารถค้นหารายการปรับปรุงมากที่สุดของพอร์ตอินเทอร์เน็ตและบริการที่เกี่ยวข้องที่IANA - พอร์ต TCP / IP ที่ได้รับมอบหมาย
Service | Port Number | Service Description |
ก้อง | 7 | UDP / TCP ส่งกลับสิ่งที่ได้รับ |
ทิ้ง | 9 | UDP / TCP พ่นอินพุตออกไป |
กลางวัน | 13 | UDP / TCP ส่งคืนเวลา ASCII |
ถ่าน | 19 | UDP / TCP ส่งคืนอักขระ |
ftp | 21 | การถ่ายโอนไฟล์ TCP |
เทลเน็ต | 23 | ล็อกอินระยะไกล TCP |
smtp | 25 | อีเมล TCP |
กลางวัน | 37 | UDP / TCP ส่งคืนเวลาไบนารี |
tftp | 69 | การถ่ายโอนไฟล์ UDP เล็กน้อย |
นิ้ว | 79 | ข้อมูล TCP เกี่ยวกับผู้ใช้ |
http | 80 | TCP เวิลด์ไวด์เว็บ |
เข้าสู่ระบบ | 513 | ล็อกอินระยะไกล TCP |
Who | 513 | UDP ข้อมูลที่แตกต่างกันเกี่ยวกับผู้ใช้ |
Xserver | 6000 | หน้าต่าง TCP X (NB> 1023) |
ฟังก์ชั่นพอร์ตและบริการ
Unix จัดเตรียมฟังก์ชันต่อไปนี้เพื่อดึงชื่อบริการจากไฟล์ / etc / services
struct servent *getservbyname(char *name, char *proto) - การโทรนี้ใช้ชื่อบริการและชื่อโปรโตคอลและส่งกลับหมายเลขพอร์ตที่เกี่ยวข้องสำหรับบริการนั้น
struct servent *getservbyport(int port, char *proto) - การโทรนี้ใช้หมายเลขพอร์ตและชื่อโปรโตคอลและส่งกลับชื่อบริการที่เกี่ยวข้อง
ค่าที่ส่งคืนสำหรับแต่ละฟังก์ชันเป็นตัวชี้ไปยังโครงสร้างที่มีรูปแบบต่อไปนี้ -
struct servent {
char *s_name;
char **s_aliases;
int s_port;
char *s_proto;
};
นี่คือคำอธิบายของช่องสมาชิก -
แอตทริบิวต์ | ค่า | คำอธิบาย |
---|---|---|
s_name | http | เป็นชื่อทางการของบริการ ตัวอย่างเช่น SMTP, FTP POP3 เป็นต้น |
s_aliases | ALIAS | มันมีรายการนามแฝงบริการ ส่วนใหญ่จะตั้งค่าเป็น NULL |
กีฬา | 80 | มันจะมีหมายเลขพอร์ตที่เกี่ยวข้อง ตัวอย่างเช่นสำหรับ HTTP จะเป็น 80 |
s_proto | TCP UDP |
ตั้งค่าเป็นโปรโตคอลที่ใช้ บริการอินเทอร์เน็ตมีให้โดยใช้ TCP หรือ UDP |
ขออภัยไม่ใช่ว่าคอมพิวเตอร์ทุกเครื่องจะจัดเก็บไบต์ที่ประกอบด้วยค่าหลายไบต์ในลำดับเดียวกัน พิจารณาอินเทอร์เน็ต 16 บิตที่ประกอบด้วย 2 ไบต์ มีสองวิธีในการจัดเก็บค่านี้
Little Endian - ในรูปแบบนี้ไบต์ลำดับต่ำจะถูกเก็บไว้ในที่อยู่เริ่มต้น (A) และไบต์ลำดับสูงจะถูกเก็บไว้ในที่อยู่ถัดไป (A + 1)
Big Endian - ในรูปแบบนี้ไบต์ลำดับสูงจะถูกเก็บไว้ในที่อยู่เริ่มต้น (A) และไบต์ลำดับต่ำจะถูกเก็บไว้ในที่อยู่ถัดไป (A + 1)
เพื่อให้เครื่องที่มีระเบียบการสั่งซื้อไบต์ที่แตกต่างกันสามารถสื่อสารกันได้อินเทอร์เน็ตโปรโตคอลจะระบุรูปแบบคำสั่งไบต์มาตรฐานสำหรับข้อมูลที่ส่งผ่านเครือข่าย สิ่งนี้เรียกว่า Network Byte Order
ในขณะที่สร้างการเชื่อมต่อซ็อกเก็ตอินเทอร์เน็ตคุณต้องตรวจสอบให้แน่ใจว่าข้อมูลในสมาชิก sin_port และ sin_addr ของโครงสร้าง sockaddr_in แสดงในลำดับไบต์ของเครือข่าย
ฟังก์ชันการสั่งซื้อไบต์
กิจวัตรในการแปลงข้อมูลระหว่างการเป็นตัวแทนภายในของโฮสต์และลำดับไบต์ของเครือข่ายมีดังนี้ -
ฟังก์ชัน | คำอธิบาย |
---|---|
htons () | โฮสต์ไปยังเครือข่ายสั้น |
htonl () | โฮสต์ไปยังเครือข่ายแบบยาว |
ntohl () | เครือข่ายโฮสต์ยาว |
ntohs () | เครือข่ายไปยังโฮสต์สั้น |
ด้านล่างนี้เป็นรายละเอียดเพิ่มเติมเกี่ยวกับฟังก์ชันเหล่านี้ -
unsigned short htons(unsigned short hostshort) - ฟังก์ชันนี้จะแปลงปริมาณ 16 บิต (2 ไบต์) จากคำสั่งไบต์ของโฮสต์เป็นลำดับไบต์ของเครือข่าย
unsigned long htonl(unsigned long hostlong) - ฟังก์ชันนี้จะแปลงปริมาณ 32 บิต (4 ไบต์) จากคำสั่งไบต์ของโฮสต์เป็นคำสั่งไบต์ของเครือข่าย
unsigned short ntohs(unsigned short netshort) - ฟังก์ชันนี้จะแปลงปริมาณ 16 บิต (2 ไบต์) จากคำสั่งไบต์เครือข่ายเป็นคำสั่งไบต์ของโฮสต์
unsigned long ntohl(unsigned long netlong) - ฟังก์ชันนี้จะแปลงปริมาณ 32 บิตจากคำสั่งไบต์เครือข่ายเป็นคำสั่งไบต์ของโฮสต์
ฟังก์ชันเหล่านี้เป็นมาโครและส่งผลให้มีการแทรกซอร์สโค้ดการแปลงลงในโปรแกรมการโทร ในเครื่องเล็ก ๆ น้อย ๆ รหัสจะเปลี่ยนค่าตามลำดับไบต์เครือข่าย ในเครื่อง big-endian ไม่มีการใส่รหัสเนื่องจากไม่จำเป็นต้องใช้ ฟังก์ชันถูกกำหนดให้เป็นโมฆะ
โปรแกรมเพื่อกำหนดลำดับไบต์ของโฮสต์
เก็บรหัสต่อไปนี้ไว้ในไฟล์byteorder.cจากนั้นคอมไพล์แล้วรันบนเครื่องของคุณ
ในตัวอย่างนี้เราเก็บค่า 0x0102 สองไบต์ไว้ในจำนวนเต็มสั้น ๆ จากนั้นดูที่สองไบต์ที่ต่อเนื่องกัน c [0] (ที่อยู่ A) และ c [1] (ที่อยู่ A + 1) เพื่อกำหนดไบต์ ใบสั่ง.
#include <stdio.h>
int main(int argc, char **argv) {
union {
short s;
char c[sizeof(short)];
}un;
un.s = 0x0102;
if (sizeof(short) == 2) {
if (un.c[0] == 1 && un.c[1] == 2)
printf("big-endian\n");
else if (un.c[0] == 2 && un.c[1] == 1)
printf("little-endian\n");
else
printf("unknown\n");
}
else {
printf("sizeof(short) = %d\n", sizeof(short));
}
exit(0);
}
ผลลัพธ์ที่สร้างโดยโปรแกรมนี้บนเครื่อง Pentium มีดังนี้ -
$> gcc byteorder.c $> ./a.out
little-endian
$>
Unix มีการเรียกใช้ฟังก์ชันต่างๆเพื่อช่วยคุณจัดการกับที่อยู่ IP ฟังก์ชันเหล่านี้จะแปลงที่อยู่อินเทอร์เน็ตระหว่างสตริง ASCII (สิ่งที่มนุษย์ชอบใช้) และค่าไบต์ของเครือข่ายที่เรียงลำดับค่าไบนารี (ค่าที่เก็บไว้ในโครงสร้างที่อยู่ของซ็อกเก็ต)
การเรียกใช้ฟังก์ชันสามรายการต่อไปนี้ใช้สำหรับการกำหนดแอดเดรส IPv4 -
- int inet_aton (const ถ่าน * strptr, struct in_addr * addrptr)
- in_addr_t inet_addr (const ถ่าน * strptr)
- ถ่าน * inet_ntoa (โครงสร้าง in_addr inaddr)
int inet_aton (const ถ่าน * strptr, struct in_addr * addrptr)
การเรียกใช้ฟังก์ชันนี้จะแปลงสตริงที่ระบุในสัญกรณ์จุดมาตรฐานอินเทอร์เน็ตเป็นที่อยู่เครือข่ายและจัดเก็บที่อยู่ในโครงสร้างที่มีให้ ที่อยู่ที่แปลงแล้วจะอยู่ใน Network Byte Order (ไบต์เรียงลำดับจากซ้ายไปขวา) ส่งคืน 1 หากสตริงถูกต้องและ 0 เมื่อเกิดข้อผิดพลาด
ต่อไปนี้เป็นตัวอย่างการใช้งาน -
#include <arpa/inet.h>
(...)
int retval;
struct in_addr addrptr
memset(&addrptr, '\0', sizeof(addrptr));
retval = inet_aton("68.178.157.132", &addrptr);
(...)
in_addr_t inet_addr (const ถ่าน * strptr)
การเรียกใช้ฟังก์ชันนี้จะแปลงสตริงที่ระบุในสัญกรณ์จุดมาตรฐานอินเทอร์เน็ตเป็นค่าจำนวนเต็มที่เหมาะสำหรับใช้เป็นที่อยู่อินเทอร์เน็ต ที่อยู่ที่แปลงแล้วจะอยู่ใน Network Byte Order (ไบต์เรียงลำดับจากซ้ายไปขวา) ส่งคืนไบต์เครือข่ายไบนารี 32 บิตที่สั่งซื้อที่อยู่ IPv4 และ INADDR_NONE เมื่อเกิดข้อผิดพลาด
ต่อไปนี้เป็นตัวอย่างการใช้งาน -
#include <arpa/inet.h>
(...)
struct sockaddr_in dest;
memset(&dest, '\0', sizeof(dest));
dest.sin_addr.s_addr = inet_addr("68.178.157.132");
(...)
ถ่าน * inet_ntoa (โครงสร้าง in_addr inaddr)
การเรียกใช้ฟังก์ชันนี้จะแปลงที่อยู่โฮสต์อินเทอร์เน็ตที่ระบุเป็นสตริงในสัญกรณ์จุดมาตรฐานอินเทอร์เน็ต
ต่อไปนี้เป็นตัวอย่างการใช้งาน -
#include <arpa/inet.h>
(...)
char *ip;
ip = inet_ntoa(dest.sin_addr);
printf("IP Address is: %s\n",ip);
(...)
บทนี้อธิบายถึงฟังก์ชันซ็อกเก็ตหลักที่จำเป็นในการเขียนไคลเอ็นต์และเซิร์ฟเวอร์ TCP ที่สมบูรณ์
แผนภาพต่อไปนี้แสดงการโต้ตอบกับไคลเอนต์และเซิร์ฟเวอร์ที่สมบูรณ์ -
ฟังก์ชั่นซ็อกเก็ต
ในการดำเนินการ I / O เครือข่ายสิ่งแรกที่กระบวนการต้องทำคือเรียกใช้ฟังก์ชันซ็อกเก็ตระบุประเภทของโปรโตคอลการสื่อสารที่ต้องการและตระกูลโปรโตคอลเป็นต้น
#include <sys/types.h>
#include <sys/socket.h>
int socket (int family, int type, int protocol);
การโทรนี้ส่งคืนตัวอธิบายซ็อกเก็ตที่คุณสามารถใช้ในการเรียกระบบในภายหลังหรือ -1 เมื่อเกิดข้อผิดพลาด
พารามิเตอร์
family - ระบุตระกูลโปรโตคอลและเป็นหนึ่งในค่าคงที่ที่แสดงด้านล่าง -
ครอบครัว | คำอธิบาย |
---|---|
AF_INET | โปรโตคอล IPv4 |
AF_INET6 | โปรโตคอล IPv6 |
AF_LOCAL | โปรโตคอลโดเมน Unix |
AF_ROUTE | การกำหนดเส้นทางซ็อกเก็ต |
AF_KEY | เต้ารับ |
บทนี้ไม่ครอบคลุมโปรโตคอลอื่น ๆ ยกเว้น IPv4
type- ระบุประเภทของซ็อกเก็ตที่คุณต้องการ อาจใช้ค่าใดค่าหนึ่งต่อไปนี้ -
ประเภท | คำอธิบาย |
---|---|
SOCK_STREAM | ซ็อกเก็ตสตรีม |
SOCK_DGRAM | ซ็อกเก็ต Datagram |
SOCK_SEQPACKET | ซ็อกเก็ตแพ็คเก็ตตามลำดับ |
SOCK_RAW | ซ็อกเก็ตดิบ |
protocol - ควรตั้งค่าอาร์กิวเมนต์เป็นประเภทโปรโตคอลเฉพาะที่ระบุด้านล่างหรือ 0 เพื่อเลือกค่าเริ่มต้นของระบบสำหรับการรวมตระกูลและประเภทที่กำหนด -
มาตรการ | คำอธิบาย |
---|---|
IPPROTO_TCP | โปรโตคอลการขนส่ง TCP |
IPPROTO_UDP | โปรโตคอลการขนส่ง UDP |
IPPROTO_SCTP | โปรโตคอลการขนส่ง SCTP |
เชื่อมต่อฟังก์ชั่น
เชื่อมต่อฟังก์ชั่นการใช้งานโดยลูกค้า TCP ที่จะสร้างการเชื่อมต่อกับเซิร์ฟเวอร์ TCP
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
การเรียกนี้จะคืนค่า 0 หากเชื่อมต่อกับเซิร์ฟเวอร์สำเร็จมิฉะนั้นจะส่งกลับ -1 เมื่อเกิดข้อผิดพลาด
พารามิเตอร์
sockfd - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
serv_addr - เป็นตัวชี้ไปยัง struct sockaddr ที่มีที่อยู่ IP ปลายทางและพอร์ต
addrlen - ตั้งค่าเป็น sizeof (struct sockaddr)
ผูกฟังก์ชั่น
ผูกฟังก์ชั่นกำหนดอยู่โปรโตคอลท้องถิ่นเพื่อซ็อกเก็ต ด้วยอินเทอร์เน็ตโปรโตคอลที่อยู่โปรโตคอลคือการรวมกันของที่อยู่ IPv4 32 บิตหรือที่อยู่ IPv6 แบบ 128 บิตพร้อมกับหมายเลขพอร์ต TCP หรือ UDP 16 บิต ฟังก์ชันนี้ถูกเรียกโดยเซิร์ฟเวอร์ TCP เท่านั้น
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr,int addrlen);
การเรียกนี้จะคืนค่า 0 หากผูกกับแอดเดรสสำเร็จมิฉะนั้นจะส่งกลับ -1 เมื่อเกิดข้อผิดพลาด
พารามิเตอร์
sockfd - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
my_addr - เป็นตัวชี้ไปยัง struct sockaddr ที่มีที่อยู่ IP และพอร์ตในเครื่อง
addrlen - ตั้งค่าเป็น sizeof (struct sockaddr)
คุณสามารถใส่ที่อยู่ IP และพอร์ตของคุณโดยอัตโนมัติ
ค่า 0 สำหรับหมายเลขพอร์ตหมายความว่าระบบจะเลือกพอร์ตแบบสุ่มและค่าINADDR_ANYสำหรับที่อยู่ IP หมายความว่าที่อยู่ IP ของเซิร์ฟเวอร์จะถูกกำหนดโดยอัตโนมัติ
server.sin_port = 0;
server.sin_addr.s_addr = INADDR_ANY;
NOTE- สงวนพอร์ตทั้งหมดที่ต่ำกว่า 1024 คุณสามารถตั้งค่าพอร์ตที่สูงกว่า 1024 และต่ำกว่า 65535 ได้เว้นแต่จะเป็นพอร์ตที่ใช้งานโดยโปรแกรมอื่น
ฟังก์ชั่น
ฟังก์ชั่นที่เรียกว่าโดยเซิร์ฟเวอร์ TCP เท่านั้นและจะดำเนินการสองการกระทำ -
ฟังก์ชั่นฟังจะแปลงซ็อกเก็ตที่ไม่ได้เชื่อมต่อเป็นซ็อกเก็ตแบบพาสซีฟซึ่งบ่งชี้ว่าเคอร์เนลควรยอมรับการร้องขอการเชื่อมต่อขาเข้าที่ส่งไปยังซ็อกเก็ตนี้
อาร์กิวเมนต์ที่สองของฟังก์ชันนี้ระบุจำนวนการเชื่อมต่อสูงสุดที่เคอร์เนลควรจัดคิวสำหรับซ็อกเก็ตนี้
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd,int backlog);
การโทรนี้ส่งคืน 0 เมื่อสำเร็จมิฉะนั้นจะส่งกลับ -1 เมื่อมีข้อผิดพลาด
พารามิเตอร์
sockfd - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
backlog - เป็นจำนวนการเชื่อมต่อที่อนุญาต
ยอมรับฟังก์ชั่น
ยอมรับฟังก์ชั่นที่เรียกว่าโดยเซิร์ฟเวอร์ TCP ที่จะกลับการเชื่อมต่อเสร็จสมบูรณ์ต่อไปจากด้านหน้าของคิวการเชื่อมต่อเสร็จสมบูรณ์ที่ ลายเซ็นของการโทรมีดังนี้ -
#include <sys/types.h>
#include <sys/socket.h>
int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
การเรียกนี้ส่งคืนตัวบอกความสำเร็จที่ไม่ใช่เชิงลบมิฉะนั้นจะส่งกลับค่า -1 เมื่อมีข้อผิดพลาด ตัวบอกที่ส่งคืนจะถือว่าเป็นตัวบอกเกี่ยวกับซ็อกเก็ตไคลเอ็นต์และการดำเนินการอ่านเขียนทั้งหมดจะทำบนตัวอธิบายนี้เพื่อสื่อสารกับไคลเอนต์
พารามิเตอร์
sockfd - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
cliaddr - เป็นตัวชี้ไปยัง struct sockaddr ที่มีที่อยู่ IP และพอร์ตของไคลเอ็นต์
addrlen - ตั้งค่าเป็น sizeof (struct sockaddr)
ส่งฟังก์ชั่น
ส่งฟังก์ชั่นที่ใช้ในการส่งข้อมูลผ่านซ็อกเก็ตสตรีมหรือซ็อกเก็ตเดตาแกรมที่เกี่ยวโยงกัน หากคุณต้องการส่งข้อมูลผ่านซ็อกเก็ตดาตาแกรม UNCONNECTED คุณต้องใช้ฟังก์ชัน sendto ()
คุณสามารถใช้การเรียกระบบwrite ()เพื่อส่งข้อมูล ลายเซ็นมีดังนี้ -
int send(int sockfd, const void *msg, int len, int flags);
การโทรนี้ส่งคืนจำนวนไบต์ที่ส่งออกไปมิฉะนั้นจะส่งกลับ -1 เมื่อเกิดข้อผิดพลาด
พารามิเตอร์
sockfd - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
msg - เป็นตัวชี้ไปยังข้อมูลที่คุณต้องการส่ง
len - เป็นความยาวของข้อมูลที่คุณต้องการส่ง (เป็นไบต์)
flags - ตั้งค่าเป็น 0
recvฟังก์ชั่น
recvฟังก์ชั่นที่ใช้ในการรับข้อมูลผ่านซ็อกเก็ตสตรีมหรือซ็อกเก็ตเดตาแกรมที่เกี่ยวโยงกัน หากคุณต้องการรับข้อมูลผ่านซ็อกเก็ตดาตาแกรมที่ไม่ได้เชื่อมต่อคุณต้องใช้ recvfrom ()
คุณสามารถใช้การเรียกระบบread ()เพื่ออ่านข้อมูล คำเรียกนี้อธิบายไว้ในบทฟังก์ชันตัวช่วย
int recv(int sockfd, void *buf, int len, unsigned int flags);
การเรียกนี้ส่งคืนจำนวนไบต์ที่อ่านลงในบัฟเฟอร์มิฉะนั้นจะส่งกลับ -1 เมื่อเกิดข้อผิดพลาด
พารามิเตอร์
sockfd - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
buf - เป็นบัฟเฟอร์ในการอ่านข้อมูล
len - เป็นความยาวสูงสุดของบัฟเฟอร์
flags - ตั้งค่าเป็น 0
sendtoฟังก์ชั่น
sendtoฟังก์ชั่นที่ใช้ในการส่งข้อมูลผ่านซ็อกเก็ตเดตาแกรมไม่เกี่ยวเนื่องกัน ลายเซ็นมีดังนี้ -
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
การโทรนี้ส่งคืนจำนวนไบต์ที่ส่งมิฉะนั้นจะส่งกลับ -1 เมื่อมีข้อผิดพลาด
พารามิเตอร์
sockfd - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
msg - เป็นตัวชี้ไปยังข้อมูลที่คุณต้องการส่ง
len - เป็นความยาวของข้อมูลที่คุณต้องการส่ง (เป็นไบต์)
flags - ตั้งค่าเป็น 0
to - เป็นตัวชี้ไปยัง struct sockaddr สำหรับโฮสต์ที่จะต้องส่งข้อมูล
tolen - ตั้งค่าเป็น sizeof (struct sockaddr)
recvfromฟังก์ชั่น
recvfromฟังก์ชั่นที่ใช้ในการรับข้อมูลจากดาต้าซ็อกเก็ตไม่เกี่ยวเนื่องกัน
int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);
การเรียกนี้จะส่งคืนจำนวนไบต์ที่อ่านลงในบัฟเฟอร์มิฉะนั้นจะส่งกลับค่า -1 เมื่อมีข้อผิดพลาด
พารามิเตอร์
sockfd - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
buf - เป็นบัฟเฟอร์ในการอ่านข้อมูล
len - เป็นความยาวสูงสุดของบัฟเฟอร์
flags - ตั้งค่าเป็น 0
from - เป็นตัวชี้ไปยัง struct sockaddr สำหรับโฮสต์ที่ต้องอ่านข้อมูล
fromlen - ตั้งค่าเป็น sizeof (struct sockaddr)
ใกล้ฟังก์ชั่น
ใกล้ฟังก์ชั่นที่ใช้ในการปิดการสื่อสารระหว่างลูกค้าและเซิร์ฟเวอร์ ไวยากรณ์มีดังนี้ -
int close( int sockfd );
การโทรนี้ส่งคืน 0 เมื่อสำเร็จมิฉะนั้นจะส่งกลับ -1 เมื่อมีข้อผิดพลาด
พารามิเตอร์
sockfd - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
ปิดฟังก์ชั่น
ปิดฟังก์ชั่นที่ใช้ในการสื่อสารได้อย่างสง่างามอย่างใกล้ชิดระหว่างลูกค้าและเซิร์ฟเวอร์ ฟังก์ชันนี้ให้การควบคุมมากขึ้นเมื่อเทียบกับฟังก์ชันปิด ด้านล่างเป็นไวยากรณ์ของการปิดระบบ -
int shutdown(int sockfd, int how);
การโทรนี้ส่งคืน 0 เมื่อสำเร็จมิฉะนั้นจะส่งกลับ -1 เมื่อมีข้อผิดพลาด
พารามิเตอร์
sockfd - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
how - ใส่หนึ่งในตัวเลข -
0 - ระบุว่าไม่อนุญาตให้รับ
1 - ระบุว่าไม่อนุญาตให้ส่งและ
2- ระบุว่าไม่อนุญาตให้ส่งและรับ เมื่อวิธีการที่ถูกตั้งไว้ที่ 2 มันเป็นสิ่งเดียวกับปิด ()
เลือกฟังก์ชั่น
เลือกฟังก์ชั่นแสดงให้เห็นซึ่งอธิบายไฟล์ที่ระบุมีความพร้อมสำหรับการอ่านพร้อมสำหรับการเขียนหรือมีเงื่อนไขข้อผิดพลาดที่รอดำเนินการ
เมื่อแอปพลิเคชันเรียกrecv หรือ recvfromแอปพลิเคชันจะถูกบล็อกจนกว่าข้อมูลจะมาถึงสำหรับซ็อกเก็ตนั้น แอปพลิเคชันอาจทำการประมวลผลที่มีประโยชน์อื่น ๆ ในขณะที่สตรีมข้อมูลขาเข้าว่างเปล่า อีกสถานการณ์หนึ่งคือเมื่อแอปพลิเคชันรับข้อมูลจากหลายซ็อกเก็ต
การเรียกrecv หรือ recvfromบนซ็อกเก็ตที่ไม่มีข้อมูลในคิวอินพุตจะป้องกันการรับข้อมูลจากซ็อกเก็ตอื่นทันที การเรียกใช้ฟังก์ชันที่เลือกช่วยแก้ปัญหานี้โดยให้โปรแกรมสำรวจที่จับซ็อกเก็ตทั้งหมดเพื่อดูว่าพร้อมใช้งานสำหรับการอ่านและเขียนแบบไม่ปิดกั้นหรือไม่
ด้านล่างเป็นไวยากรณ์ของการเลือก -
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
การโทรนี้ส่งคืน 0 เมื่อสำเร็จมิฉะนั้นจะส่งกลับ -1 เมื่อมีข้อผิดพลาด
พารามิเตอร์
nfds- ระบุช่วงของตัวอธิบายไฟล์ที่จะทดสอบ ฟังก์ชัน select () จะทดสอบตัวอธิบายไฟล์ในช่วง 0 ถึง nfds-1
readfds- ชี้ไปที่อ็อบเจ็กต์ประเภทfd_setที่อินพุตระบุตัวอธิบายไฟล์ที่จะตรวจสอบว่าพร้อมที่จะอ่านและเอาต์พุตระบุว่าตัวอธิบายไฟล์ใดพร้อมที่จะอ่าน สามารถเป็น NULL เพื่อระบุชุดว่าง
writefds- ชี้ไปที่อ็อบเจ็กต์ประเภทfd_setที่อินพุตระบุตัวอธิบายไฟล์ที่จะตรวจสอบว่าพร้อมที่จะเขียนและเอาต์พุตระบุว่าตัวอธิบายไฟล์ใดพร้อมที่จะเขียน สามารถเป็น NULL เพื่อระบุชุดว่าง
exceptfds- ชี้ไปที่อ็อบเจ็กต์ประเภทfd_setที่อินพุตระบุตัวอธิบายไฟล์ที่จะตรวจสอบเงื่อนไขข้อผิดพลาดที่รอดำเนินการและเอาต์พุตระบุว่าตัวอธิบายไฟล์ใดมีเงื่อนไขข้อผิดพลาดที่รอดำเนินการ สามารถเป็น NULL เพื่อระบุชุดว่าง
timeout- ชี้ไปที่โครงสร้างเวลาที่ระบุระยะเวลาที่การเรียกเลือกควรสำรวจตัวอธิบายสำหรับการดำเนินการ I / O ที่มีอยู่ หากค่าการหมดเวลาเป็น 0 การเลือกจะส่งกลับทันที หากอาร์กิวเมนต์การหมดเวลาเป็น NULL การเลือกจะบล็อกจนกว่าที่จับไฟล์ / ซ็อกเก็ตอย่างน้อยหนึ่งไฟล์พร้อมสำหรับการดำเนินการ I / O ที่พร้อมใช้งาน มิฉะนั้นการเลือกจะกลับมาหลังจากระยะเวลาในการหมดเวลาหมดไปหรือเมื่อตัวอธิบายไฟล์ / ซ็อกเก็ตอย่างน้อยหนึ่งไฟล์พร้อมสำหรับการดำเนินการ I / O
ค่าที่ส่งคืนจาก select คือจำนวนแฮนเดิลที่ระบุในชุดตัวอธิบายไฟล์ที่พร้อมสำหรับ I / O หากถึงขีด จำกัด เวลาที่ระบุโดยฟิลด์การหมดเวลาให้เลือก return 0 มีมาโครต่อไปนี้สำหรับจัดการชุดตัวอธิบายไฟล์ -
FD_CLR(fd, &fdset)- ล้างบิตสำหรับ file descriptor fd ใน file descriptor set fdset
FD_ISSET(fd, &fdset)- ส่งคืนค่าที่ไม่ใช่ศูนย์ถ้าบิตสำหรับ file descriptor fdถูกตั้งค่าใน file descriptor set ที่ชี้ไปที่fdsetและเป็น 0
FD_SET(fd, &fdset) - ตั้งค่าบิตสำหรับ file descriptor fd ใน file descriptor set fdset
FD_ZERO(&fdset) - กำหนดค่าเริ่มต้น file descriptor ตั้งค่า fdset ให้มีศูนย์บิตสำหรับตัวอธิบายไฟล์ทั้งหมด
ลักษณะการทำงานของมาโครเหล่านี้ไม่ได้กำหนดไว้หากอาร์กิวเมนต์ fd น้อยกว่า 0 หรือมากกว่าหรือเท่ากับ FD_SETSIZE
ตัวอย่าง
fd_set fds;
struct timeval tv;
/* do socket initialization etc.
tv.tv_sec = 1;
tv.tv_usec = 500000;
/* tv now represents 1.5 seconds */
FD_ZERO(&fds);
/* adds sock to the file descriptor set */
FD_SET(sock, &fds);
/* wait 1.5 seconds for any data to be read from any single socket */
select(sock+1, &fds, NULL, NULL, &tv);
if (FD_ISSET(sock, &fds)) {
recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len);
/* do something */
}
else {
/* do something else */
}
บทนี้จะอธิบายถึงฟังก์ชั่นตัวช่วยทั้งหมดที่ใช้ในการเขียนโปรแกรมซ็อกเก็ต ฟังก์ชั่นตัวช่วยอื่น ๆ มีอธิบายไว้ในบท -Ports and Servicesและเครือข่าย Byte Orders.
เขียนฟังก์ชั่น
เขียนความพยายามในการทำงานเพื่อไบต์เขียน nbyte จากบัฟเฟอร์ชี้โดยbufไปยังไฟล์ที่เกี่ยวข้องกับการอธิบายไฟล์เปิดfildes
คุณยังสามารถใช้ฟังก์ชันsend ()เพื่อส่งข้อมูลไปยังกระบวนการอื่น
#include <unistd.h>
int write(int fildes, const void *buf, int nbyte);
เมื่อดำเนินการสำเร็จแล้ว write () จะส่งคืนจำนวนไบต์ที่เขียนไปยังไฟล์ที่เกี่ยวข้องกับ fildes ตัวเลขนี้ไม่เคยมากกว่า nbyte มิฉะนั้นจะส่งคืน -1
พารามิเตอร์
fildes - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
buf - เป็นตัวชี้ไปยังข้อมูลที่คุณต้องการส่ง
nbyte- เป็นจำนวนไบต์ที่จะเขียน ถ้า nbyte เป็น 0 การเขียน () จะส่งกลับ 0 และไม่มีผลลัพธ์อื่น ๆ หากไฟล์นั้นเป็นไฟล์ปกติ มิฉะนั้นผลลัพธ์จะไม่ระบุ
อ่านฟังก์ชั่น
อ่านพยายามฟังก์ชั่นในการอ่าน nbyte ไบต์จากไฟล์ที่เกี่ยวข้องกับบัฟเฟอร์ fildes, ลงในบัฟเฟอร์ที่ชี้ไปตาม buf
คุณยังสามารถใช้ฟังก์ชันrecv ()เพื่ออ่านข้อมูลไปยังกระบวนการอื่น
#include <unistd.h>
int read(int fildes, const void *buf, int nbyte);
เมื่อดำเนินการสำเร็จแล้ว write () จะส่งคืนจำนวนไบต์ที่เขียนไปยังไฟล์ที่เกี่ยวข้องกับ fildes ตัวเลขนี้ไม่เคยมากกว่า nbyte มิฉะนั้นจะส่งคืน -1
พารามิเตอร์
fildes - เป็นตัวบอกซ็อกเก็ตที่ส่งคืนโดยฟังก์ชันซ็อกเก็ต
buf - เป็นบัฟเฟอร์ในการอ่านข้อมูล
nbyte - เป็นจำนวนไบต์ที่จะอ่าน
ส้อมฟังก์ชั่น
ส้อมฟังก์ชั่นสร้างกระบวนการใหม่ กระบวนการใหม่ที่เรียกว่ากระบวนการลูกจะเป็นสำเนาที่แน่นอนของกระบวนการเรียก (กระบวนการแม่) กระบวนการลูกจะสืบทอดแอตทริบิวต์จำนวนมากจากกระบวนการหลัก
#include <sys/types.h>
#include <unistd.h>
int fork(void);
เมื่อดำเนินการสำเร็จ fork () จะส่งคืน 0 ไปยังกระบวนการลูกและ ID กระบวนการของกระบวนการลูกไปยังกระบวนการแม่ มิฉะนั้น -1 จะถูกส่งกลับไปยังกระบวนการหลักไม่มีการสร้างกระบวนการย่อยและตั้งค่า errno เพื่อระบุข้อผิดพลาด
พารามิเตอร์
void - หมายความว่าไม่จำเป็นต้องมีพารามิเตอร์
bzeroฟังก์ชั่น
bzeroสถานที่ฟังก์ชั่นnbyte null ไบต์ในสตริงs ฟังก์ชันนี้ใช้เพื่อตั้งค่าโครงสร้างซ็อกเก็ตทั้งหมดด้วยค่า null
void bzero(void *s, int nbyte);
ฟังก์ชันนี้ไม่ส่งกลับอะไรเลย
พารามิเตอร์
s- ระบุสตริงที่ต้องเติมด้วย null ไบต์ นี่จะเป็นตัวแปรโครงสร้างแบบจุดต่อซ็อกเก็ต
nbyte- ระบุจำนวนไบต์ที่จะเติมด้วยค่า null นี่จะเป็นขนาดของโครงสร้างซ็อกเก็ต
bcmpฟังก์ชั่น
bcmpฟังก์ชั่นเปรียบเทียบ s1 สตริงไบต์กับไบต์สตริง s2 สตริงทั้งสองถือว่ามีความยาว nbyte ไบต์
int bcmp(const void *s1, const void *s2, int nbyte);
ฟังก์ชันนี้จะคืนค่า 0 หากสตริงทั้งสองเหมือนกันไม่เช่นนั้น 1 ฟังก์ชัน bcmp () จะคืนค่า 0 เสมอเมื่อ nbyte เป็น 0
พารามิเตอร์
s1 - ระบุสตริงแรกที่จะเปรียบเทียบ
s2 - ระบุสตริงที่สองที่จะเปรียบเทียบ
nbyte - ระบุจำนวนไบต์ที่จะเปรียบเทียบ
bcopyฟังก์ชั่น
bcopyฟังก์ชั่นสำเนา nbyte ไบต์จากสตริง s1 จะ s2 สตริง สตริงที่ทับซ้อนกันได้รับการจัดการอย่างถูกต้อง
void bcopy(const void *s1, void *s2, int nbyte);
ฟังก์ชันนี้ไม่ส่งกลับอะไรเลย
พารามิเตอร์
s1 - ระบุสตริงต้นทาง
s2v - ระบุสตริงปลายทาง
nbyte - ระบุจำนวนไบต์ที่จะคัดลอก
memsetฟังก์ชั่น
memsetฟังก์ชั่นนอกจากนี้ยังใช้กับตัวแปรโครงสร้างชุดในลักษณะเดียวกับที่bzero. ดูไวยากรณ์ที่ระบุด้านล่าง
void *memset(void *s, int c, int nbyte);
ฟังก์ชันนี้จะส่งกลับตัวชี้เป็นโมฆะ ในความเป็นจริงตัวชี้ไปยังหน่วยความจำที่ตั้งไว้และคุณต้องจัดลำดับตามนั้น
พารามิเตอร์
s - ระบุแหล่งที่จะตั้งค่า
c - ระบุอักขระที่จะตั้งค่าบน nbyte places
nbyte - ระบุจำนวนไบต์ที่จะกำหนด
ในการสร้างเซิร์ฟเวอร์ TCP คุณต้องทำตามขั้นตอนด้านล่าง -
สร้างซ็อกเก็ตด้วยการเรียกระบบซ็อกเก็ต ()
ผูกซ็อกเก็ตกับแอดเดรสโดยใช้การเรียกระบบbind () สำหรับซ็อกเก็ตเซิร์ฟเวอร์บนอินเทอร์เน็ตแอดเดรสประกอบด้วยหมายเลขพอร์ตบนเครื่องโฮสต์
ฟังการเชื่อมต่อกับการโทรของระบบlisten ()
ยอมรับการเชื่อมต่อกับการเรียกระบบaccept () โดยทั่วไปสายนี้จะบล็อกจนกว่าไคลเอ็นต์จะเชื่อมต่อกับเซิร์ฟเวอร์
ส่งและรับข้อมูลโดยใช้การเรียกระบบread ()และwrite ()
ตอนนี้ให้เราใส่ขั้นตอนเหล่านี้ในรูปแบบของซอร์สโค้ด ใส่รหัสนี้ลงในไฟล์server.cและคอมไพล์ด้วยคอมไพเลอร์gcc
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
int main( int argc, char *argv[] ) {
int sockfd, newsockfd, portno, clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
/* First call to socket() function */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
/* Initialize socket structure */
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 5001;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
/* Now bind the host address using bind() call.*/
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
/* Now start listening for the clients, here process will
* go in sleep mode and will wait for the incoming connection
*/
listen(sockfd,5);
clilen = sizeof(cli_addr);
/* Accept actual connection from the client */
newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
if (newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}
/* If connection is established then start communicating */
bzero(buffer,256);
n = read( newsockfd,buffer,255 );
if (n < 0) {
perror("ERROR reading from socket");
exit(1);
}
printf("Here is the message: %s\n",buffer);
/* Write a response to the client */
n = write(newsockfd,"I got your message",18);
if (n < 0) {
perror("ERROR writing to socket");
exit(1);
}
return 0;
}
จัดการการเชื่อมต่อหลายรายการ
เพื่อให้เซิร์ฟเวอร์สามารถจัดการการเชื่อมต่อพร้อมกันหลายรายการเราได้ทำการเปลี่ยนแปลงต่อไปนี้ในโค้ดด้านบน -
ใส่คำสั่งยอมรับและรหัสต่อไปนี้ในลูปไม่มีที่สิ้นสุด
หลังจากสร้างการเชื่อมต่อแล้วให้เรียกใช้fork ()เพื่อสร้างกระบวนการใหม่
กระบวนการลูกจะปิดsockfdและเรียกใช้ฟังก์ชันการประมวลผลโดยส่งผ่านตัวบอกไฟล์ซ็อกเก็ตใหม่เป็นอาร์กิวเมนต์ เมื่อกระบวนการทั้งสองเสร็จสิ้นการสนทนาตามที่ระบุโดยการกลับมาของกระบวนการdoprocessing ()กระบวนการนี้ก็จะออกไป
ปิดการปกครองnewsockfd เนื่องจากโค้ดทั้งหมดนี้อยู่ในวงวนที่ไม่มีที่สิ้นสุดโค้ดจะกลับไปที่คำสั่ง accept เพื่อรอการเชื่อมต่อครั้งต่อไป
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
void doprocessing (int sock);
int main( int argc, char *argv[] ) {
int sockfd, newsockfd, portno, clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n, pid;
/* First call to socket() function */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
/* Initialize socket structure */
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 5001;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
/* Now bind the host address using bind() call.*/
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
/* Now start listening for the clients, here
* process will go in sleep mode and will wait
* for the incoming connection
*/
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1) {
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}
/* Create child process */
pid = fork();
if (pid < 0) {
perror("ERROR on fork");
exit(1);
}
if (pid == 0) {
/* This is the client process */
close(sockfd);
doprocessing(newsockfd);
exit(0);
}
else {
close(newsockfd);
}
} /* end of while */
}
การแบ่งรหัสต่อไปนี้แสดงการใช้งานฟังก์ชันการประมวลผลแบบง่าย
void doprocessing (int sock) {
int n;
char buffer[256];
bzero(buffer,256);
n = read(sock,buffer,255);
if (n < 0) {
perror("ERROR reading from socket");
exit(1);
}
printf("Here is the message: %s\n",buffer);
n = write(sock,"I got your message",18);
if (n < 0) {
perror("ERROR writing to socket");
exit(1);
}
}
ในการทำให้กระบวนการเป็นไคลเอนต์ TCP คุณต้องทำตามขั้นตอนที่ระบุด้านล่าง & ลบ;
สร้างซ็อกเก็ตด้วยการเรียกระบบซ็อกเก็ต ()
เชื่อมต่อซ็อกเก็ตกับแอดเดรสของเซิร์ฟเวอร์โดยใช้การเรียกระบบconnect ()
ส่งและรับข้อมูล มีหลายวิธีในการดำเนินการนี้ แต่วิธีที่ง่ายที่สุดคือใช้การเรียกระบบread ()และwrite ()
ตอนนี้ให้เราใส่ขั้นตอนเหล่านี้ในรูปแบบของซอร์สโค้ด ใส่รหัสนี้ลงในไฟล์client.c และรวบรวมด้วย gcc คอมไพเลอร์
รันโปรแกรมนี้และส่งชื่อโฮสต์และหมายเลขพอร์ตของเซิร์ฟเวอร์เพื่อเชื่อมต่อกับเซิร์ฟเวอร์ซึ่งคุณต้องเรียกใช้ในหน้าต่าง Unix อื่นอยู่แล้ว
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
int main(int argc, char *argv[]) {
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
/* Create a socket point */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
/* Now connect to the server */
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR connecting");
exit(1);
}
/* Now ask for a message from the user, this message
* will be read by server
*/
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
/* Send message to the server */
n = write(sockfd, buffer, strlen(buffer));
if (n < 0) {
perror("ERROR writing to socket");
exit(1);
}
/* Now read server response */
bzero(buffer,256);
n = read(sockfd, buffer, 255);
if (n < 0) {
perror("ERROR reading from socket");
exit(1);
}
printf("%s\n",buffer);
return 0;
}
นี่คือรายการฟังก์ชันทั้งหมดที่เกี่ยวข้องกับการเขียนโปรแกรมซ็อกเก็ต
ฟังก์ชั่นพอร์ตและบริการ
Unix จัดเตรียมฟังก์ชันต่อไปนี้เพื่อดึงชื่อบริการจากไฟล์ / etc / services
struct servent *getservbyname(char *name, char *proto) - การโทรนี้ใช้ชื่อบริการและชื่อโปรโตคอลและส่งกลับหมายเลขพอร์ตที่เกี่ยวข้องสำหรับบริการนั้น
struct servent *getservbyport(int port, char *proto) - การโทรนี้ใช้หมายเลขพอร์ตและชื่อโปรโตคอลและส่งกลับชื่อบริการที่เกี่ยวข้อง
ฟังก์ชันการสั่งซื้อไบต์
unsigned short htons (unsigned short hostshort) - ฟังก์ชันนี้จะแปลงปริมาณ 16 บิต (2 ไบต์) จากคำสั่งไบต์ของโฮสต์เป็นลำดับไบต์ของเครือข่าย
unsigned long htonl (unsigned long hostlong) - ฟังก์ชันนี้จะแปลงปริมาณ 32 บิต (4 ไบต์) จากคำสั่งไบต์ของโฮสต์เป็นคำสั่งไบต์ของเครือข่าย
unsigned short ntohs (unsigned short netshort) - ฟังก์ชันนี้จะแปลงปริมาณ 16 บิต (2 ไบต์) จากคำสั่งไบต์เครือข่ายเป็นคำสั่งไบต์ของโฮสต์
unsigned long ntohl (unsigned long netlong) - ฟังก์ชันนี้จะแปลงปริมาณ 32 บิตจากคำสั่งไบต์เครือข่ายเป็นคำสั่งไบต์ของโฮสต์
ฟังก์ชั่นที่อยู่ IP
int inet_aton (const char *strptr, struct in_addr *addrptr)- การเรียกใช้ฟังก์ชันนี้จะแปลงสตริงที่ระบุในสัญกรณ์จุดมาตรฐานอินเทอร์เน็ตเป็นที่อยู่เครือข่ายและจัดเก็บที่อยู่ในโครงสร้างที่มีให้ ที่อยู่ที่แปลงจะอยู่ใน Network Byte Order (ไบต์เรียงลำดับจากซ้ายไปขวา) จะคืนค่า 1 หากสตริงถูกต้องและ 0 เมื่อเกิดข้อผิดพลาด
in_addr_t inet_addr (const char *strptr)- การเรียกใช้ฟังก์ชันนี้จะแปลงสตริงที่ระบุในสัญกรณ์จุดมาตรฐานอินเทอร์เน็ตเป็นค่าจำนวนเต็มที่เหมาะสำหรับใช้เป็นที่อยู่อินเทอร์เน็ต ที่อยู่ที่แปลงแล้วจะอยู่ใน Network Byte Order (ไบต์เรียงลำดับจากซ้ายไปขวา) ส่งคืนไบต์เครือข่ายไบนารี 32 บิตที่สั่งซื้อที่อยู่ IPv4 และ INADDR_NONE เมื่อเกิดข้อผิดพลาด
char *inet_ntoa (struct in_addr inaddr) - การเรียกใช้ฟังก์ชันนี้จะแปลงที่อยู่โฮสต์อินเทอร์เน็ตที่ระบุเป็นสตริงในสัญกรณ์จุดมาตรฐานอินเทอร์เน็ต
ฟังก์ชั่น Socket Core
int socket (int family, int type, int protocol) - การโทรนี้ส่งคืนตัวอธิบายซ็อกเก็ตที่คุณสามารถใช้ในการเรียกระบบในภายหลังหรือให้ -1 เมื่อเกิดข้อผิดพลาด
int connect (int sockfd, struct sockaddr *serv_addr, int addrlen)- ฟังก์ชันการเชื่อมต่อถูกใช้โดยไคลเอนต์ TCP เพื่อสร้างการเชื่อมต่อกับเซิร์ฟเวอร์ TCP การเรียกนี้จะคืนค่า 0 หากเชื่อมต่อกับเซิร์ฟเวอร์สำเร็จมิฉะนั้นจะคืนค่า -1
int bind(int sockfd, struct sockaddr *my_addr,int addrlen)- ฟังก์ชั่นผูกกำหนดที่อยู่โปรโตคอลท้องถิ่นให้กับซ็อกเก็ต การเรียกนี้จะคืนค่า 0 หากผูกกับแอดเดรสสำเร็จมิฉะนั้นจะคืนค่า -1
int listen(int sockfd, int backlog)- ฟังก์ชั่นฟังถูกเรียกโดยเซิร์ฟเวอร์ TCP เพื่อฟังคำขอของไคลเอ็นต์เท่านั้น การเรียกนี้ส่งคืน 0 เมื่อสำเร็จมิฉะนั้นจะส่งกลับ -1
int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)- ฟังก์ชันยอมรับถูกเรียกโดยเซิร์ฟเวอร์ TCP เพื่อยอมรับคำขอของไคลเอ็นต์และเพื่อสร้างการเชื่อมต่อจริง การเรียกนี้ส่งคืนตัวอธิบายที่ไม่ใช่เชิงลบเกี่ยวกับความสำเร็จมิฉะนั้นจะส่งกลับ -1
int send(int sockfd, const void *msg, int len, int flags)- ฟังก์ชันส่งใช้เพื่อส่งข้อมูลผ่านซ็อกเก็ตสตรีมหรือซ็อกเก็ตดาต้าแกรมที่เชื่อมต่อ การเรียกนี้ส่งคืนจำนวนไบต์ที่ส่งออกไปมิฉะนั้นจะส่งกลับ -1
int recv (int sockfd, void *buf, int len, unsigned int flags)- ฟังก์ชัน recv ใช้เพื่อรับข้อมูลผ่านซ็อกเก็ตสตรีมหรือซ็อกเก็ตดาตาแกรมที่เชื่อมต่อ การเรียกนี้จะส่งคืนจำนวนไบต์ที่อ่านลงในบัฟเฟอร์มิฉะนั้นจะส่งกลับค่า -1 เมื่อมีข้อผิดพลาด
int sendto (int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen)- ฟังก์ชัน sendto ใช้เพื่อส่งข้อมูลผ่านซ็อกเก็ตดาตาแกรมที่ไม่ได้เชื่อมต่อ การโทรนี้ส่งคืนจำนวนไบต์ที่ส่งมิฉะนั้นจะส่งกลับ -1 เมื่อมีข้อผิดพลาด
int recvfrom (int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen)- ฟังก์ชัน recvfrom ใช้เพื่อรับข้อมูลจากซ็อกเก็ตดาตาแกรมที่ไม่ได้เชื่อมต่อ การเรียกนี้จะส่งคืนจำนวนไบต์ที่อ่านลงในบัฟเฟอร์มิฉะนั้นจะส่งกลับค่า -1 เมื่อมีข้อผิดพลาด
int close (int sockfd)- ฟังก์ชันปิดใช้เพื่อปิดการสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์ การเรียกนี้ส่งคืน 0 เมื่อสำเร็จมิฉะนั้นจะส่งกลับ -1
int shutdown (int sockfd, int how)- ฟังก์ชันการปิดระบบใช้เพื่อปิดการสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์อย่างสง่างาม ฟังก์ชันนี้ให้การควบคุมมากกว่าเมื่อเทียบกับฟังก์ชันปิด จะคืนค่า 0 เมื่อสำเร็จหรือไม่เช่นนั้น -1
int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) - ฟังก์ชั่นนี้ใช้เพื่ออ่านหรือเขียนซ็อกเก็ตหลายตัว
ฟังก์ชั่นตัวช่วยซ็อกเก็ต
int write (int fildes, const void *buf, int nbyte)- ฟังก์ชั่นการเขียนพยายามเขียนไบต์ nbyte จากบัฟเฟอร์ที่ชี้ไปโดย buf ไปยังไฟล์ที่เกี่ยวข้องกับตัวอธิบายไฟล์ที่เปิด fildes เมื่อดำเนินการสำเร็จแล้ว write () จะส่งคืนจำนวนไบต์ที่เขียนไปยังไฟล์ที่เกี่ยวข้องกับ fildes ตัวเลขนี้ไม่เคยมากกว่า nbyte มิฉะนั้นจะส่งคืน -1
int read (int fildes, const void *buf, int nbyte)- ฟังก์ชั่นการอ่านพยายามอ่านไบต์ nbyte จากไฟล์ที่เกี่ยวข้องกับ open file descriptor, fildes, เข้าไปในบัฟเฟอร์ที่ชี้ไปโดย buf เมื่อดำเนินการสำเร็จแล้ว write () จะส่งคืนจำนวนไบต์ที่เขียนไปยังไฟล์ที่เกี่ยวข้องกับ fildes ตัวเลขนี้ไม่เคยมากกว่า nbyte มิฉะนั้นจะส่งคืน -1
int fork (void)- ฟังก์ชันส้อมสร้างกระบวนการใหม่ กระบวนการใหม่ที่เรียกว่ากระบวนการลูกจะเป็นสำเนาที่แน่นอนของกระบวนการเรียก (กระบวนการหลัก)
void bzero (void *s, int nbyte)- ฟังก์ชัน bzero จะวาง nbyte null ไบต์ในสตริง s ฟังก์ชันนี้จะใช้เพื่อตั้งค่าโครงสร้างซ็อกเก็ตทั้งหมดด้วยค่า null
int bcmp (const void *s1, const void *s2, int nbyte)- ฟังก์ชัน bcmp เปรียบเทียบสตริงไบต์ s1 กับสตริงไบต์ s2 ทั้งสองสายถือว่ามีความยาว nbyte ไบต์
void bcopy (const void *s1, void *s2, int nbyte)- ฟังก์ชัน bcopy คัดลอกไบต์ nbyte จากสตริง s1 ไปยังสตริง s2 สตริงที่ทับซ้อนกันได้รับการจัดการอย่างถูกต้อง
void *memset(void *s, int c, int nbyte) - ฟังก์ชัน memset ยังใช้เพื่อตั้งค่าตัวแปรโครงสร้างในลักษณะเดียวกับ bzero