WinSock2 за игри

И така, вече има две налични версии на WinSock: 1.1 и 2. Предлагам да използвате втората версия. Какво ще правим след това? Ще напишем клиент-сървър версия на Rock-Paper-Scissors. Сървърът ще бъде многонишково конзолно приложение, клиентът ще бъде написан на DirectX.

Като начало ще ви покажа как е организиран WinSock, ще обясня различните начини, по които можете да програмирате сокети, и ще опиша функциите, които се използват за това. След като се справим с WinSock, ще го използваме, за да напишем играчката, която споменах по-горе.

За да използвате WinSock, трябва да включите подходящия заглавен файл и да добавитеws2_32.libкъм вашия проект. Сега всичко е готово за програмиране под WinSock. Но как работи и откъде да започнете?

Има няколко начина за програмиране на гнезда. Можете да използвате основни функции на UNIX/Berkley или функции, специфични за Microsoft Windows, или да използвате обектно-ориентираната MFC версия на сокети. В началото исках да използвам OO версията на сокетите, защото Мисля, че класовете правят API по-лесно смилаем. Но това не са само класове, това е MFC. „Направено възможно най-твърдо“ е мотото им. Не, MFC е страхотен, но фактът, че трябва да създадете Win32 приложение и да обработвате съобщенията на windows socket, изпратени до вашата програма, е разочароващ, особено в случай на сървър. Защо трябва да направим сървъра като Win32 приложение? Безсмислено е. За да направим нещата по-лесни за себе си, ще използваме най-основните UNIX/Berkley сървърни функции.

Описание:WSADataсе използва, когато зареждате и инициализиратеws2_32.dll. Този тип се използва като върната стойност на функциятаWSAStartup(). Използвайте го, за да определите версията на WinSock на вашия компютър.

Описание:Този тип данни се използва за съхраняване на дескриптор на сокет. Тези дескриптори се използват за идентифициране на сокета. Всъщност SOCKET е просто unsigned int.

Е, нека започнем с програмирането

Първо трябва да заредите ws2_32.dll:

Вероятно имате въпрос - какво означава 0x0202? Това означава версия 2.2. Ако се изисква версия 1.1, този номер трябва да се промени на 0x0101. WSAStartup() попълва променлива от тип WSADATA и зарежда динамичната библиотека на сокета. WSACleanup(), съответно, го разтоварва.

Създайте сокет

Присвоете желания порт към гнездото (свържете порта на гнездото и гнездото):

Слушане на свързания порт (порт за слушане)

Тук приехме връзка от клиент, който иска да се присъедини към играта. Има нещо друго интересно в редаlisten(SOCKET s, int backlog). Какво еназад?Backlogе броят клиенти, които могат да се свържат, докато използваме сокета, т.е. тези клиенти ще трябва да изчакат, докато сървърът договори връзка с други клиенти. Например, ако посочите 5 като изоставане и 7 души се опитат да се присъединят, тогава последните 2 ще получат съобщение за грешка и ще бъдат принудени да се свържат по-късно. Обикновено този параметър варира от 2 до 10, в зависимост от максималния капацитет на сървъра.

Опит за свързване с гнездо (опитайте & свържете гнездо)

По принцип това е всичко, което е необходимо, за да поискате връзка. Променливатаtargetобозначава сокета, към който се опитваме да се свържем (сървър). Функцията connect() изисква сокет (от страна на клиента),описание на гнездото за прикачен файл (от страната на сървъра) и променливия размер на това описание. Тази функция просто изпраща заявка за връзка и изчаква отговор от сървъра, като докладва всички възникнали грешки.

Приемане на връзка )

Като цяло всичко е ясно тук. Броят на клиентите не трябва да се указва от израза MAX_CLIENTS. Използва се тук само за подобряване на яснотата и четливостта на този код.number_of_clientsе променлива, която съдържа броя на свързаните клиенти. client[MAX_CLIENTS] е масив от тип SOCKET, който се използва за съхраняване на дескрипторите на сокета на свързани клиенти. client_sock[MAX_CLIENTS] - масив от типsockaddr, съдържащ информация за типа на връзката, номер на порт и т.н. Обикновено не ни е необходим наистина, въпреки че някои функции изискват описание на връзката като параметър. Основният цикъл просто чака заявка за връзка, след което я приема и стартира нишка, за да говори с клиента.

Изпращане на данни (писане или изпращане)

Вторият параметър на функцията send() е char FAR *buf, който е указател към буфера за данни, който искаме да изпратим. Третият параметър е размерът на буфера за изпращане. Последният параметър е за задаване на различни флагове. Няма да го използваме и го оставяме като нула.

Получаване на данни (четене или получаване)

recv()е почти катоsend(), с изключение на това, че не изпращаме данни, а получаваме.

Тук просто преобразуваме низа const char *Host в u_long addr номер, който от своя страна може да се използва за свързване към друг компютър.

Затворете гнездозатворете гнездо)

да азЗнам. Това може да изглежда глупаво. Извикването на две функции и дори проверката дали са постъпили данни за затваряне на сокета е странно, но такъв е животът. Функциятаshutdown(SOCKET s, int how) забранява определен атрибут на socket s. Ето възможните опции за променливатаhow: · SD_SEND - сокетът вече не може да изпраща данни · SD_RECEIVE - сокетът вече не може да получава данни · SD_BOTH - сокетът вече не може да получава или изпраща.