Мережевий код

Написання правильного і коректного мережевого коду - завдання досить проста. Треба тільки трохи підучити і «спробувати» Winsock. Але необхідно так само писати і швидкий код, інакше задоволення від гри по мережі (а, точніше, по модему) особливого не отримаєш. Досить згадати оригінальний Quake. Якщо хто пробував грати в цю іграшку по модему, думаю, не один десяток нелітературних виразів відмочив :).

В даному випадку йдеться про з'єднання клієнт-сервер, коли клієнт посилає серверу дані про дії користувача, а сервер, обробивши всіх клієнтів, розсилає їм дані про стан гри.

У цьому способі зв'язку є свої переваги і недоліки.

Переваги - утруднення cheating-а, клієнту не обов'язково мати навороченную конфігурацію комп'ютера, адже йому треба тільки прийняти серверні дані про те, що змінилося в ігровому світі і що стОит зобразити на екрані. До того ж, немає спеціальної синхронізації для того, щоб клієнти мали ідентичні стану ігрового світу (Наприклад, Doom не був побудований за технологією клієнт-сервер, кожен комп'ютер розраховував сам різні параметри гри на основі генератора псевдовипадкових чисел, і доводилося все це синхронізувати).

Зате, є дуже суттєві недоліки, які практично непомітні при грі по локальній мережі, але дуже сильно проявляються при грі по модему. Найголовніший недолік - Ping, точніше не він сам;), а його високе значення. Ping - це час, за яке пакет від клієнта досягає сервера, плюс час відповіді сервера, плюс час проходження пакета від сервера до клієнта. У локальній мережі його значення зазвичай лежить в межах від 5 до 30 мсек. При грі ж по модему, значення ping може бути від 50 (ну це просто ідеальні умови) і до безкінечності. Якщо припустити, що модемний зв'язок буде прийнятною, значення Ping буде залежати тільки від "кривизни" мережевого коду і від стану ігрового світу (зміни різних параметрів ігрових елементів).

Таким чином, оптимізація мережевого коду, а точніше оптимізація розміру переданих даних - дуже серйозна і складна задача.

Але, перш за все, звичайно ж, ми розглянемо, як все це взагалі працює, а вже потім перейдемо до оптимізації.

Отже, маємо: з'єднання за допомогою Winsock (можна, звичайно, використовувати DirectPlay, але тільки, чим менше у нас посередників, тим швидше все буде працювати, чи не так?), Протокол UDP (TCP з'єднання в нашому випадку використовувати можна тільки в локальній мережі , тому що одна з переваг протоколу TCP - 100% -а доставка пакетів - в даному випадку перетворюється на величезний недолік, адже якщо пакет не доставлений успішно, його будуть посилати ще раз, в стан гри вже змінилося!).

Приклади побудовані з використанням відкритого вихідного коду Quake2. (Як відступ від теми, хочу сказати особиста думка про це коді - код написаний досить "прямо" і оптимально, безглючная і взагалі, легко читається). Було б також непогано, якщо Ви будете періодично заглядати в документацію до winsock. Корисно спочатку прочитати теорію, а потім розглянути це на практиці.

Після успішного виконання цих дій, бібліотека winsock готова Вас обслужити :).

Тепер напишемо метод ініціалізації сокетов.

Сокетів може бути не більше 2-ух. Один для сервера, другий - для клієнта (для виділеного сервера він не потрібен).

Можна завести 2 змінні, server_socket і client_socket, а можна, як зроблено в Quake2, створити масив з 2-ух елементів і заголовки описати enumerator:

А в основному файлі опишемо масив

static int ip_sockets [] =; // IP server client sockets

Напишемо загальний метод ініціалізації сокетов, який буде викликатися при найпершій ініціалізації клієнта або сервера.

У методі використовується інший більш конкретизований метод ініціалізації сокетов по протоколу IP. Так само можна зробити ініціалізацію для протоколу IPX.

Ну ось ми і написали методи ініціалізації мережі. Залишилося зовсім небагато - написати методи для посилки / прийому пакетів і методи роботи з локальним клієнтом.

Для цих методів необхідно опис деяких структур.

Метод посилки пакета:

Метод прийому пакета:

Опис класу buffer:

Цей клас призначений для більш зручної роботи з масивом даних різних типів.

Залишилося тільки розібратися зі способом передачі даних між локальними клієнтом і сервером.

Зробимо імітацію роботи winsock. У winsock ми можемо послати кілька пакетів іншого комп'ютера, поки він їх буде приймати, тобто, треба створити деяку чергу для пакетів. У Quake2 це реалізовано досить простим і оригінальним способом - через "закільцований" масив. Можна це зробити з використанням STL + клас buffer. Але як я вже сказав раніше, код Quake2 написаний досить оптимально, до того ж, нам не потрібні методи класу buffer, нам потрібен просто масив і змінна його розміру, тому для прикладу використовуємо оригінальний код.

Ну а тепер самі методи:

Ця була перша частина статті. Тут був описаний низькорівневий драйвер для роботи з мережею. Безпосередньо він практично не використовується движком. У другій частині буде описано клас, з яким безпосередньо буде працювати движок. У ньому буде реалізовано декілька цікавих речей, найголовніша з яких - можливість 100% доставки "важливих" даних (reliable data). Але це буде пізніше.

Схожі статті