我想设计一个简单的S/C 聊天工具。其中,客户端和服务端界面是这样的:
基本思路如下:当一个客户端通过Socket连上服务端之后,会向服务端发送登录的用户名等信息。服务端获取这些信息并保存起来。与此同时,服务端会将所有已经连接上服务器的客户端的用户名信息通过Socket发送给每一个客户端,并在客户端的在线好友列表中显示出来。
附上代码:
package com.iheima.chat;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class ClientInterface extends JPanel
{
private static final long serialVersionUID = 1L;
private JFrame client;
private JTextField name;
private JTextField address;
private JTextField port;
private JButton login;
private JButton reset;
private ChatClient chatClient;
public ClientInterface()
{
client=new JFrame("客户端");
client.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
client.setSize(300,200);
client.setAlwaysOnTop(true);
client.setResizable(false);
JLabel administrator=new JLabel("用户名");
name=new JTextField(20);
name.setText("PriscillaChan");
JLabel portNumber=new JLabel("端口号");
port=new JTextField(20);
port.setText("5000");
JLabel hostAddress=new JLabel("IP地址");
address=new JTextField(20);
address.setText("localhost");
login=new JButton("登录");
login.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
loginValidate(client,port.getText());
}
});
reset=new JButton("重置");
reset.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
name.setText("");
address.setText("");
port.setText("");
name.requestFocus();//用户名文本框重新获得焦点
}
});
JPanel subPanel1=new JPanel();
subPanel1.add(administrator);
subPanel1.add(name);
JPanel subPanel2=new JPanel();
subPanel2.add(portNumber);
subPanel2.add(port);
JPanel subPanel3=new JPanel();
subPanel3.add(hostAddress);
subPanel3.add(address);
JPanel subPanel4=new JPanel();
subPanel4.add(login);
subPanel4.add(reset);
JPanel subPanel5=new JPanel();
subPanel5.setLayout(new GridLayout(0,1));
subPanel5.add(subPanel1);
subPanel5.add(subPanel2);
subPanel5.add(subPanel3);
subPanel5.add(subPanel4);
JPanel panel1=new JPanel();
panel1.setLayout(new BorderLayout());
panel1.add(BorderLayout.NORTH,subPanel5);
add(panel1);
this.setBackground(Color.CYAN);
client.getContentPane().add(this);
client.setVisible(true);
}
public String getName()
{
return name.getText();
}
public void loginValidate(Component co,String port)
{
if(Util.isRangeCorrect(co,port))
{
try
{
Socket socket = new Socket("localhost",Integer.parseInt(port));
if(socket.isBound())
{
login.setEnabled(false);
reset.setEnabled(false);
client.setVisible(false);
chatClient = new ChatClient();
new userLoginThread(this,socket,chatClient).start();
}
}
catch (UnknownHostException e)
{
System.err.println("UnknownHostException:"+e);
}
catch (IOException e)
{
System.err.println("IOException:"+e);
}
}
}
public static void main(String[] args)
{
new ClientInterface();
}
}
---------------------------------------------------------------
客户端登录服务器后,马上启用一个线程,用于与服务器进行交互。
代码如下:
package com.iheima.chat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class userLoginThread extends Thread
{
private ClientInterface client;
private Socket socket;
private ChatClient chatClient;
private InputStream is;
private OutputStream os;
public userLoginThread(ClientInterface client,Socket socket,ChatClient chatClient)
{
this.client=client;
this.socket = socket;
this.chatClient = chatClient;
try
{
this.is = socket.getInputStream();
this.os = socket.getOutputStream();
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void run()
{
try
{
String s = Util.getLoginInfo(client.getName(),socket.getLocalPort());
os.write(s.getBytes());
while(true)
{
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String str = "";
StringBuffer sb = new StringBuffer();
while(null != (str = br.readLine()))
{
sb.append(str);
}
this.chatClient.getJTextArea1().setText(sb.toString());
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
相应地,服务端的代码如下:
package com.iheima.chat;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.HashMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
public class ServerInterface extends JPanel
{
private static final long serialVersionUID = 1L;
private static JFrame server;
private JLabel serverState;
private JTextArea usersList;
private JScrollPane pane;
private JButton turnOn;
private JLabel inputPort;
private JTextField port;
private static HashMap<String, Integer> map = new HashMap<String, Integer>();
private StringBuffer users = new StringBuffer();
public ServerInterface()
{
server = new JFrame("服务器端");
server.setAlwaysOnTop(true);
server.setSize(300, 420);
server.setResizable(false);
inputPort = new JLabel("端口号:", SwingConstants.CENTER);
port = new JTextField(15);
port.setText("5000");
serverState = new JLabel("未连接!", SwingConstants.CENTER);
serverState.setBackground(Color.BLACK);
serverState.setForeground(Color.RED);
turnOn = new JButton("开启连接");
turnOn.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
try
{
linkTo();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
});
JPanel panel1 = new JPanel();
panel1.add(inputPort);
panel1.add(port);
JPanel panel2 = new JPanel();
panel2.add(serverState);
panel2.add(turnOn);
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(BorderLayout.NORTH, panel1);
panel.add(BorderLayout.SOUTH, panel2);
usersList = new JTextArea(16, 20);
usersList.setForeground(Color.RED);
usersList.setEditable(false);
pane = new JScrollPane(usersList);
add(panel);
add(pane);
}
public StringBuffer getUsers()
{
return users;
}
public JTextArea getUsersList()
{
return usersList;
}
public JLabel getServerState()
{
return serverState;
}
public JButton getTurnOn()
{
return turnOn;
}
public HashMap<String, Integer> getMap()
{
return map;
}
public void linkTo() throws IOException
{
new ServerConnection(this,Integer.parseInt(port.getText())).start();
}
public static void main(String[] args) throws Exception
{
ServerInterface si = new ServerInterface();
si.setBackground(Color.CYAN);
server.getContentPane().add(si);
server.setVisible(true);
server.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
if (JOptionPane.showConfirmDialog(server,
new String("确实要退出吗?"), "退出",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.INFORMATION_MESSAGE) == JOptionPane.OK_OPTION);
{
System.exit(0);
}
}
});
}
}
----------------------------------------------------------
服务端启动后,会有一个主线程,当某个客户端与之连接之后,服务端会在主线程下马上再启动一个用于与该客户端进行交互的线程。
代码如下:
package com.iheima.chat;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JOptionPane;
public class ServerConnection extends Thread
{
private ServerInterface server;
private ServerSocket serverSocket;
public ServerConnection(ServerInterface server, int port)
{
try
{
this.server = server;
this.serverSocket = new ServerSocket(port);
this.server.getServerState().setText("已连接!");
this.server.getTurnOn().setEnabled(false);
}
catch (IOException e)
{
e.printStackTrace();
JOptionPane.showMessageDialog(this.server, "服务器错误!", "警告",
JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void run()
{
while (true)
{
try
{
Socket socket = serverSocket.accept();
new GetListThread(this.server, socket).start();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
------------------------------------------------
package com.iheima.chat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Map;
import java.util.Set;
public class GetListThread extends Thread
{
private ServerInterface serverInterface;
private Socket socket;
private InputStream is;
private OutputStream os;
public GetListThread(ServerInterface serverInterface,Socket socket)
{
this.serverInterface=serverInterface;
this.socket=socket;
try
{
is = socket.getInputStream();
os = socket.getOutputStream();
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void run()
{
while(true)
{
try
{
is = socket.getInputStream();
byte[] buf = new byte[200];
int length = is.read(buf,0,buf.length);
String s1 = new String(buf,0,length);
String name = Util.parseName(s1);
int port = Util.parsePort(s1);
StringBuffer sb = serverInterface.getUsers().append(name+"\n");
serverInterface.getUsersList().setText(sb.toString());
serverInterface.getMap().put(name,port);
//下面代码用于将登录的所有用户列表发送给每一个客户端
Map<String,Integer> map = serverInterface.getMap();
Set<String> userNames = map.keySet();
StringBuffer buffer = new StringBuffer();
for(String userName:userNames)
{
buffer.append(userName + "\n");
}
os.write(buffer.toString().getBytes());
System.out.println(buffer.toString());
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
但是,在进行测试时,发现服务端可以正常接收到来及客户端的消息。但是,客户端却无法从服务端获取在线好友列表。debug追踪时,问题在于以下代码总是还没被执行就跳过了:
try
{
String s = Util.getLoginInfo(client.getName(),socket.getLocalPort());
os.write(s.getBytes());
while(true)
{
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String str = "";
StringBuffer sb = new StringBuffer();
while(null != (str = br.readLine()))
{
sb.append(str);
}
this.chatClient.getJTextArea1().setText(sb.toString());//这一行代码总是得不到执行就跳过
}
}
catch (IOException e)
{
e.printStackTrace();
}
可是,如果把上面这段代码提取出来,就可以正常执行了!
这个问题已经困扰我好几天了!绞尽脑汁,却还是得不到答案!衷心希望黑马论坛的大牛可以为我解除心头之痛!
|
|