===== Instalacja bibliotek deweloperskich w laboratoriach ===== Aby zainstalować potrzebne pakiety na __Linuksie SK prog__, należy wykonać:
su
apt update
apt install libsfml-dev
Aby zainstalować potrzebne pakiety na __Linuksie lokalnym__, należy wykonać:
sudo zypper install -y sfml2-devel
====== Dokumentacja SFML ======
[[https://www.sfml-dev.org/tutorials/2.6/#network-module|Zbiór samouczków SFML]]
[[https://www.sfml-dev.org/documentation/2.6.1/group__network.php|API klas sieciowych]]
====== Ogólne informacje ======
===== SFML =====
[[https://sfml-dev.org|SFML]] jest prostym zestawem bibliotek do rysowania
okienek, opakowywania okna dla OpenGL, obsługi dźwięku i obsługi sieci.
\\
"Prostym" należy rozumieć zarówno jako "łatwym do nauki" jak i "mającym
funkcjonalność ograniczoną do wybranych, podstawowych rzeczy".
===== Wątki =====
SFML oferuje własne wątki, zamki, …, ale wg. oficjalnej dokumentacji te klasy
ustępują bibliotece standardowej C+%%%%+:
c++ prog.cpp -o prog -lsfml-graphics -lsfml-network
Dla systemu automatyzacji budowania CMake należy do pliku ''CMakeLists.txt'' dodać:
find_package(SFML 2.5 COMPONENTS system network REQUIRED)
target_link_libraries(prog … sfml-system sfml-network … )
SFML __nie__ ustawia zmiennych zawierających listę modułów do linkowania, trzeba
je podać ręcznie.
Przykład kompletnego pliku ''CMakeLists.txt'':
cmake_minimum_required(VERSION 3.12)
project(myProg)
set(CMAKE_CXX_STANDARD 20)
find_package(SFML 2.5 COMPONENTS graphics system network REQUIRED)
add_executable(myProg main.cpp window.h window.cpp)
target_link_libraries(myProg sfml-graphics sfml-network)
====== Moduł obsługi sieci ======
Klasy do obsługi sieci są dostępne po dodaniu do kodu dyrektywy:
#include
===== Adresy IP =====
**Uwaga: SFML obsługuje tylko IPv4**
Adres IP jest reprezentowany przez klasę ''[[https://www.sfml-dev.org/documentation/2.6.1/classsf_1_1IpAddress.php#details|sf::IpAddress]]''
\\
Ta klasa ma konstruktor przyjmujący ''std::string'' / ''char*'' akceptujący
zapisany jako tekst adres IP **lub nazwę domenową**.
\\
Wersja rozwojowa SFML zamiast konstruktora używa metody statycznej ''[[https://github.com/SFML/SFML/blob/master/include/SFML/Network/IpAddress.hpp#L270|sf::IpAddress::resolve]]''
===== Klasy gniazd =====
[[https://www.sfml-dev.org/documentation/2.6.1/classsf_1_1TcpListener.php|TcpListener]] - serwer TCP:
* ''listen'' - rozpoczyna nasłuchiwanie; przyjmuje przynajmniej numer portu
* ''accept'' - przyjmuje następne połączenie
* ''close'' - zamyka gniazdo
[[https://www.sfml-dev.org/documentation/2.6.1/classsf_1_1TcpSocket.php|TcpSocket]] - klient TCP:
* ''connect'' - nawiązuje połączenie; przyjmuje przynajmniej adres IP i numer portu
* ''send'' - wysyła dane
* ''receive'' - odbiera dane
* ''disconnect'' - rozłącza i zamyka gniazdo
[[https://www.sfml-dev.org/documentation/2.6.1/classsf_1_1UdpSocket.php|UdpSocket]] - gniazdo UDP:
* ''bind'' - ustala lokalny adres; przyjmuje przynajmniej numer portu
* ''send'' - wysyła dane
* ''receive'' - odbiera dane
**Uwaga:** klasy oferowane przez SFML nie mają konstruktorów kopiujących (//copy constuctor//) __ani przesuwających (//move constructor//)__.
==== Tryb nieblokujący ====
Wszystkie klasy udostępniają metodę ''setBlocking'' pozwalającą przestawić gniazdo w tryb nieblokujący.
==== Konwencje ====
Operacje sieciowe zwracają wartość z wyliczenia ''[[https://www.sfml-dev.org/documentation/2.6.1/classsf_1_1Socket.php#a51bf0fd51057b98a10fbb866246176dc|sf::Socket::Status]]'':
* ''Done'' jeżeli wszystko się powiodło,
* ''NotReady'' lub ''Partial'' dla odpowiednich wyników działania gniazd w trybie nieblokującym((Choć czasami mimo odebrania części danych funkcje odbierające zwracają ''NotReady'')),
* ''Disconnected'' jeśli gniazdo nie jest bądź przestało być połączone((Szczegóły w [[https://github.com/SFML/SFML/blob/2.6.x/src/SFML/Network/Unix/SocketImpl.cpp#L89|implementacji]] biblioteki)),
* ''Error'' jeśli wystąpił błąd (inny niż raportowany jako ''Disconnected'').
**Uwaga:** operacje takie jak ''accept'', ''send'', ''receive'' też zwracają ''sf::Socket::Status''.
\\
Odpowiednio nowe połączenie, ilość wysłanych / odebranych bajtów jest wpisywana do dodatkowego
argumentu przekazywanego przez referencję, np:
char buffer[1024];
size_t receivedBytes;
if (sock.receive(buffer, 1024, receivedBytes) == sf::Socket::Done) {
sf::String receivedBufferAsString(std::string(buffer, receivedBytes));
(…)
std::unordered_map clients;
if (servSock.accept(clients[clientIdSequencer]) == sf::Socket::Done) {
int clientId = clientIdSequencer++;
clients[clientId].send(helloPacket);
(…)
enum MessageType : sf::Uint8 { Foo = 1, Bar, Baz };
struct Message {
Message(MessageType type) : type(type){};
virtual ~Message() {}
const MessageType type;
};
struct FooMessage : public Message {
FooMessage() : Message(MessageType::Foo){};
sf::String text;
sf::Uint32 number;
};
(…)
// wysłanie wiadomości
FooMessage m;
sf::Packet packet;
packet << m.type << m.text << m.number;
socket.send(packet);
(…)
// odebranie wiadomości
sf::Packet packet;
socket.receive(packet); // (pominięto obsługę błędów)
sf::Uint8 type;
packet >> type;
switch (type) {
case MessageType::Foo: {
FooMessage m;
packet >> m.text >> m.number;
(…)
sf::Packet &operator<<(sf::Packet &packet, const FooMessage &msg) {
packet << msg.type << msg.text << msg.number;
return packet;
}
sf::Packet &operator>>(sf::Packet &packet, FooMessage &msg) {
packet >> msg.text >> msg.number;
return packet;
}
(…)
// wysłanie wiadomości
FooMessage m;
sf::Packet packet;
packet << m;
socket.send(packet);
(…)
// odebranie wiadomości
sf::Packet packet;
socket.receive(packet); // (pominięto obsługę błędów)
sf::Uint8 type;
packet >> type;
switch (type) {
case MessageType::Foo: {
FooMessage m;
packet >> m;
(…)
#include
#include
int main(int, char **argv) {
sf::TcpListener srvSock;
std::list clients;
srvSock.listen(atoi(argv[1]));
sf::SocketSelector selector;
selector.add(srvSock);
while (true) {
selector.wait();
if (selector.isReady(srvSock)) {
clients.emplace_back();
srvSock.accept(clients.back());
selector.add(clients.back());
}
decltype(clients)::iterator curr, next = clients.begin();
while ((curr = next++) != clients.end()) {
if (!selector.isReady(*curr))
continue;
char data[1024];
size_t recvCnt;
if (curr->receive(data, 1024, recvCnt) != sf::Socket::Done) {
selector.remove(*curr);
clients.erase(curr);
continue;
}
for (auto &c : clients)
if (&c != &(*curr))
c.send(data, recvCnt);
}
}
}