• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Java - socket progarmming

Object Storage Arubacloud
+1 głos
123 wizyt
pytanie zadane 11 stycznia w Java przez Specjalny Nowicjusz (230 p.)

Mam problem z socket programming w javie. Inicializuje połączenie pomiędzy klientem i serverem (pomyślnie). Niezależnie od siebie klient i server tworzą ekran z grą (również pomyślnie) - zmiany w grze mają następować poprzez wysyłanie i odbieranie prostych komunikatów tekstowych. Chce wymieniać informacje w pętli gdzie to server zaczyna, wykonuje ruch, po wykonanym ruchu wysyła o nim informacje klientowi i rozpoczyna czekanie na wiadomość od klienta. Analogicznie klient rozpoczyna pętle od czekania na wiadomość, przetwarza zmiany (póki co w formie placeholdera printującego otrzymaną wiadomość), wykonuje ruch i wysyła o nim wiadomość. Tyle co do założeń bo problem pojawia się w linijce "update = bf.readLine();" po stronie klienta - nie czeka on na wiadomość a od razu zwraca wartość null. Dlaczego tak jest skoro readLine() jest modotą blockującą która powinna czekać na odpowiedni input. Jak to naprawić?

Server:

package network;

import view.GameWindow;
import java.net.*;
import java.io.*;
import javax.swing.SwingUtilities;

// The Server class represents a server in the client-server architecture
public class Server {
    // The port number that the server listens on
    private static final int PORT = 4999;
    // The game session that the server manages
    private boolean game = true;
    private String update;

    public Server() {
            // Connecting
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("Server started, waiting for client...");
            Socket clientSocket = serverSocket.accept();
            System.out.println("Client connected, starting game...");
            System.out.println("Connected");

            // Initialize GUI
            GameWindow gameWindow = new GameWindow();
            System.out.println("GUI Initialized");

            // Initialize input and output streams
            InputStreamReader in = new InputStreamReader(clientSocket.getInputStream());
            BufferedReader bf = new BufferedReader(in);
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);            

            while (game) {
                gameWindow.setDropEnabled(true);
                while(gameWindow.isDropEnabled()) {
                    System.out.print("");
                }
                out.println(gameWindow.getLastPlacedCardInfo());
            
                update = bf.readLine();
                System.out.println(update);
            }

        } catch (IOException e) {
            System.out.println("error");
            e.printStackTrace();
        }
    }

    // The main method that starts the server
    public static void main(String[] args) {
        Server server = new Server();
    }
}

Client:

package network;

import java.net.*;
import java.io.*;
import javax.swing.SwingUtilities;
import view.GameWindow;

// The Client class represents a client in the client-server architecture
public class Client {
    // The IP address and port number of the server
    private static final String SERVER_IP = "localhost";
    private static final int SERVER_PORT = 4999;
    private boolean game = true;
    private String update;

    public Client() {
            // Connecting
        try (Socket socket = new Socket(SERVER_IP, SERVER_PORT)) {
            System.out.println("Connected to server, starting game...");
            System.out.println("Connected");

            // Initialize GUI
            GameWindow gameWindow = new GameWindow();
            System.out.println("GUI Initialized");

            // Initialize input and output streams
            InputStreamReader in = new InputStreamReader(socket.getInputStream());
            BufferedReader bf = new BufferedReader(in);
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

            while (game) {
                update = bf.readLine();
                System.out.println(update);
            
                gameWindow.setDropEnabled(true);
                System.out.println(gameWindow.getLastPlacedCardInfo());
            
                out.println(gameWindow.getLastPlacedCardInfo());
            }

            out.close();
			in.close();
			socket.close();

        } catch (IOException e) {
            System.out.println("error");
            e.printStackTrace();
        }
    }

    // The main method that starts the client
    public static void main(String[] args) {
        Client client = new Client();
    }
}

 

komentarz 11 stycznia przez Oscar Nałogowiec (29,320 p.)

Taka ogólna uwaga: jak występują problemy z komunikacją sieciową można użyć kilku narzędzi. Po pierwsze nie uruchamiaj dwóch programów naraz, najpierw uruchom jedną stronę, zastępując drugą odpowiednim narzędziem. Pod linuxem masz netcat - uruchamiane poleceniem nc. Może robić na klienta i serwera. Jeśli to nie pomoże to przydany jest też sniffer - "podglądacz pakietów" - tcpdump. wireshark itp.

Jak bawiłem się socketami w javie napotkałem problem polegający na konieczności "wypchnięcia" wysyłanych danych. Protokoły sieciowe starają się oszczędzić pasmo i wysyłają największe możliwe porcje danych buforując je. By wymusić wysłanie wołaj metodę flush() na strumieniu wyjściowym skojarzonym z socketem.

komentarz 11 stycznia przez Specjalny Nowicjusz (230 p.)
edycja 11 stycznia przez Specjalny

@Oscar,  dzięki za rade, pobawie się kiedyś z netcatem. Niestety aktualnie nie mam pod ręką komputera z linuxem.

A co do drugiej części to w printWriter mam pole autoflush ustawione na true, więc myślałem że nie musze dodatkowo flushować. Ale tak czy siak dodanie dodatkowego flusha po wysłaniu wiadomości nie pomaga, bo update = bf.readLine(); nadal wraca null zupełnie jakby strumień został zamknięty

komentarz 11 stycznia przez Oscar Nałogowiec (29,320 p.)
Uruchomiłem ten kod u siebie, oczywiście po wyrzuceniu GameWindow i wysyłaniu stałych tekstów i działa. Jeśli jest problem z utrzymaniem połączenia to sniffer bardziej pomoże, a i polecenie netstat (jest też w Windzie) może sprawdzić czy połączenie faktycznie zniknęło.
komentarz 11 stycznia przez Wiciorny Ekspert (270,190 p.)
też uważam, że przez to iż programy lecą na innych procesach w ogóle, to w tym wypadku nie ma gwarancji tego, że w ogóle IN/OUT są synchronizowane.
I realnie przy wolno działającym połączeniu po prostu nie ma synchronizacji i faktycznie buffor jest pusty, bo został wyflushowany wcześniej.
A wątki odczytują dane z konsoli bazując w ogóle na innym procesie
komentarz 12 stycznia przez Specjalny Nowicjusz (230 p.)

@Oscar, @Wiciorny błędem było moje niedopatrzenie bo faktycznie server i client uruchomione osobno działają poprawnie (czego wcześniej nie testowałem (jestem debilem)). Ja natomiast, będąc pewnym że nie ma tam błędu próbowałem uruchomić je w ten sposób
 

public class Main {
    private static final String SERVER_IP = "localhost";
    private static final int SERVER_PORT = 4444;

public static void main(String[] args) {

    try {
        Socket socket = new Socket(SERVER_IP, SERVER_PORT);
        System.out.println("Connected to server, starting as client...");
        Client client = new Client();
    } catch (IOException e) {
        System.out.println("No active server, starting as server...");
        Server server = new Server();
    }
    }
}

 

Nadal jednak nie rozumiem co jest złego w startowaniu ich w ten sposób. 


Co do samego rozwiązania - jestem świadomy, że da się to zrobić lepiej i bezpieczniej ale mam cel zrobić to w javie której szczerze nie lubię. Aplikacja jest na własny wąski użytek więc jak na razie taka implementacja mi pasuje.

1 odpowiedź

0 głosów
odpowiedź 13 stycznia przez Wiciorny Ekspert (270,190 p.)
public class Main {
    private static final String SERVER_IP = "localhost";
    private static final int SERVER_PORT = 4444;
 
public static void main(String[] args) {
 
    try {
        Socket socket = new Socket(SERVER_IP, SERVER_PORT);
        System.out.println("Connected to server, starting as client...");
        Client client = new Client();
    } catch (IOException e) {
        System.out.println("No active server, starting as server...");
        Server server = new Server();
    }
    }
}



Program startujesz w jednym procesie, w kodzie, który dostarczyłeś, wszystko jest uruchamiane w jednym wątku głównym (tzw. "main thread"). Jeśli obiekt klienta (Client) i serwera (Server) są uruchamiane w tym samym wątku, zazwyczaj może, ale nie musi,  to prowadzić do problemów z synchronizacją, zwłaszcza jeśli są one związane z operacjami wejścia/wyjścia (I/O), takimi jak operacje na socketach,

Druga sprawa to fakt, że nie masz gwarancji tutaj  że klient (Client) bedzie dzialal poprawnie, skoro jest uruchamiany bez oczekiwania na połączenie z serwerem. W twoim kodzie, klient jest inicjalizowany bez sprawdzania, czy połączenie z serwerem zostało ustanowione pomyślnie.


W przypadku aplikacji sieciowej zazwyczaj zaleca się korzystanie z wielowątkowości, aby uniknąć blokowania się operacji I/O.

Sprawdź zachowanie w taki sposó:

 

public class Main {
    private static final String SERVER_IP = "localhost";
    private static final int SERVER_PORT = 4444;

    public static void main(String[] args) {
        Thread serverThread = new Thread(() -> {
            try {
                System.out.println("Starting as server...");
                Server server = new Server();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        Thread clientThread = new Thread(() -> {
            try {
                Socket socket = new Socket(SERVER_IP, SERVER_PORT);
                System.out.println("Connected to server, starting as client...");
                Client client = new Client();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        serverThread.start();
        clientThread.start();
    }
}

chociaż to też może prowadzić do błędnych odczytów, gdy nie ma odpowiedniej synchronizacji zasobu, ewentualnie wątki nie pracuja na danych bezposrednio z pamięci tylko cache 

Podobne pytania

0 głosów
1 odpowiedź 244 wizyt
pytanie zadane 13 października 2023 w JavaScript przez JaaO Początkujący (490 p.)
0 głosów
0 odpowiedzi 375 wizyt
pytanie zadane 3 maja 2023 w JavaScript przez kordix Gaduła (3,910 p.)
0 głosów
1 odpowiedź 445 wizyt
pytanie zadane 15 stycznia 2019 w Java przez ILikeJava Obywatel (1,230 p.)

92,579 zapytań

141,432 odpowiedzi

319,664 komentarzy

61,964 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...