UDP ที่ไม่บล็อกหรือแยกเธรดสำหรับรับ


10

ฉันกำลังสร้างเกมผู้เล่นหลายคน (สำหรับผู้เล่นอายุต่ำกว่า 64 ปี) ฉันตัดสินใจที่จะมีเธรดแยกต่างหากสำหรับลูปเครือข่าย แต่ฉันสงสัยว่ามันจะดีกว่าถ้าจะสร้างเธรดพิเศษสำหรับรับ UDP หรือตั้งซ็อกเก็ตการรับบนการไม่บล็อก (ไม่มีเธรดพิเศษ)

หรือจะเป็นการดีกว่าถ้าใช้วิธีอื่นเช่น Asynchronous Sockets ยินดีต้อนรับวิธีการที่ดีกว่าเสมอ!

คำตอบ:


6

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

แต่ถ้าคุณมีปัญหาและอยากจะเขียนรหัสเครือข่ายของคุณใหม่อีกครั้งฉันคิดว่าคุณมีตัวเลือกหลัก ๆ สี่อย่าง:

  1. รหัสบล็อกแบบมัลติเธรด (สิ่งที่คุณกำลังทำอยู่ตอนนี้)
  2. ซ็อกเก็ตที่ไม่ปิดกั้นพร้อมการแจ้งเตือนระดับที่เรียก
  3. ซ็อกเก็ตที่ไม่ปิดกั้นพร้อมการแจ้งเตือนการเปลี่ยนแปลงความพร้อม
  4. ซ็อกเก็ตแบบอะซิงโครนัส

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

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

แต่เหนือสิ่งอื่นใดการใช้งานซ็อกเก็ตส่วนใหญ่ (และการใช้งาน I / O ส่วนใหญ่ ณ เวลานั้น) จะไม่ถูกบล็อกในระดับต่ำสุด การดำเนินการบล็อกนั้นมีไว้เพื่อทำให้การพัฒนาโปรแกรมง่ายขึ้นเล็กน้อย เมื่อซ็อกเก็ตปิดกั้น CPU ในเธรดนั้นไม่มีการใช้งานอย่างสมบูรณ์ดังนั้นเหตุใดจึงสร้างสิ่งที่เป็นนามธรรมที่ไม่มีการปิดกั้นการบล็อกสิ่งที่เป็นนามธรรมมากกว่างานที่ไม่ได้ปิดกั้นอยู่แล้ว

การเขียนโปรแกรมซ็อกเก็ตที่ไม่มีการปิดกั้นนั้นค่อนข้างน่ากังวลหากคุณยังไม่ได้ลองใช้ แต่กลับกลายเป็นว่ามันค่อนข้างง่ายและถ้าคุณกำลังทำโพลอินพุตอยู่คุณมีความคิดที่จะทำซ็อกเก็ตที่ไม่บล็อก

สิ่งแรกที่คุณต้องทำคือตั้งค่าซ็อกเก็ตเป็นแบบไม่บล็อก คุณทำอย่างนั้นกับfcntl()คุณทำด้วย

หลังจากนั้นก่อนที่จะทำsend(), recv(), sendto(), recvfrom(), accept()( connect()เป็นที่แตกต่างกันบิต) หรือสายอื่น ๆ ที่สามารถป้องกันด้ายคุณเรียกselect()บนซ็อกเก็ตselect()แจ้งให้คุณทราบว่าการดำเนินการอ่านหรือเขียนที่ตามมาสามารถทำได้บนซ็อกเก็ตโดยไม่บล็อกหรือไม่ หากเป็นกรณีนี้คุณสามารถดำเนินการตามที่คุณต้องการได้อย่างปลอดภัยและซ็อกเก็ตจะไม่บล็อก

รวมถึงสิ่งนี้ในเกมค่อนข้างง่าย หากคุณมีวนซ้ำเกมอยู่แล้วเช่นนี้:

while game_is_running do
    poll_input()
    update_world()
    do_sounds()
    draw_world()
end

คุณสามารถเปลี่ยนเป็นแบบนี้ได้:

while game_is_running do
    poll_input()
    read_network()
    update_world()
    do_sounds()
    write_network()
    draw_world()
end

ที่ไหน

function read_network()
    while select(socket, READ) do
        game.net_input.enqueue(recv(socket))
    end
end

และ

function write_network()
    while not game.net_output.empty and select(socket, WRITE) do
        send(socket, game.net_output.dequeue())
    end
end

ในแง่ของทรัพยากรหนังสือเล่มเดียวที่ฉันคิดว่าทุกคนต้องมีในชั้นหนังสือแม้ว่าจะเป็นหนังสือเล่มเดียวที่พวกเขามีคือ " Unix Network Programming, Vol 1 " โดย Richard Stevens ปลายปี ไม่สำคัญว่าคุณใช้ Windows หรือระบบปฏิบัติการอื่นหรือการเขียนโปรแกรมซ็อกเก็ตภาษา อย่าคิดว่าคุณเข้าใจซ็อกเก็ตจนกว่าคุณจะอ่านหนังสือเล่มนี้

แหล่งข้อมูลอื่นที่คุณสามารถค้นหาภาพรวมทั่วไปของโซลูชันที่มีในแง่ของการเขียนโปรแกรมซ็อกเก็ตหลายรายการ (ส่วนใหญ่เกี่ยวข้องกับการเขียนโปรแกรมเซิร์ฟเวอร์) คือหน้านี้


ฉันเพิ่งเริ่มโปรแกรมเกมของฉันดังนั้นการเขียนใหม่จึงไม่เป็นปัญหา คำตอบนี้มีประโยชน์จริง ๆ และขอบคุณสำหรับคำแนะนำหนังสือ คุณรู้หรือไม่ว่าหนังสือที่เฉพาะเจาะจงมากขึ้นเกี่ยวกับการสร้างเครือข่ายในเกม
มีเหตุมีผล

3
@YannickLange: ไม่ แต่ฉันจะไม่แนะนำให้คุณมองหาหนังสือเฉพาะเกมเนื่องจากการเชื่อมต่อเครือข่ายเป็นคนประเภทที่ไม่เชื่อเรื่องพระเจ้าและยังไม่มีหนังสือเล่มใดที่ขึ้นอยู่กับระดับของหนังสือของ Stevens ความรุ่งโรจน์สำหรับการใช้ UDP แม้ว่าการใช้โปรโตคอลที่มุ่งเน้นข้อความเป็นความคิดที่ดีเมื่อเขียนโปรแกรมที่เน้นข้อความ (เช่นเกมส่วนใหญ่) หลายคนมักจะจบลงด้วยการเขียนข้อความที่เป็นนามธรรมผ่าน TCP ซึ่งเป็นสิ่งที่เป็นนามธรรมที่สร้างขึ้นผ่านโปรโตคอลที่มุ่งเน้นข้อความ (IP)
Panda Pyjama

-1

คุณไม่ได้เขียนภาษาที่ใช้ในการเขียนโปรแกรม แต่ภาษาส่วนใหญ่จะมีเฟรมเวิร์กซ็อกเก็ตแบบอะซิงโครนัสที่ดีซึ่งมักจะใช้คุณสมบัติของระบบปฏิบัติการพื้นฐาน

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

ดังนั้นฉันขอแนะนำให้คุณใช้ซ็อกเก็ตแบบอะซิงโครนัส


4
ทำไมเขาถึงต้องการเธรดสำหรับซ็อกเก็ตทุกอัน? โดยเฉพาะอย่างยิ่งกับซ็อกเก็ต async? เซิร์ฟเวอร์เฮฟวี่เวทจำนวนมากใช้โมเดล "กลุ่มเธรด" เพื่อประมวลผลข้อมูลซ็อกเก็ตโดยที่แต่ละบริการเธรด N ไคลเอ็นต์และพวกเขาจะถูกวางหรือฆ่าตามความจำเป็น แค่คิดว่าคำตอบนี้จำเป็นต้องได้รับการปรับปรุง: D
Grimshaw

@Grimshaw ฉันคิดว่าคุณเข้าใจผิดคำตอบของฉัน ฉันคิดว่าฉันได้ชี้แจงแล้วว่าแบบจำลองแบบมัลติเธรดจะต้องใช้เมื่อปิดกั้นซ็อกเก็ต
ฟิลิปป์

@ ฟิลิปฉันไม่ได้บอกภาษาการเขียนโปรแกรมที่ใช้เพราะฉันคิดว่ามันไม่เกี่ยวข้องกับคำถาม (ฉันใช้ c ++ ตามวิธี) ฉันขอขอบคุณสำหรับคำแนะนำในการใช้ซ็อกเก็ตแบบอะซิงโครนัส คุณรู้จักหนังสือ / เว็บเพจที่แนะนำสำหรับการเรียนรู้วิธีการเหล่านี้ในเกม (เครื่องมือ) หรือไม่?
Yannick Lange

@Grimshaw: และNลูกค้าแต่ละบริการด้ายอย่างไร? หากคุณกำลังบล็อกและคุณต้องการแน่ใจว่าซ็อกเก็ตแต่ละอันได้รับการประมวลผลข้อมูลโดยไม่คำนึงถึงสถานะของซ็อกเก็ตที่เหลือคุณต้องมีหนึ่งเธรดต่อซ็อกเก็ต นั่นหรือไม่ใช้การบล็อกซ็อกเก็ต
Panda Pyjama

ความคิดเห็นของฉันเป็นรุ่นแรกของคำตอบของคุณในขณะเดียวกันคุณแก้ไขและแก้ไขมันลืมสิ่งที่ฉันพูดว่า: D
Grimshaw
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.