Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML 2.X] TCP/IP - Jak napisać prostego klienta

Ostatnio zmodyfikowano 2025-06-04 00:13
Autor Wiadomość
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":

C/C++
#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;
}
P-181922
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.
P-181927
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
C/C++
#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 ) {
           
           
// Próba odbioru wiadomości
           
char buffer[ 100 ]; // Bufor do przechowywania wiadomości
           
std::size_t received; // Ilość odebranych bajtów
           
           
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
C/C++
#include <iostream>
#include <SFML/System.hpp>
#include <SFML/Network.hpp>
using namespace std;

int main() {
   
// Tworzymy obiekt socketu
   
sf::TcpSocket socket;
   
   
// Łączymy się z serwerem (adres IP serwera i port 53000)
   
sf::IpAddress serverIP = "192.168.97.73"; // Adres IP lokalnego hosta (możesz zmienić na IP serwera)
   
unsigned short port = 53000;
   
   
// Próba połączenia z serwerem
   
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";
   
   
// Wiadomość do wysłania
   
std::string message = "Hello!";
   
   
int i = 0;
   
while( true ) {
       
// Wysyłamy wiadomość do serwera
       
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 ) );
   
}
   
   
// Zamknięcie połączenia z serwerem
   
socket.disconnect();
   
   
return 0;
}
P-181930
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.
P-182456
« 1 »
  Strona 1 z 1