Saya mengirim sedikit data ke dan dari server, untuk game yang saya buat.
Saat ini saya mengirim data lokasi seperti ini:
sendToClient((("UID:" + cl.uid +";x:" + cl.x)));
sendToClient((("UID:" + cl.uid +";y:" + cl.y)));
sendToClient((("UID:" + cl.uid +";z:" + cl.z)));
Jelas itu mengirimkan masing-masing nilai X, Y, dan Z.
Akankah lebih efisien mengirim data seperti ini?
sendToClient((("UID:" + cl.uid +"|" + cl.x + "|" + cl.y + "|" + cl.z)));
networking
joehot200
sumber
sumber
Jawaban:
Segmen TCP memiliki banyak overhead. Ketika Anda mengirim pesan 10 byte dengan satu paket TCP, Anda sebenarnya mengirim:
menghasilkan 42 byte traffic untuk mengangkut 10 byte data. Jadi, Anda hanya menggunakan kurang dari 25% dari bandwidth yang tersedia. Dan itu belum memperhitungkan overhead yang dikonsumsi protokol tingkat rendah seperti Ethernet atau PPPoE (tetapi ini sulit untuk diperkirakan karena ada begitu banyak alternatif).
Selain itu, banyak paket kecil memberikan tekanan lebih besar pada router, firewall, sakelar, dan peralatan infrastruktur jaringan lainnya, jadi ketika Anda, penyedia layanan dan pengguna Anda tidak berinvestasi pada perangkat keras berkualitas tinggi, ini dapat berubah menjadi hambatan lain.
Untuk alasan itu Anda harus mencoba mengirim semua data yang Anda miliki sekaligus dalam satu segmen TCP.
Mengenai penanganan kehilangan paket : Saat Anda menggunakan TCP, Anda tidak perlu khawatir tentang hal itu. Protokol itu sendiri memastikan bahwa setiap paket yang hilang dikirim ulang dan paket diproses secara berurutan, sehingga Anda dapat mengasumsikan bahwa semua paket yang Anda kirim akan tiba di sisi lain, dan mereka akan tiba dalam urutan yang Anda kirim. Harga untuk ini adalah ketika ada paket yang hilang, pemain Anda akan mengalami kelambatan yang cukup besar, karena satu paket yang dijatuhkan akan menghentikan seluruh aliran data sampai diminta kembali dan diterima.
Ketika ini merupakan masalah, Anda selalu dapat menggunakan UDP. Tapi kemudian Anda harus mencari solusi sendiri untuk hilang dan out-of-order pesan (setidaknya jaminan bahwa pesan yang tidak tiba, tiba lengkap dan tidak rusak).
sumber
Satu besar (masuk akal) lebih baik.
Seperti yang Anda katakan, packet loss adalah alasan utama. Paket umumnya dikirim dalam bingkai dengan ukuran tetap, jadi lebih baik mengambil satu bingkai dengan pesan besar daripada 10 bingkai dengan 10 kecil.
Namun dengan TCP standar, ini bukan masalah, kecuali jika Anda menonaktifkannya. (Ini disebut algoritme Nagle , dan untuk gim Anda harus menonaktifkannya.) TCP akan menunggu batas waktu yang tetap atau hingga paketnya "penuh". Di mana "penuh" adalah beberapa angka ajaib, sebagian ditentukan oleh ukuran bingkai.
sumber
recv()
panggilan untuk setiapsend()
panggilan, yang merupakan hal yang dicari kebanyakan orang. Namun, menggunakan protokol yang menjamin hal ini, seperti halnya UDP. "Ketika semua yang Anda miliki adalah TCP, semuanya tampak seperti aliran"Semua jawaban sebelumnya salah. Dalam praktiknya, tidak masalah apakah Anda mengeluarkan satu
send()
panggilan lama atau beberapasend()
panggilan kecil .Seperti yang dinyatakan Phillip, segmen TCP memiliki beberapa overhead, tetapi sebagai pemrogram aplikasi, Anda tidak memiliki kendali atas bagaimana segmen dihasilkan. Secara sederhana:
OS sepenuhnya bebas untuk buffer semua data Anda dan mengirimkannya dalam satu segmen, atau mengambil yang panjang dan memecahnya menjadi beberapa segmen kecil.
Ini memiliki beberapa implikasi, tetapi yang paling penting adalah:
Alasan di balik ini adalah TCP adalah protokol stream . TCP memperlakukan data Anda sebagai aliran byte yang panjang, dan sama sekali tidak memiliki konsep "paket". Dengan
send()
Anda menambahkan byte ke aliran itu, dan denganrecv()
Anda mendapatkan byte dari sisi lain. TCP akan secara agresif menyangga dan membagi data Anda di mana pun ia mau untuk memastikan data Anda sampai ke pihak lain secepat mungkin.Jika Anda ingin mengirim dan menerima "paket" dengan TCP, Anda harus mengimplementasikan penanda mulai paket, penanda panjang dan sebagainya. Bagaimana kalau menggunakan protokol berorientasi pesan seperti UDP saja? UDP menjamin satu
send()
panggilan diterjemahkan ke satu datagram terkirim dan ke saturecv()
panggilan!sumber
recv()
, Anda perlu membuat buffering sendiri untuk mengimbanginya. Saya akan memeringkatnya dalam kesulitan yang sama dengan menerapkan keandalan di atas UDP.while(1) { uint16_t size; read(sock, &size, sizeof(size)); size = ntoh(size); char message[size]; read(sock, buffer, size); handleMessage(message); }
(menghilangkan penanganan kesalahan dan membaca sebagian untuk singkatnya tetapi tidak banyak berubah). Melakukan ini adalahselect
tidak menambah kerumitan dan jika Anda menggunakan TCP Anda mungkin perlu buffer sebagian pesan. Menerapkan keandalan yang kuat di atas UDP jauh lebih rumit dari itu.Banyak paket kecil baik-baik saja. Bahkan, jika Anda khawatir tentang overhead TCP, cukup masukkan
bufferstream
yang mengumpulkan hingga 1500 karakter (atau apa pun MTU TCP Anda, paling baik untuk memintanya secara dinamis), dan atasi masalah di satu tempat. Melakukan hal itu membuat Anda kekurangan ~ 40 byte untuk setiap paket tambahan yang seharusnya Anda buat.Yang mengatakan, masih lebih baik untuk mengirim lebih sedikit data, dan membangun objek yang lebih besar membantu di sana. Tentu saja lebih kecil untuk dikirim
"UID:10|1|2|3
daripada mengirimUID:10;x:1UID:10;y:2UID:10;z:3
. Bahkan, juga pada titik ini Anda tidak harus menciptakan kembali roda, gunakan perpustakaan seperti protobuf yang dapat mengurangi data seperti itu ke string 10 byte atau kurang.Satu-satunya hal yang tidak boleh Anda lupakan adalah memasukkan
Flush
perintah pada stream Anda di lokasi yang relevan, karena begitu Anda berhenti menambahkan data ke stream Anda, itu mungkin menunggu tanpa batas sebelum mengirim apa pun. Benar-benar bermasalah ketika klien Anda menunggu data itu, dan server Anda tidak akan mengirim sesuatu yang baru sampai klien mengirim perintah berikutnya.Paket loss adalah sesuatu yang bisa Anda pengaruhi di sini, secara marginal. Setiap byte yang Anda kirim berpotensi rusak, dan TCP akan secara otomatis meminta pengiriman ulang. Paket yang lebih kecil berarti peluang yang lebih rendah untuk setiap paket yang rusak, tetapi karena mereka bertambah pada biaya overhead, Anda mengirim lebih banyak byte, meningkatkan kemungkinan paket yang hilang lebih banyak. Ketika sebuah paket hilang, TCP akan melakukan buffer semua data yang berhasil sampai paket yang hilang dikirim ulang dan diterima. Ini menghasilkan penundaan besar (ping). Sementara total kerugian dalam bandwidth karena paket loss bisa diabaikan, ping yang lebih tinggi tidak diinginkan untuk gim.
Intinya: Kirim sesedikit mungkin data, kirim paket besar, dan jangan menulis metode tingkat rendah Anda sendiri untuk melakukannya, tetapi mengandalkan perpustakaan terkenal dan metode seperti
bufferstream
dan protobuf untuk menangani angkat berat.sumber
bufferstream
itu sepele, itu sebabnya saya menyebutnya metode. Anda masih ingin menanganinya di satu tempat, dan tidak mengintegrasikan logika buffer Anda dengan kode pesan Anda. Adapun Obyek Serialisasi, saya sangat ragu Anda mendapatkan sesuatu yang lebih baik daripada ribuan orang-jam yang dimasukkan ke sana, bahkan jika Anda DO mencoba, saya sangat menyarankan Anda membandingkan solusi Anda dengan implementasi yang dikenal.Walaupun saya adalah orang baru dalam pemrograman jaringan, saya ingin berbagi pengalaman terbatas saya dengan menambahkan beberapa poin:
Mengenai pengukuran, metrik yang harus dipertimbangkan adalah:
Seperti disebutkan, jika Anda mengetahui bahwa Anda tidak terbatas dalam arti tertentu dan dapat menggunakan UDP, lakukan itu. Ada beberapa implementasi berbasis UDP di luar sana, jadi Anda tidak perlu menemukan kembali roda atau bekerja melawan pengalaman bertahun-tahun dan pengalaman yang terbukti. Implementasi yang layak disebutkan adalah:
Kesimpulan: karena implementasi UDP dapat mengungguli (dengan faktor 3x) yang TCP, masuk akal untuk mempertimbangkannya, setelah Anda mengidentifikasi skenario Anda ramah UDP. Diperingatkan! Menerapkan tumpukan TCP lengkap di atas UDP selalu merupakan ide yang buruk.
sumber