Introducció
En aquest tema, explorarem com integrar funcionalitats de xarxa en les aplicacions DirectX. La capacitat de comunicar-se a través de la xarxa és essencial per a molts jocs i aplicacions modernes, permetent el joc multijugador, la sincronització de dades en temps real i molt més.
Objectius
- Comprendre els conceptes bàsics de la programació de xarxes.
- Aprendre a configurar una connexió de xarxa en una aplicació DirectX.
- Implementar la comunicació bàsica entre clients i servidors.
- Gestionar la sincronització de dades en temps real.
Conceptes Bàsics de Xarxes
Protocols de Xarxa
Els protocols de xarxa defineixen com es comuniquen els dispositius a través de la xarxa. Els dos protocols més comuns són:
- TCP (Transmission Control Protocol): Proporciona una connexió fiable i orientada a la connexió. És ideal per a aplicacions que requereixen la transmissió precisa de dades, com ara jocs de torns.
- UDP (User Datagram Protocol): És un protocol sense connexió i no garanteix la fiabilitat de la transmissió. És més ràpid que TCP i s'utilitza sovint en jocs en temps real on la velocitat és més important que la precisió.
Adreces IP i Ports
- Adreça IP: Identifica de manera única un dispositiu en una xarxa.
- Port: Identifica una aplicació o servei específic en un dispositiu.
Configuració de la Connexió de Xarxa
Llibreries de Xarxa
Per implementar funcionalitats de xarxa en DirectX, podem utilitzar llibreries com Winsock (Windows Sockets API). Winsock proporciona una interfície per a la programació de xarxes en Windows.
Exemple de Configuració de Winsock
A continuació, es mostra un exemple bàsic de com inicialitzar Winsock i establir una connexió TCP.
#include <winsock2.h> #include <ws2tcpip.h> #include <iostream> #pragma comment(lib, "Ws2_32.lib") int main() { WSADATA wsaData; int iResult; // Inicialitzar Winsock iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { std::cerr << "WSAStartup failed: " << iResult << std::endl; return 1; } // Crear un socket SOCKET ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (ConnectSocket == INVALID_SOCKET) { std::cerr << "Error at socket(): " << WSAGetLastError() << std::endl; WSACleanup(); return 1; } // Configurar l'adreça del servidor sockaddr_in clientService; clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = inet_addr("127.0.0.1"); clientService.sin_port = htons(27015); // Connectar al servidor iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService)); if (iResult == SOCKET_ERROR) { std::cerr << "Unable to connect to server: " << WSAGetLastError() << std::endl; closesocket(ConnectSocket); WSACleanup(); return 1; } std::cout << "Successfully connected to server!" << std::endl; // Tancar el socket closesocket(ConnectSocket); WSACleanup(); return 0; }
Explicació del Codi
- Inicialització de Winsock:
WSAStartup
inicialitza l'ús de la llibreria Winsock. - Creació del Socket:
socket
crea un nou socket per a la comunicació. - Configuració de l'Adreça del Servidor:
sockaddr_in
defineix l'adreça del servidor al qual ens connectarem. - Connexió al Servidor:
connect
intenta establir una connexió amb el servidor. - Tancament del Socket:
closesocket
tanca el socket iWSACleanup
allibera els recursos de Winsock.
Comunicació Bàsica entre Clients i Servidors
Enviar i Rebre Dades
Un cop establerta la connexió, podem enviar i rebre dades utilitzant les funcions send
i recv
.
// Enviar dades const char* sendbuf = "Hello, Server!"; iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0); if (iResult == SOCKET_ERROR) { std::cerr << "send failed: " << WSAGetLastError() << std::endl; closesocket(ConnectSocket); WSACleanup(); return 1; } // Rebre dades char recvbuf[512]; iResult = recv(ConnectSocket, recvbuf, 512, 0); if (iResult > 0) { std::cout << "Bytes received: " << iResult << std::endl; std::cout << "Data: " << recvbuf << std::endl; } else if (iResult == 0) { std::cout << "Connection closed" << std::endl; } else { std::cerr << "recv failed: " << WSAGetLastError() << std::endl; }
Explicació del Codi
- Enviar Dades:
send
envia dades al servidor. - Rebre Dades:
recv
rep dades del servidor.
Exercici Pràctic
Objectiu
Implementar una aplicació client-servidor bàsica on el client envia un missatge al servidor i el servidor respon amb un missatge de confirmació.
Instruccions
- Servidor: Escriu un programa que escolti connexions entrants i respongui amb un missatge de confirmació.
- Client: Escriu un programa que es connecti al servidor, enviï un missatge i mostri la resposta del servidor.
Solució
Servidor
#include <winsock2.h> #include <ws2tcpip.h> #include <iostream> #pragma comment(lib, "Ws2_32.lib") int main() { WSADATA wsaData; int iResult; // Inicialitzar Winsock iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { std::cerr << "WSAStartup failed: " << iResult << std::endl; return 1; } // Crear un socket per escoltar SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (ListenSocket == INVALID_SOCKET) { std::cerr << "Error at socket(): " << WSAGetLastError() << std::endl; WSACleanup(); return 1; } // Configurar l'adreça del servidor sockaddr_in serverService; serverService.sin_family = AF_INET; serverService.sin_addr.s_addr = INADDR_ANY; serverService.sin_port = htons(27015); // Lligar el socket iResult = bind(ListenSocket, (SOCKADDR*)&serverService, sizeof(serverService)); if (iResult == SOCKET_ERROR) { std::cerr << "bind failed: " << WSAGetLastError() << std::endl; closesocket(ListenSocket); WSACleanup(); return 1; } // Escoltar connexions entrants iResult = listen(ListenSocket, SOMAXCONN); if (iResult == SOCKET_ERROR) { std::cerr << "listen failed: " << WSAGetLastError() << std::endl; closesocket(ListenSocket); WSACleanup(); return 1; } std::cout << "Waiting for client to connect..." << std::endl; // Acceptar una connexió SOCKET ClientSocket = accept(ListenSocket, NULL, NULL); if (ClientSocket == INVALID_SOCKET) { std::cerr << "accept failed: " << WSAGetLastError() << std::endl; closesocket(ListenSocket); WSACleanup(); return 1; } std::cout << "Client connected!" << std::endl; // Rebre dades del client char recvbuf[512]; iResult = recv(ClientSocket, recvbuf, 512, 0); if (iResult > 0) { std::cout << "Bytes received: " << iResult << std::endl; std::cout << "Data: " << recvbuf << std::endl; // Enviar resposta al client const char* sendbuf = "Message received!"; iResult = send(ClientSocket, sendbuf, (int)strlen(sendbuf), 0); if (iResult == SOCKET_ERROR) { std::cerr << "send failed: " << WSAGetLastError() << std::endl; } } else if (iResult == 0) { std::cout << "Connection closing..." << std::endl; } else { std::cerr << "recv failed: " << WSAGetLastError() << std::endl; } // Tancar el socket closesocket(ClientSocket); closesocket(ListenSocket); WSACleanup(); return 0; }
Client
#include <winsock2.h> #include <ws2tcpip.h> #include <iostream> #pragma comment(lib, "Ws2_32.lib") int main() { WSADATA wsaData; int iResult; // Inicialitzar Winsock iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { std::cerr << "WSAStartup failed: " << iResult << std::endl; return 1; } // Crear un socket SOCKET ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (ConnectSocket == INVALID_SOCKET) { std::cerr << "Error at socket(): " << WSAGetLastError() << std::endl; WSACleanup(); return 1; } // Configurar l'adreça del servidor sockaddr_in clientService; clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = inet_addr("127.0.0.1"); clientService.sin_port = htons(27015); // Connectar al servidor iResult = connect(ConnectSocket, (SOCKADDR*)&clientService, sizeof(clientService)); if (iResult == SOCKET_ERROR) { std::cerr << "Unable to connect to server: " << WSAGetLastError() << std::endl; closesocket(ConnectSocket); WSACleanup(); return 1; } std::cout << "Successfully connected to server!" << std::endl; // Enviar dades const char* sendbuf = "Hello, Server!"; iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0); if (iResult == SOCKET_ERROR) { std::cerr << "send failed: " << WSAGetLastError() << std::endl; closesocket(ConnectSocket); WSACleanup(); return 1; } // Rebre dades char recvbuf[512]; iResult = recv(ConnectSocket, recvbuf, 512, 0); if (iResult > 0) { std::cout << "Bytes received: " << iResult << std::endl; std::cout << "Data: " << recvbuf << std::endl; } else if (iResult == 0) { std::cout << "Connection closed" << std::endl; } else { std::cerr << "recv failed: " << WSAGetLastError() << std::endl; } // Tancar el socket closesocket(ConnectSocket); WSACleanup(); return 0; }
Conclusió
En aquest tema, hem après els conceptes bàsics de la programació de xarxes i com integrar funcionalitats de xarxa en una aplicació DirectX utilitzant Winsock. Hem vist com establir una connexió TCP, enviar i rebre dades, i hem implementat una aplicació client-servidor bàsica. Aquestes habilitats són fonamentals per desenvolupar jocs i aplicacions que requereixen comunicació en temps real a través de la xarxa.
Curs de Programació DirectX
Mòdul 1: Introducció a DirectX
- Què és DirectX?
- Configuració de l'Entorn de Desenvolupament
- Comprendre l'API de DirectX
- Crear la Teva Primera Aplicació DirectX
Mòdul 2: Conceptes Bàsics de Direct3D
- Introducció a Direct3D
- Inicialitzar Direct3D
- Renderitzar un Triangle
- Gestionar el Bucle de Renderització
Mòdul 3: Treballar amb Shaders
Mòdul 4: Tècniques Avançades de Renderització
Mòdul 5: Models 3D i Animació
Mòdul 6: Optimització del Rendiment
- Perfilat i Depuració
- Optimitzar el Rendiment de la Renderització
- Gestió de Memòria
- Multifil en DirectX