[SFML 2.X] TCP/IP - Jak napisać prostego klienta
Ostatnio zmodyfikowano 2025-06-04 00:13
tBane Temat założony przez niniejszego użytkownika |
[SFML 2.X] TCP/IP - Jak napisać prostego klienta » 2024-11-18 23:35:14 Witam. Uczę się komunikacji TCP/IP. Napisałem prosty server, który nasłuchuje na porcie 53000 i oczekuje połączenia z klientem. Potrzebuję napisać prostego klienta, który połączy się z serverem oraz wyśle mu tekst "Hello!". Pomożecie ? Poza tym mam jeszcze pytanie dlaczego port akurat 53000? Jakie wartości może przyjmować port itp. Kod mojego "servera": #include <iostream> #include <SFML/System.hpp> #include <SFML/Graphics.hpp> #include <SFML/Network.hpp> using namespace std;
sf::RenderWindow * window; sf::TcpListener listener;
int main() { window = new sf::RenderWindow( sf::VideoMode( 720, 480 ), "SERVER" ); window->setFramerateLimit( 5 ); sf::IpAddress localIP = sf::IpAddress::getLocalAddress(); sf::IpAddress publicIP = sf::IpAddress::getPublicAddress(); cout << "Adres IP lokalny (LAN): " << localIP << "\n"; cout << "Adres IP publiczny: " << publicIP << "\n"; if( listener.listen( 53000 ) != sf::Socket::Done ) { cout << "Nie udało się nasłuchiwać na porcie 53000\n"; return - 1; } cout << "Server listening on 53000\n"; bool waiting_for_client = true; while( window->isOpen() ) { while( waiting_for_client == true ) { sf::TcpSocket client; if( listener.accept( client ) != sf::Socket::Done ) { cout << "not connecting\n"; return - 1; } cout << "client " << client.getRemoteAddress() << " conecting\n"; waiting_for_client = false; } sf::Event event; while( window->pollEvent( event ) ) { if( event.type == sf::Event::Closed ) { window->close(); } } window->clear(); window->display(); } return 0; }
|
|
pekfos |
» 2024-11-19 16:38:17 Kodu tyle co nic. Skoro to napisałeś, to co cię powstrzymuje przed napisaniem klienta? Robisz connect() i wysyłasz dane... Poza tym mam jeszcze pytanie dlaczego port akurat 53000? Jakie wartości może przyjmować port itp. Bo tak się napisało. Może być dowolna liczba 16-bitowa bez znaku, różna od zera, która nie jest obecnie w użyciu w systemie. Jak masz wybrać na stałe, najlepiej w zakresie 1024-32767. Poniżej możesz potrzebować uprawnień administratora, a powyżej są zakresy losowo przydzielanych portów. |
|
tBane Temat założony przez niniejszego użytkownika |
» 2024-11-19 18:18:42 Ok. Czyli jak rozumiem dobrą praktyką podczas Tworzenia listenera będzie sprawdzenie portów przed utworzeniem servera ? Klienta napisałem i działa :-) Server#include <iostream> #include <SFML/System.hpp> #include <SFML/Graphics.hpp> #include <SFML/Network.hpp> using namespace std;
sf::RenderWindow * window; sf::TcpListener listener; sf::TcpSocket client1;
int main() { window = new sf::RenderWindow( sf::VideoMode( 720, 480 ), "SERVER" ); window->setFramerateLimit( 5 ); sf::IpAddress localIP = sf::IpAddress::getLocalAddress(); sf::IpAddress publicIP = sf::IpAddress::getPublicAddress(); cout << "Adres IP lokalny (LAN): " << localIP << "\n"; cout << "Adres IP publiczny: " << publicIP << "\n"; if( listener.listen( 53000 ) != sf::Socket::Done ) { cout << "Nie udało się nasłuchiwać na porcie 53000\n"; return - 1; } cout << "Server listening on 53000\n"; bool waiting_for_client = true; while( window->isOpen() ) { while( waiting_for_client == true ) { if( listener.accept( client1 ) != sf::Socket::Done ) { cout << "not connecting\n"; return - 1; } cout << "client " << client1.getRemoteAddress() << " conecting\n"; waiting_for_client = false; } bool server_run = true; while( server_run ) { char buffer[ 100 ]; std::size_t received; if( client1.receive( buffer, sizeof( buffer ), received ) != sf::Socket::Done ) { cout << "Error receiving message\n"; } else { cout << "Received message: " << buffer << "\n"; } } sf::Event event; while( window->pollEvent( event ) ) { if( event.type == sf::Event::Closed ) { window->close(); } cout << "nasłuchuję ... \n"; } window->clear(); window->display(); } return 0; }
Klientp#include <iostream> #include <SFML/System.hpp> #include <SFML/Network.hpp> using namespace std;
int main() { sf::TcpSocket socket; sf::IpAddress serverIP = "192.168.97.73"; unsigned short port = 53000; if( socket.connect( serverIP, port ) != sf::Socket::Done ) { cout << "Nie udało się połączyć z serwerem\n"; return - 1; } cout << "Połączono z serwerem: " << serverIP << ":" << port << "\n"; std::string message = "Hello!"; int i = 0; while( true ) { if( socket.send( message.c_str(), message.length() + 1 ) != sf::Socket::Done ) { cout << "Nie udało się wysłać wiadomości\n"; return - 1; } else cout << "send - \"Hello!\\n"; sf::sleep( sf::seconds( 1 ) ); } socket.disconnect(); return 0; }
|
|
termistor |
» 2025-06-04 00:13:34 Witaj.
Odpowiedź na pytanie o porty: Port 53000 nie ma specjalnego znaczenia. Wybór jest dowolny, ale powinien unikać: 1. **Portów < 1024** – wymagają uprawnień administratora. 2. **Portów > 65535** – przekraczają 16-bitowy zakres. Dobrym standardem jest wybór wartości z zakresu [1024; 65535), najlepiej z podprzedziału [49152; 65535), który jest rezerwowany dla dynamicznych portów.
Sprawdzanie dostępności portu: Tak, to dobry zwyczaj. Twoja metoda: ```cpp if (listener.listen(53000) != sf::Socket::Done) { /* error */ } ``` jest poprawna. Dodatkowo można sprawdzić, czy port nie jest zajęty wcześniej, np. poprzez próbkę portów w pętli (patrz niżej).
---
Komentarze do kodu serwera: 1. **Infinite loop w `server_run`**: Twój kod: ```cpp while (server_run) { client1.receive(...); } ``` blokuje główną pętlę programu, co uniemożliwi przetwarzanie zdarzeń okna (`pollEvent`). Rozwiązanie: - Użyj osobnego wątku do odbierania danych. - Albo przetwarzaj dane tylko przy aktualizacji (np. co klatkę).
2. **Bufor `char buffer[100]`**: Działa, ale lepiej dynamicznie alokować pamięć lub użyć `std::string` z `resize()`.
3. **Zmienna `client1`**: Serwer akceptuje połączenie raz, ale nie obsłuży wielu klientów. Dla wielu klientów lepiej przechowywać połączenia w kontenerze (np. `std::vector<sf::TcpSocket*>`).
---
Komentarze do kodu klienta: 1. **Infinite loop `while (true)`**: Kod: ```cpp while (true) { socket.send(...); sf::sleep(sf::seconds(1)); } ``` nigdy nie zakończy się. Rozwiązanie: - Dodaj warunek wyjścia (np. `break` po pierwszej wiadomości). - Albo zmień na `for (int i = 0; i < 1; ++i) { ... }`.
2. **Wysyłanie `message.length() + 1`**: Poprawne, ponieważ `c_str()` zwraca NULL-terminated string.
---
Udogodnienia w kodzie: 1. **Automatyczne sprawdzanie portów**: Przykład: ```cpp unsigned short findAvailablePort() { for (int port = 50000; port <= 60000; ++port) { sf::TcpListener listener; if (listener.listen(port) == sf::Socket::Done) { return port; } } return 0; // brak portów } ```
2. **Zamknięcie gniazda w `finally`**: Użyj `std::shared_ptr` z destruktorem lub `RAII` do automatycznego zamykania połączeń.
---
Podsumowanie: - Kod działa, ale wymaga poprawek w strukturze (np. wątki, obsługa wielu klientów). - Port 53000 jest akceptowalny. - Unikaj infinite loopów w głównym wątku.
Jeśli chcesz, mogę podać gotowy przykład serwera obsługującego wiele klientów.
Pozdrawiam. |
|
« 1 » |