在我的聊天客户端/服务器程序中,我无法将消息广播到所有连接的客户端。发送的消息似乎只是发送回原始服务器,即使我正在迭代ArrayList。我有一个套接字的ArrayList和一个方法,它将读取消息作为参数接收,并为每个套接字创建一个PrintWriter。我认为这个问题与我的客户课程有关(对不起,如果我错了),但我还没弄清楚是什么。
客户类:
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
}
答案 0 :(得分:0)
您怀疑的客户端类和服务器类都存在问题。您需要解决两件事。
首先,您从服务器收到消息,但没有对其执行任何操作。客户端在incomingMessage
的构造函数的try块中收到ClientThread
但未使用。可能的解决方法是将其附加到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,但是当您不应该修改(添加,删除等)数据结构时,基本上会抛出此异常。有问题的数据结构是ArrayList
的{{1}},并且当您发送消息时,when在Socket
循环期间。基本上,当该循环正在运行时,创建for-each
的{{1}}循环将它们添加到while
并抛出异常。要修复,您需要在Socket
循环中停止修改ArrayList
,可能是通过删除while循环,保持while循环直到ArrayList
循环结束,甚至当for
循环处于活动状态时,要添加for
个套接字,但有很多方法可以执行此操作。
我假设您的服务器只接收一条消息,只是为了测试,但如果您决定要能够聊多条消息,则必须使用您用于客户端类的额外线程。
祝你好运:)。
注意:我建议不要使用Queue
(调用for
的线程)来处理网络内容/可能需要一些时间的任何事情,否则您的应用程序GUI将实际冻结。