【问题标题】:Messages are only being sent to the client that sent them in my chat program消息仅发送给在我的聊天程序中发送消息的客户端
【发布时间】:2017-04-11 14:58:44
【问题描述】:

在我的聊天客户端/服务器程序中,我无法将消息广播到所有连接的客户端。即使我正在遍历 ArrayList,发送的消息似乎也只被发送回原始服务器。我有一个 ArrayList 的套接字和一个将读取消息作为参数并为每个套接字创建一个 PrintWriter 的方法。我认为问题与我的 Client 类有关(如果我错了,请原谅),但我无法弄清楚是什么。

客户端类:

public class H7Client extends Thread implements ActionListener{

private JTextField jtfPortName = new JTextField(20);
private JTextField jtfHostName = new JTextField(20);
private String hostName;
private int portNumber;

private JTextArea jtaChat = new JTextArea("Send a message to the client", 15,40);
private JTextArea jtaRecive = new JTextArea("WELCOME TO THE CHAT!", 15,40);
private JTextField jtfUName = new JTextField("user");



public H7Client(){
    JFrame defaultFrame = new JFrame();

    JLabel jlPortName = new JLabel("Enter The Port number");
    JLabel jlHostName = new JLabel("Enter the Host name");
    JLabel jlUName = new JLabel("Enter a username");

    JButton jbSetSocketInfo = new JButton("Confirm Port and Host Info");
    JButton jbExit = new JButton("Exit");
    JButton jbSendText = new JButton("Send");

    jbSetSocketInfo.addActionListener(this);
    jbExit.addActionListener(this);
    jbSendText.addActionListener(this);



    JPanel jpNorth = new JPanel();
    JPanel jpCenter = new JPanel();
    JPanel jpLabels = new JPanel();

    defaultFrame.add(jpNorth,BorderLayout.NORTH);
    jpNorth.add(jbSetSocketInfo,BorderLayout.EAST);
    jpNorth.add(jbSendText, BorderLayout.CENTER);
    jpNorth.add(jbExit,BorderLayout.WEST);


    defaultFrame.add(jpCenter,BorderLayout.CENTER);
    jpCenter.add(jtaChat,BorderLayout.SOUTH);
    jpCenter.add(jpLabels,BorderLayout.NORTH);

    jpLabels.setLayout(new GridLayout(2,3));
    jpLabels.add(jlHostName);
    jpLabels.add(jlPortName);
    jpLabels.add(jlUName);
    jpLabels.add(jtfHostName);
    jpLabels.add(jtfPortName);
    jpLabels.add(jtfUName);

    defaultFrame.add(jtaRecive,BorderLayout.SOUTH);

    defaultFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);


    defaultFrame.setLocationRelativeTo(null);
    defaultFrame.setSize(800,800);
    defaultFrame.setVisible(true);

}

public void setClientComms(String message){
    try{
        // open communications to the server
        Socket s = new Socket(hostName, portNumber);
        ClientThread ct = new ClientThread(s, message);
    }
    catch(IOException e){
        e.printStackTrace();
    }
}

class ClientThread extends Thread{

    public ClientThread(Socket sock, String msg) {

        try {

            // open input stream
            InputStream in = sock.getInputStream();
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(in));

            // open output stream
            OutputStream out = sock.getOutputStream();
            PrintWriter pout = new PrintWriter(
                    new OutputStreamWriter(out));


            // write something to the server
            pout.println(msg);

            // make sure it went
            pout.flush();

            // read something back from server
            String incomingMessage = br.readLine();

            // print the something to the user
            System.out.println("Message: " + msg);
            //jtaChat.setText("");
            jtaRecive.append("\n" + msg);


            // Send the terminating string to the server
            pout.println("quit");
            pout.flush();

            // close everything
            pout.close();
            br.close();
            sock.close();
        } catch (UnknownHostException uhe) {
            System.out.println("What host you speak of?");
        } catch (IOException ioe) {
            System.out.println("Bad IO?");
            ioe.printStackTrace();
        }
    }
}

public void actionPerformed(ActionEvent event) {
    if (event.getActionCommand().equals("Exit")) {
        System.exit(0);
    } else if (event.getActionCommand().equals("Send")) {
        String chatMessage = jtaChat.getText();
        jtaChat.setText("");
        setClientComms(chatMessage);


    } else if (event.getActionCommand().equals("Confirm Port and Host Info")) {
        hostName = jtfHostName.getText();
        //NEED TO ADD IN WAY TO HANDLE IP ADDRESSES
        portNumber = Integer.parseInt(jtfPortName.getText());
    }
}


public static void main(String [] args) {

    new H7Client();


}

}

服务器类:

public class H7Server{

public ArrayList<Socket> clients = new ArrayList<Socket>();

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



    ServerSocket ss = null;

    try {
        System.out.println("getLocalHost: "+ InetAddress.getLocalHost() );
        System.out.println("getByName:    "+InetAddress.getByName("localhost") );

        ss = new ServerSocket(16789);
        Socket cs = null;
        while(true){        // run forever once up
            //try{
            cs = ss.accept();               // wait for connection
            clients.add(cs);
            ThreadServer ths = new ThreadServer( cs );
            ths.start();
       } // end while
    }
    catch( BindException be ) {
        System.out.println("Server already running on this computer, stopping.");
    }
    catch( IOException ioe ) {
        System.out.println("IO Error");
        ioe.printStackTrace();
    }

} // end main

class ThreadServer extends Thread {
    Socket cs;

    public ThreadServer( Socket cs ) {
        this.cs = cs;
    }

    public void run() {

           BufferedReader br;
           String clientMsg;
           try {
               br = new BufferedReader(
                       new InputStreamReader(
                               cs.getInputStream()));


               clientMsg = br.readLine();                // from client
               System.out.println("Server read: " + clientMsg);
               while(clientMsg != null) {
                   sendMessage(clientMsg);
               }

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


    }

    public synchronized void sendMessage(String s){
        try{
            for(Socket sock: clients) {
                PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream()));

                pw.println(s);
                pw.flush();
            }
        }
        catch(IOException e){
            e.printStackTrace();
        }


    }

} // end class ThreadServer

}

【问题讨论】:

    标签: java multithreading sockets


    【解决方案1】:

    正如您所怀疑的,您的客户端类和服务器类都存在问题。您需要解决两件事。

    首先,您收到来自服务器的消息,但没有对它做任何事情。客户端在ClientThread 的构造函数的try 块中收到incomingMessage,但未使用。一种可能的解决方法是将其附加到jtaReceive 并删除另一个附加,但不要这样做。

    对于实时聊天,客户端必须至少有两个线程:一个用于发送消息,一个用于不断检查是否有任何消息要接收。也许,这就是您试图通过ClientThread 实现的目标,但不幸的是它不起作用。除了错误的代码之外,您对ClientThread 的使用会创建一个Thread,但实际上从未运行它。你需要有一个线程,在run 方法中,包含一个永远的while 循环,它不断地从服务器接收消息。一种实现是这样的:(您可以重命名为任何您喜欢的名称)

    class ReceiverThread extends Thread {
    
        private BufferedReader s;
    
        public ReceiverThread(Socket sock) throws IOException {
            s = new BufferedReader(new InputStreamReader(sock.getInputStream()));
        }
    
        @Override
        public void run() {
            while (true) 
                try {
                    String message = s.readLine();
                    // ...code to do whatever with message...
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    
    }
    

    此外,您还需要创建一个保持打开状态的套接字(可能在您确认主机和端口信息时使用该套接字创建并启动此线程)。

    服务器类的问题是困扰我们所有人的问题:臭名昭著的ConcurrentModificationException。你可以阅读它here,但基本上当你修改(添加、删除等)数据结构时会抛出这个异常,而你不应该这样做。有问题的数据结构是SocketArrayList,当您发送消息时,时间是在for-each 循环期间。基本上,当该循环运行时,while 循环创建Socket 将它们添加到ArrayList 并抛出异常。要修复,当您处于for 循环时,您需要停止修改ArrayList,可能通过删除while 循环,保持while 循环直到for 循环完成,或者甚至有一个Queuefor 循环处于活动状态时要添加套接字,但是有很多方法可以做到这一点。

    我假设您的服务器只会收到一条消息,只是为了测试,但是如果您决定要能够聊天多条消息,则必须使用用于客户端类的额外线程。

    祝你好运:)。

    注意:我建议不要将AWT thread(调用actionPerformed 的线程)用于联网的东西/任何可能需要一些时间的东西,否则您的应用程序GUI 将实际上冻结。

    【讨论】:

    • 非常感谢,这真的很有帮助。很抱歉回复晚了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-01
    • 2015-03-28
    • 2014-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多