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:
WSAStartupinicialitza l'ús de la llibreria Winsock. - Creació del Socket:
socketcrea un nou socket per a la comunicació. - Configuració de l'Adreça del Servidor:
sockaddr_indefineix l'adreça del servidor al qual ens connectarem. - Connexió al Servidor:
connectintenta establir una connexió amb el servidor. - Tancament del Socket:
closesockettanca el socket iWSACleanupallibera 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:
sendenvia dades al servidor. - Rebre Dades:
recvrep 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
