ปัญหาทั้งหมดในวิทยาการคอมพิวเตอร์สามารถแก้ไขได้ด้วยการอ้อมอีกระดับหนึ่ง ยกเว้นปัญหาที่มีการอ้อมมากเกินไป
David Wheeler

คำพูดที่ดีและบทนำที่ดีในการโพสต์บล็อกนี้! เราต้องการให้ข้อมูลเชิงลึกเกี่ยวกับ Internal Networking Interface (INI) ของ Mongoose Embedded Web Server

พื้นหลัง

เดิมที Mongoose ใช้ BSD-socket API และฟังก์ชั่นซ็อกเก็ตจำนวนมากและโหมดที่ไม่ปิดกั้น นั่นหมายความว่าสามารถพกพาได้เฉพาะกับแพลตฟอร์มที่รองรับ API นี้เท่านั้น มันใช้งานได้บ้างสำหรับแพลตฟอร์มที่รองรับซ็อกเก็ตเหล่านั้น แต่เราพบว่าแพลตฟอร์มส่วนใหญ่รองรับในแบบของตัวเอง ผู้เขียน SDK ฮาร์ดแวร์ "ลืม" ฟังก์ชั่น เลือก นอกจากนี้ พวกเขายังต้องการรวมฟังก์ชัน เลือก และ ยอมรับ เข้าด้วยกัน สิ่งนี้ทำให้โซลูชัน BSD-socket API ดั้งเดิมของเราใช้งานยาก เรามีสองทางเลือกในการแก้ไข:

  1. อย่าพอร์ตไปยังแพลตฟอร์มที่ไม่เพียงแค่รองรับ API ซ็อกเก็ตแบบเต็ม
  2. ใช้ฟังก์ชันที่ขาดหายไปสำหรับแต่ละแพลตฟอร์ม

การเคลื่อนไหวที่กล้าหาญ

ตัวเลือกที่หนึ่งไม่ใช่วิธีที่กล้าหาญในการทำสิ่งต่าง ๆ ใช่ไหม? ? เรารักความซับซ้อน

ไมโครชิปพังพอนตัวแรกที่ถูกย้ายไปยังคือ Arduino (Mega) และดำเนินการโดยใช้วิธีที่สอง เราได้พัฒนาเลเยอร์ซ็อกเก็ต BSD ที่ด้านบนของ Arduino Socket Library มันทำงานได้ดีกับอาการสะอึกบางอย่าง

แพลตฟอร์มถัดไปคือ ESP8266 ที่นี่ เราต้องตัดสินใจว่าเราต้องการบรรลุอะไรจริงๆ เราเริ่มทำงานกับ ESP8266 เมื่อนานมาแล้ว ไม่มี RTOS SDK พร้อม LWIP (และซ็อกเก็ต BSD) นอกจากนี้ เราต้องการพอร์ต Mongoose ไปยังแพลตฟอร์มแบบฝังอีกมากมาย และเริ่มเข้าใจว่าแพลตฟอร์มส่วนใหญ่จะมีปัญหากับซ็อกเก็ต BSD

ถึงเวลาที่จะแยก Mongoose Embedded Web Server ออกเป็นระดับโปรโตคอลและระดับเครือข่าย ใช่ เดวิด วีลเลอร์ ถึงเวลาเพิ่มระดับของการอ้อมไปอีกระดับแล้ว

การวางแผนอินเทอร์เฟซเครือข่ายภายในของเรา

อินเทอร์เฟซเครือข่ายภายในของเรา (INI) ควรจะเป็นเช่นนั้น

  1. เป็นการเชื่อมต่อ (ไม่ใช่ซ็อกเก็ต!) - Mongoose เน้นการเชื่อมต่อ ดังนั้นการใช้อินเทอร์เฟซเชิงการเชื่อมต่อควรเรียบง่ายกว่านี้
  2. รองรับทั้ง TCP และ UDP (ส่วนที่เหลือของโปรโตคอลที่รองรับจะขึ้นอยู่กับทั้งสองนี้)
  3. เป็น async ตามการโทรกลับ
  4. เรียบง่าย!

คุณเดาถูกแล้ว จุดที่เป็นปัญหาที่สุดคือ: จงเรียบง่าย ปริศนาของเรา:

ในด้านหนึ่ง การออกแบบอินเทอร์เฟซที่ยากต่อการใช้งานนั้นเป็นเรื่องง่าย แต่แล้วใครจะเป็นคนใช้งานล่ะ?

ในทางกลับกัน Mongoose มีคุณสมบัติมากมาย รวมถึงคุณสมบัติที่เกี่ยวข้องกับเครือข่าย การหาสมดุลระหว่างฟังก์ชันการทำงานและความเรียบง่ายเป็นสิ่งสำคัญ

เป็นผลให้อินเทอร์เฟซเครือข่ายภายใน Mongoose มีฟังก์ชันประมาณ 20 ฟังก์ชัน

ภาพรวมของ INI

มีฟังก์ชันหลายกลุ่มใน INI API:

  1. การจัดการการเชื่อมต่อ
  2. ข้อมูลส่งและรับ
  3. ฟังก์ชั่นไคลเอนต์
  4. ฟังก์ชั่นเซิร์ฟเวอร์
  5. ฟังก์ชั่นยูทิลิตี้ชุดเล็ก

ฟังก์ชันส่วนใหญ่ (ยกเว้นฟังก์ชันการจัดการการเชื่อมต่อ) มีสองเวอร์ชัน: หนึ่งเวอร์ชันสำหรับ TCP และอีกหนึ่งเวอร์ชันสำหรับ UDP

สุดท้ายแต่ไม่ท้ายสุด: มีชุดการโทรกลับ INI ของเรามีอินเทอร์เฟซแบบโทรกลับ กล่าวโดยสรุป นั่นหมายความว่า ฟังก์ชันนั้น เช่น เชื่อมต่อส่งคืนระหว่างกลางและเรียกใช้การโทรกลับ จากนั้น การเชื่อมต่อจะถูกสร้างขึ้น (หรือมีข้อผิดพลาดเกิดขึ้น)

รูปแบบการโต้ตอบของเลเยอร์ค่อนข้างดั้งเดิม: โปรโตคอลระดับแอปพลิเคชันใช้โปรโตคอลการขนส่ง API ในขณะที่โปรโตคอลการขนส่งใช้การใช้งาน INI เพื่อสื่อสารกับ API เฉพาะแพลตฟอร์ม (หรืออุปกรณ์)

ในขณะนี้ เรามีการใช้งาน INI 3 แบบ:

  1. ซ็อกเก็ต BSD
  2. LWIP
  3. SimpleLink (Texas Instruments CC3100/C3200)

การใช้งานเฉพาะของ INIถูกเลือก ณ เวลาคอมไพล์โดยการกำหนดมาโคร CS_PLATFORM (สามารถระบุมาโครนี้ได้ด้วยตนเอง โดยใช้ตัวเลือกคอมไพเลอร์ หรือชุดของ #ifdef #elseในโค้ด Mongoose จะพยายามดำเนินการให้คุณ)

มันทำงานอย่างไร

การใช้งาน INI เฉพาะ ต้องใช้ชุดของฟังก์ชันเพื่อสร้างการเชื่อมต่อ ส่งข้อมูล รับ ฯลฯ แกนหลักเรียกฟังก์ชันเหล่านี้เพื่อสร้างการเชื่อมต่อ ส่งข้อมูล ฯลฯ หาก INI strong>ต้องการส่งข้อมูลประเภทใด ๆ ไปยังคอร์ โดยจะเรียกใช้การโทรกลับที่นำไปใช้เป็นส่วนใหญ่ใน Mongoose Core แทนที่จะเป็นใน INI กล่าวคือ ผู้นำไปใช้ INI ไม่ควรจัดเตรียมการใช้งานฟังก์ชันดังกล่าว (ในส่วนใหญ่ กรณีที่พวกเขาตั้งชื่อจะลงท้ายด้วย _cb) อย่างไรก็ตาม INI ควรใช้การโทรกลับหนึ่งครั้งเช่นกัน (mg_if_recved,ดูด้านล่าง)

นี่คือแผนภาพเล็กๆ ของลำดับการดำเนินการ:

ฟังก์ชั่น

ตอนนี้ มาดูภาพรวมโดยย่อของฟังก์ชัน INI (คำอธิบายแบบเต็มสามารถพบได้ในเอกสาร Mongoose ที่นี่):

ฟังก์ชั่นการจัดการการเชื่อมต่อ:

กิจกรรมส่วนใหญ่ที่เกี่ยวข้องกับการจัดการการเชื่อมต่อนั้นดำเนินการโดย Mongoose เอง ดังนั้น INI จึงมีฟังก์ชันเพื่อตอบสนองการดำเนินการเฉพาะแพลตฟอร์ม ตัวอย่างเช่น “Socket INI” จะสร้างซ็อกเก็ตในการใช้งาน mg_if_create_conn

  • mg_if_create_conn
  • mg_if_destroy_conn
  • mg_close_conn

ฟังก์ชั่นไคลเอนต์และเซิร์ฟเวอร์:

  • mg_if_connect_tcp
  • mg_if_connect_udp

เมื่อสร้างการเชื่อมต่อแล้ว ควรเรียกใช้ฟังก์ชัน mg_if_connect_cb

  • mg_if_listen_tcp
  • mg_if_listen_udp
  • mg_if_accept_new_conn

จากนั้น INI จะยอมรับการเชื่อมต่อใหม่และควรเรียกใช้ mg_if_accept_tcp_cb

ฟังก์ชั่นส่งและรับ:

  • mg_if_tcp_send, mg_if_udp_send และการโทรกลับที่เกี่ยวข้อง mg_if_sent_cb

เมื่อได้รับ INIควรเรียกใช้การเรียกกลับ mg_if_recv_tcp_cbและ mg_if_recv_udp_cb หากแกนกลางรับรู้ปริมาณการใช้ มันจะเรียก mg_if_recved

ภารกิจเสร็จสมบูรณ์?

เราบรรลุสิ่งที่เราตั้งใจจะทำหรือไม่? นี่เป็นการเตือนใจถึงสิ่งที่เราต้องการ:

Internal Networking Interfaces (INI) ของเราควรจะเป็น

  1. เป็นการเชื่อมต่อ (ไม่ใช่ซ็อกเก็ต!) - Mongoose เน้นการเชื่อมต่อ ดังนั้นการใช้อินเทอร์เฟซเชิงการเชื่อมต่อควรเรียบง่ายกว่านี้
  2. รองรับทั้ง TCP และ UDP (ส่วนที่เหลือของโปรโตคอลที่รองรับจะขึ้นอยู่กับทั้งสองนี้)
  3. เป็น async ตามการโทรกลับ
  4. เรียบง่าย!

เราคิดว่าเราเข้าใจแล้ว ขณะนี้มีวิธีพอร์ต Mongoose ไปยังแพลตฟอร์มต่างๆ มันอาจจะไม่ใช่เรื่องง่ายเสมอไป แต่ก็เป็นไปได้ และเราไม่จำเป็นต้องสร้างทางลัดหรืออะไรทำนองนั้นเพื่อให้มันเกิดขึ้น

เมื่อมองไปในอนาคต เรากำลังวางแผนที่จะพอร์ต Mongoose ไปยังแพลตฟอร์มแบบฝังตัวเพิ่มเติม ดังนั้นเราจะจบลงด้วยการใช้งาน INI ที่แตกต่างกัน 5–6 แบบ คอยติดตามมัน!

เผยแพร่ครั้งแรกที่ blog.cesanta.com.