UDP non-blocking atau utas terpisah untuk menerima?

10

Saya membuat game multi-pemain (untuk di bawah 64 pemain). Saya sudah memutuskan untuk memiliki utas terpisah untuk loop jaringan, tetapi saya bertanya-tanya apakah akan lebih baik untuk membuat utas tambahan untuk menerima UDP atau mengatur soket penerima pada non-pemblokiran (tanpa utas tambahan).

Atau lebih baik menggunakan metode lain seperti Asynchronous Sockets? Metode yang lebih baik selalu diterima!

Yannick Lange
sumber

Jawaban:

6

Oke, pertama-tama, jika Anda memiliki sesuatu dan berfungsi, biasanya ide yang baik untuk membiarkannya seperti itu. Mengapa memperbaiki apa yang tidak rusak?

Tetapi jika Anda mengalami masalah, dan ingin menulis ulang kode jaringan Anda, saya pikir Anda memiliki empat pilihan utama:

  1. Kode pemblokiran multithreaded (apa yang Anda lakukan saat ini)
  2. Soket yang tidak menghalangi dengan pemberitahuan yang dipicu oleh level
  3. Soket yang tidak menghalangi dengan pemberitahuan perubahan kesiapan
  4. Soket asinkron

Setelah menulis banyak klien dan server multiplayer (dari peer-to-peer ke multiplayer besar-besaran), saya suka berpikir bahwa opsi 2 mengarah pada kompleksitas paling sedikit, dengan kinerja yang cukup baik, baik untuk server dan bagian klien dari permainan. Sebagai penutup kedua, saya akan menggunakan opsi 4, tetapi itu biasanya mengharuskan Anda untuk memikirkan kembali seluruh program Anda, dan saya sebagian besar menganggapnya berguna untuk server dan bukan klien.

Secara khusus, saya ingin menyarankan agar tidak memblokir soket di lingkungan multithreaded, karena hal itu biasanya mengarah ke penguncian dan fitur sinkronisasi lainnya yang tidak hanya sangat meningkatkan kompleksitas kode, tetapi juga dapat menurunkan kinerjanya, karena beberapa thread menunggu lainnya.

Tetapi di atas semua itu, sebagian besar implementasi socket (dan sebagian besar implementasi I / O pada saat itu) adalah non-blocking pada level terendah. Operasi pemblokiran hanya disediakan untuk menyederhanakan pengembangan program-program sepele. Ketika soket memblokir, CPU di utas itu benar-benar idle, jadi mengapa membangun abstraksi non-blocking atas abstraksi blocking atas tugas yang sudah tidak memblokir?

Pemrograman soket non-blocking agak menakutkan jika Anda belum mencobanya, tetapi ternyata itu cukup sederhana, dan jika Anda sudah melakukan polling input, Anda sudah memiliki pola pikir untuk melakukan soket non-blocking.

Hal pertama yang ingin Anda lakukan adalah mengatur soket ke non-blocking. Anda melakukannya dengan fcntl().

Setelah itu, sebelum Anda melakukannya send(), recv(), sendto(), recvfrom(), accept()( connect()sedikit berbeda) atau panggilan lain yang bisa memblokir benang, Anda menelepon select()pada soket. select()memberi tahu Anda apakah operasi baca atau tulis selanjutnya dapat dilakukan pada soket tanpa memblokirnya. Jika itu yang terjadi, Anda dapat dengan aman melakukan operasi yang Anda inginkan, dan soket tidak akan menghalangi.

Termasuk ini dalam sebuah game cukup sederhana. Jika Anda sudah memiliki loop game, misalnya seperti ini:

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

Anda dapat mengubahnya agar terlihat seperti ini:

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

dimana

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

dan

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

Dalam hal sumber daya, satu-satunya buku yang saya pikir harus dimiliki semua orang di rak buku mereka, bahkan jika itu satu-satunya buku yang mereka miliki, adalah " Unix Network Programming, Vol 1. " oleh almarhum Richard Stevens. Tidak masalah jika Anda melakukan pemrograman soket OS atau bahasa atau Windows lainnya. Jangan pikir Anda mengerti soket sampai Anda membaca buku ini.

Sumber lain di mana Anda dapat menemukan gambaran umum umum dari solusi yang tersedia dalam hal pemrograman banyak soket (kebanyakan relevan untuk pemrograman server) adalah halaman ini .

Piyama Panda
sumber
Saya baru memulai mesin game saya, jadi menulis ulang tidak menjadi masalah. Jawaban ini sangat berguna dan terima kasih atas rekomendasi buku. Apakah Anda juga tahu buku yang lebih spesifik tentang jaringan dalam game?
Yannick Lange
3
@YannickLange: Tidak, tapi saya tidak akan merekomendasikan Anda mencari buku-buku khusus game, karena jaringan cukup banyak genre-agnostik, dan juga tidak ada buku lain yang setingkat buku Stevens. Kudos untuk menggunakan UDP, karena menggunakan protokol berorientasi pesan adalah ide yang baik ketika menulis program berorientasi pesan (seperti kebanyakan game). Banyak orang cenderung akhirnya menulis abstraksi pesan melalui TCP, yang dengan sendirinya merupakan abstraksi berorientasi aliran yang dibangun di atas protokol berorientasi pesan (IP).
Panda Pajama
-1

Anda tidak menulis bahasa pemrograman mana yang Anda gunakan, tetapi sebagian besar bahasa menyediakan kerangka kerja asinkron yang bagus yang sering menggunakan fitur-fitur sistem operasi yang mendasarinya.

Ketika Anda menulis implementasi berulir Anda sendiri berdasarkan soket pemblokiran (ingat: ketika Anda memiliki banyak klien, Anda memerlukan utas untuk setiap soket tunggal), Anda hanya akan menemukan kembali apa yang sudah disediakan oleh kerangka soket asinkron.

Jadi saya akan merekomendasikan Anda untuk menggunakan soket asinkron.

Philipp
sumber
4
mengapa dia membutuhkan utas untuk setiap soket? Apalagi dengan soket async? Banyak server kelas berat menggunakan model "kumpulan thread" untuk memproses data soket, di mana setiap layanan thread N klien dan mereka dihasilkan atau dibunuh sesuai kebutuhan. Bayangkan saja jawaban ini membutuhkan perbaikan: D
Grimshaw
@ Grimshaw Saya pikir Anda salah mengerti jawaban saya. Saya pikir saya sekarang telah mengklarifikasi bahwa model multi-threaded akan diperlukan saat menggunakan soket pemblokiran.
Philipp
@ Pilip Saya tidak tahu bahasa pemrograman mana yang saya gunakan karena, saya pikir itu tidak benar-benar relevan dengan pertanyaan (saya menggunakan c ++ omong-omong). Saya berterima kasih atas rekomendasi Anda untuk menggunakan soket asinkron. Apakah Anda tahu buku / halaman web yang disarankan untuk mempelajari metode-metode ini dalam game (engine)?
Yannick Lange
@ Grimshaw: Dan bagaimana setiap Nklien layanan thread ? Jika Anda memblokir, dan Anda ingin memastikan setiap soket memproses datanya terlepas dari status soket yang lain, Anda harus memiliki satu utas per soket. Itu, atau tidak menggunakan soket pemblokir.
Panda Pajama
Komentar saya adalah versi pertama dari jawaban Anda, sementara itu, Anda mengedit dan memperbaikinya, lupakan apa yang saya katakan: D
Grimshaw