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 - сокетът вече не може да получава или изпраща.