简体   繁体   中英

Java TCP Echo Server - Broadcast

I have a simple echo server, and I want when a connected user types anything to the server, all other clients and that client will get a the message + " | MOD".

It wont send to all clients now but it should and I just don't know what's wrong in my code, so now it will just send the message + " | MOD" to the client who sent the message but not to all others also as it should.

I just don't get it, I have a loop that goes through all clients, but it still won't send to all.

SERVER:

package com.murplyx.server;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class Server {
    public static ServerSocket server;
    public static ArrayList<Socket> clients = new ArrayList<Socket>();

    public static void broadcast(String message) {
        try {
            for (Socket socket : clients) {
                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

                out.println(message);
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String args[]) {
        try {
            server = new ServerSocket(9000);

            while (true) {
                clients.add(server.accept());

                for (Socket socket : clients) {
                    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                    String line = in.readLine();

                    if (line != null) {
                        broadcast(line + " | MOD");
                    }
                }
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

CLIENT:

package com.murplyx.client;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Client {
    public static void main(String args[]) {
        try {
            while (true) {
                Socket socket = new Socket("localhost", 9000);

                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

                BufferedReader input = new BufferedReader(new InputStreamReader(System.in));

                out.println(input.readLine());

                System.out.println(in.readLine());

                socket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Please help.

Thanks alot.

One of the issues you have is that each client will repeatedly do read stdin, write socket, read socket, write stdout, ... ad infinitum.

When you broadcast all other clients are still typically sat in the read stdin phase, so they don't know that there's stuff waiting to be read on the socket. They're still waiting for the user to enter something.

One of the simplest options is to start two threads in each client - one just handles read stdin, write socket, ... and the other handles read socket, write stdout .

[Another (potentially more sophisticated) option us to use Java NIO to poll both the socket and stdin for available input at the same time ].

A second issue is that you're blocking in the accept call in the server, and then reading from each socket in turn . You might accept in one thread, and have another thread per client read from just the client, and rebroadcast to the others. NIO can also be a good option here - you can poll for reads any any client.

I'm not exactly sure how ArrayLists play with sockets, so I definitely would go back to using a normal array for it (see the edited code here Java EchoTCPServer - Send to all clients )

Some things I see that I think can to be fixed:

On the Client:

-Stop closing the socket in the While loop. Close it OUTSIDE the while loop (When the client is done with the server). Also, declare the socket outside the Loop.

NOTE ON THIS : When a client makes a socket to connect to the server, it is automatically given a device port, so two different devices will never have the same IP connected to the server. A TCP connection consists of 2 ports, server socket and client socket, and the sockets are denoted by [deviceip:port,serverip:port] (iirc).

-Also, on the client you don't need to declare a new reader everytime you move through the while loop. Put that all outside. The only thing inside the while loop should be your readline + print statements.

-readLine is a blocking method. (just in case you don't know what that means, it means that readLine will make your program be stuck there until the it actually reads a line. To bypass this, you can use an if statement combined with the .ready() function. The ready function checks to see if there is anything to be "read in", so if there's no input it wont be stuck on "readLine".

On the Server:

-Like i said earlier, I'd change back to using a normal Array.

-Your server will still get stuck on .accept(). As such, you will never be able to read input from the clients except once after each connection. You can use a thread to listen instead, and it will still work.

eg: (this code goes with the code that's in the link i attached (also your question), put it before the while loop of your server)

// create a tcp listener thread to deal with listening to clients
Thread listenerThread = new Thread() {
    public void run() {
        String clientSentence;

        while (true) {
            //loop through each connected socket    
            for (int i = 0; i <= intLastSocket; i++) {
                Socket z = clientSocket[i];
                //make sure the socket is not null or closed (can't do anything 
                //with closed or null sockets           
                if ((z != null) && (!z.isClosed())) {
                    try {
                        // Deal with TCP input here
                        BufferedReader input = new BufferedReader(new 
                           InputStreamReader(z.getInputStream()));
                        // read in a line but only if there is one
                        if (input.ready()) {
                            clientSentence = input.readLine();
                        }
                    } catch (IOException x) {
                        printTCP("IOException caught when reading in: "
                                + x.toString());
                    }
                    if (clientSentence != null) {
                        System.out.println("Received from client: "
                                + clientSentence);
                        //send this message to the client
                        outputStream[i].println(clientSentence + " | MOD");
                    }

                    // clear the input
                    clientSentence = null;
                }
            }
        }
    }
};
listenerThread.start();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM