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