Java套接字 - 使用线程将所有客户端输入发送到所有其他客户端

时间:2014-10-09 12:48:56

标签: java multithreading sockets

程序的过程如下:

在服务器中:

-Open new window with GUI
-Start a serversocket on port 1500
-Await a client to connect
-Accept the client
-Wait to recieve their username
-Send their client number
-Start a new Thread for the client
-Store playernames, positions and the threads

在线程中:

while loop:

-Wait to Receive Object from client, where the Object is a Vector3f (float,float,float)
-Set the ArrayList of Vector3f, where the index is clientnumber, to equal the Object recieved from the client
-Send the client the arraylist

在客户端:

-Join server at specified ip
-Send username
-Recieve clientnumber

while loop:

-Send Vector3f
-recieve arraylist of Vector3f

但是......我认为这会奏效。我认为客户端会正确连接,每个客户端都会收到其他每个玩家位置的arraylist(Vector3f)。它似乎适用于连接的前4个客户端,直到:

java.util.ConcurrentModificationException

不确定如何解决这个问题......

以下是所有代码:

客户端:

package main;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;

public class Client {
    private Socket socket = null;
    private ObjectInputStream inStream = null;
    private ObjectOutputStream outStream = null;
    String name = "bob";
    int port;
    String ip;
    Vector3f v3f = new Vector3f(0, 0, 0);
    int clientnumber = 0;
    int numberofclients = 0;
    ArrayList<Vector3f> positions = new ArrayList<Vector3f>();

    public Client(int port, String ip, String name) {
        this.port = port;
        this.ip = ip;
        this.name = name;
        startClient();
    }

    public void startClient() {
        try {
            socket = new Socket(ip, port);
            System.out.println("Connected");
            outStream = new ObjectOutputStream(socket.getOutputStream());//send name
            outStream.writeObject(name);

            inStream = new ObjectInputStream(socket.getInputStream());//recieve client number
            clientnumber = (int) inStream.readObject();

            while (true) {
                v3f = new Vector3f(Math.random()*10+1, Math.random()*10+1, Math.random()*10+1);
                outStream = new ObjectOutputStream(socket.getOutputStream());//send position
                outStream.writeObject(v3f);

                inStream = new ObjectInputStream(socket.getInputStream());//recieve position(s)
                positions = (ArrayList<Vector3f>) inStream.readObject();

                numberofclients = positions.size();
            }

        } catch (Exception e) {
            System.err.println("Client Error: " + e.getMessage());
            System.err.println("Localized: " + e.getLocalizedMessage());
            System.err.println("Stack Trace: " + e.getStackTrace());
        }
    }

    public static void main(String[] args) {
        new Client(1500, "localHost", "bob");
    }
}

服务器:

package main;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class Server {

    static JFrame f = new JFrame();
    static JPanel p = new JPanel();
    static JLabel info = new JLabel();
    private ServerSocket serverSocket = null;
    private Socket socket = null;
    private boolean connection = false;
    private boolean serverisrunning = true;
    private int port;
    static JTextArea jt = new JTextArea();
    public static boolean showtimestamp = false;
    private JScrollPane sc = new JScrollPane(jt,
            JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    String timeStamp = new SimpleDateFormat("HH:mm:ss").format(Calendar
            .getInstance().getTime());
    String defaulttext = System.getProperty("user.name") + " / " + getIp()
            + " > ";
    String error = "Error - ";
    String badcom = " Is Not Recognised As A Valid Command";
    String baduse = "Incorrect Use";
    String tftext = "";
    ObjectInputStream inStream = null;
    ObjectOutputStream outStream = null;
    JTextField tf = new JTextField("/");
    static ArrayList<EchoThread> clients = new ArrayList<EchoThread>();
    static ArrayList<String> clientnames = new ArrayList<String>();
    static ArrayList<Vector3f> clientpos = new ArrayList<Vector3f>();
    Dimension ss = Toolkit.getDefaultToolkit().getScreenSize();

    public Server(int port) {
        this.port = port;

        f.setTitle("Server");
        f.setBounds(ss.width - 750, 50, 701, 350);
        f.setResizable(false);
        f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        f.setAlwaysOnTop(true);
        f.setLayout(null);
        f.add(p);
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                if (connection) {
                    try {
                        socket.close();
                        serverSocket.close();
                    } catch (IOException e) {
                    }
                }
                System.exit(0);
            }
        });

        f.add(sc);
        sc.setBounds(151, 0, 560, 300);
        jt.setBackground(Color.BLACK);
        jt.setForeground(Color.WHITE);
        jt.setLineWrap(true);
        jt.setEditable(false);
        append("Hello World");

        f.add(info);
        info.setOpaque(true);
        info.setBounds(0, 0, 150, 19);
        info.setBackground(Color.RED);
        info.setText("Clients Connected: " + clients.size());

        f.add(tf);
        tf.setBounds(151, 298, 544, 23);
        tf.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                tftext = tf.getText();
                RunCommand(tftext);
                tf.setText("/");
            }
        });
        tf.setBackground(Color.BLACK);
        tf.setForeground(Color.WHITE);

        f.getContentPane().setBackground(Color.BLACK);
        f.setVisible(true);
        startServer();
    }

    protected void RunCommand(String txt) {
        serverMessage("'" + txt.replace("/", "") + "'\n");
        if (txt.contains("/")) {
            if (txt.equals("/")) {
                return;
            }
            String t = txt.toLowerCase().replace("/", "");
            if (t.contains("setshowtime")) {
                if (t.endsWith(" true")) {
                    showtimestamp = true;
                    append("ShowTimeStamp - true");
                } else if (t.endsWith(" false")) {
                    showtimestamp = false;
                    append("ShowTimeStamp - false");
                } else {
                    append(error + baduse + " '" + txt + "' "
                            + "\nCorrect Use: SetShowTime true/false");
                }
            } else if (t.contains("say ")) {
                message(t.replace("say ", ""));
            } else if (t.contains("hello")) {
                append("Hi " + System.getProperty("user.name"));
            } else if (t.contains("stop")) {
                serverMessage("Server Starting\nInitialising socket connections:");
                serverisrunning = false;
            } else if (t.contains("kick")) {
                int indexOf = t.indexOf('c');
                String name = t.substring(indexOf + 2).trim();
                serverMessage("Kicking Player " + name);
                for (int i = 0; i < clientnames.size(); i++) {
                    if (clientnames.get(i).toLowerCase().contains(name)) {
                        append("\n'" + name + "' was found: "
                                + clientnames.get(i));
                        p.remove(clients.get(i).clientinfo);
                        p.revalidate();
                        f.validate();
                        f.repaint();
                        clients.get(i).clientinfo.setVisible(false);
                        clients.remove(i);
                        clientnames.remove(i);
                        info.setText("Clients Connected: " + clients.size());
                    }
                }
            } else {// if none of the other commands
                append(error + "'" + t + "'" + badcom);
            }
        } else {
            // dunno
        }
    }

    public void sendToAll(Object message){
        for(EchoThread client : clients)
            client.write(clientpos.get(clients.indexOf(client)));
    }

    public static void removeClient(int i) {
        p.remove(clients.get(i).clientinfo);
        p.revalidate();
        f.validate();
        f.repaint();
        clients.get(i).clientinfo.setVisible(false);
        clients.remove(i);
        clientnames.remove(i);
        clientpos.remove(i);
        info.setText("Clients Connected: " + clients.size());
    }

    public void message(String msg) {
        timeStamp = new SimpleDateFormat("HH:mm-ss").format(Calendar
                .getInstance().getTime());
        jt.append(" [" + timeStamp + "] " + msg);
        jt.append("\n" + defaulttext);
        jt.setCaretPosition(jt.getDocument().getLength());
    }

    public void append(String text) {
        timeStamp = new SimpleDateFormat("HH:mm-ss").format(Calendar
                .getInstance().getTime());
        if (showtimestamp) {
            jt.append(" [" + timeStamp + "] ");
        }
        jt.append(text);
        jt.append("\n" + defaulttext);
        jt.setCaretPosition(jt.getDocument().getLength());
    }

    public static void serverMessage(String text) {
        jt.append(text);
        jt.setCaretPosition(jt.getDocument().getLength());
    }

    public void startServer() {
        try {
            serverSocket = new ServerSocket(port);
            append("\nPort: " + port + "\nLocal IP address: "
                    + InetAddress.getLocalHost().getHostAddress()
                    + "\nExternal IP address: " + getIp()
                    + "\nType 'stop' to stop listening for clients...\n");

            while (serverisrunning) {
                if (clients.size() < 15) {
                    socket = serverSocket.accept();

                    inStream = new ObjectInputStream(socket.getInputStream());// recieve username
                    String name = (String) inStream.readObject();
                    clientnames.add(name);
                    System.out.println(name + " Connected");
                    outStream = new ObjectOutputStream(socket.getOutputStream());// send client number
                    outStream.writeObject(clients.size() + 1);

                    clientpos.add(new Vector3f());

                    clients.add(new EchoThread(socket));
                    clients.get(clients.size() - 1).start();
                    clients.get(clients.size() - 1).clientindex = (clients
                            .size());
                    clients.get(clients.size() - 1).clientname = clientnames
                            .get((clients.size() - 1));


                    connection = true;

                    timeStamp = new SimpleDateFormat("HH:mm-ss")
                            .format(Calendar.getInstance().getTime());
                    append("Client Connected: " + clients.size() + "\n"
                            + clients + "\n" + "Client IP: "
                            + socket.getRemoteSocketAddress() + "\n\n###### "
                            + name + " Connected " + timeStamp + " ######\n");

                    for (int i = 0; i < clients.size(); i++) {
                        if (!clients.get(i).isAlive()) {
                            clients.remove(i);
                            clientnames.remove(i);
                        }
                    }
                }

                info.setText("Clients Connected: " + clients.size());
            }

        } catch (Exception e) {
        }
    }

    public static String getIp() {
        URL whatismyip;
        try {
            whatismyip = new URL("http://checkip.amazonaws.com");

            BufferedReader in = null;
            in = new BufferedReader(new InputStreamReader(
                    whatismyip.openStream()));
            String ip = in.readLine();

            if (in != null) {
                in.close();
            }

            return ip;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        new Server(1500);
    }
}

Vector3f:

package main;

import java.io.Serializable;
import javax.swing.JTextArea;

public class Vector3f implements Serializable {

    public double x;
    public double y;
    public double z;

    public Vector3f(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Vector3f(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Vector3f(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Vector3f() {
        this.x = 0;
        this.y = 0;
        this.z = 0;
    }

    public void OutputToConsole(String additionaltext) {
        System.out.println(additionaltext + " x:" + x + " y:" + y + " z:" + z);
    }

    public void appendToTextArea(JTextArea ta, String additionaltext) {
        ta.append(additionaltext + " x:" + x + " y:" + y + " z:" + z);
    }

}

EchoThread:

package main;

import java.awt.Color;
import java.awt.Font;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

import javax.swing.JFrame;
import javax.swing.JLabel;

public class EchoThread extends Thread {
    volatile boolean finished = false;
    protected Socket socket;
    boolean isrunning = false;
    boolean edited = false;
    ObjectInputStream inStream = null;
    ObjectOutputStream outStream = null;
    Vector3f v3f = new Vector3f(0, 0, 0);
    JLabel clientinfo = new JLabel();
    JFrame f;
    int clientindex = 0;
    String clientname = "";
    int numberofclients = Server.clients.size();

    public EchoThread(Socket clientSocket) {
        this.socket = clientSocket;

        f = Server.f;
        f.add(clientinfo);
        clientinfo.setOpaque(true);
        clientinfo.setBounds(0, 20 + (20 * Server.clients.size()), 150, 19);
        clientinfo.setBackground(Color.GREEN);

    }

    public void run() {
        while (!finished) {
            numberofclients = Server.clients.size();
            try {
                inStream = new ObjectInputStream(socket.getInputStream());
                v3f = (Vector3f) inStream.readObject();

                clientinfo.setText("[" + clientindex + "] " + clientname + ": "
                        + (int) v3f.x + "," + (int) v3f.y + "," + (int) v3f.z);
                FontToFit(clientinfo);

                Server.clientpos.set(clientindex-1, v3f);

                outStream = new ObjectOutputStream(socket.getOutputStream());
                outStream.writeObject(Server.clientpos);


            } catch (Exception e) {
            System.out.println(e);
                try {
                    //Server.removeClient(clientindex - 1);
                    end();
                    Server.serverMessage(clientname + " Disconnected");
                    socket.shutdownOutput();
                    inStream.close();
                    outStream.close();
                    socket.shutdownInput();
                    socket.close();
                } catch (IOException e1) {
                }
                return;
            }
        }
    }

    public void write(Object obj) {
        try{
            outStream.writeObject(obj);
        }
        catch(IOException e){ e.printStackTrace(); }
    }

    public void end() {
        finished = true;
    }

    public void FontToFit(JLabel label) {
        Font labelFont = label.getFont();
        String labelText = label.getText();

        double stringWidth = label.getFontMetrics(labelFont).stringWidth(
                labelText);
        double componentWidth = label.getWidth();

        // Find out how much the font can grow in width.
        double widthRatio = (double) componentWidth / (double) stringWidth;

        double newFontSize = (int) (labelFont.getSize() * widthRatio);
        double componentHeight = label.getHeight();

        // Pick a new font size so it will not be larger than the height of
        // label.
        double fontSizeToUse = Math.min(newFontSize, componentHeight);

        // Set the label's font size to the newly determined size.
        label.setFont(new Font(labelFont.getName(), Font.PLAIN,
                (int) fontSizeToUse - 1));
    }
}

1 个答案:

答案 0 :(得分:1)

我没有查看你的代码示例,因为它有很多,但是你在标题中提到的问题答案很简短。

问:如何使用线程将一个客户端的输入广播到所有其他客户端?

答:你没有。

每个客户端使用一个线程来接收来自每个客户端的输入,当客户端C向您发送内容时,让服务客户端C的一个线程向所有客户端广播该消息其他客户端套接字