java 聊天室 :在线人员列表显示,广播和私聊,经过测试,还有一点小bug。请多指教了!
package socketForm;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
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.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
public class Client extends JFrame implements ActionListener {
private static String HOST = "127.0.0.1";
private static int PORT = 9090;
private JTextField username;
private DefaultListModel lm;
private JList list;
private JTextField msg;
private JTextArea allMsg = new JTextArea();
private Socket client; // 声明一个客户端的连接
private PrintWriter pw;
public Client() throws Exception {
System.err.println("-----------client-------------");
addJMenuBar();
username = new JTextField(10);
JPanel p = new JPanel();
p.add(new JLabel("用户标识:"));
p.add(username);
JButton conn = new JButton("连接");
conn.setActionCommand("c");
conn.addActionListener(this);
p.add(conn);
JButton exitBtn = new JButton("退出");
exitBtn.setActionCommand("exit");
exitBtn.addActionListener(this);
p.add(exitBtn);
p.setBorder(new LineBorder(Color.GRAY));
// 以下是中间的面板
JPanel center = new JPanel(new BorderLayout());
lm = new DefaultListModel();
list = new JList(lm);
lm.addElement("全部");
list.setSelectedIndex(0); // 默认选中全部
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
list.setVisibleRowCount(2);
JScrollPane jc = new JScrollPane(list);
jc.setBorder(new TitledBorder("在线"));
jc.setPreferredSize(new Dimension(70, center.getHeight()));
center.add(jc, BorderLayout.EAST);
JPanel send = new JPanel();
send.add(new JLabel("消息:"));
msg = new JTextField(20);
send.add(msg);
JButton sendBtn = new JButton("发送");
sendBtn.setActionCommand("send");
sendBtn.addActionListener(this);
send.add(sendBtn);
center.add(send, BorderLayout.SOUTH);
allMsg.setEditable(false);
center.add(new JScrollPane(allMsg));
Container c = getContentPane();
// layout 布局位置
c.add(p, BorderLayout.NORTH);
c.add(center, BorderLayout.CENTER);
addWindowListener(new WindowAdapter() { // 当用户退出时,先要通知所有人,有人退出
public void windowClosing(WindowEvent arg0) {
sendExitMsg();
}
});
setBounds(300, 300, 400, 300);
setVisible(true);
}
public static void main(String[] args) throws Exception {
// 提供window装饰
// JFrame.setDefaultLookAndFeelDecorated(true);
new Client();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("c")) {
System.err.println("连接...");
connecting();
} else if (e.getActionCommand().equals("send")) {
// [1]接受者 [2]content [3] sender
String msg = "on@#" + list.getSelectedValue() + "@#"
+ this.msg.getText() + "@#" + username.getText();
System.err.println(">发出的消息为:" + msg);
pw.println(msg);
pw.flush();
} else if (e.getActionCommand().equals("exit")) { // 用户要退出
sendExitMsg();
}
}
private void connecting() {
try {
System.err.println(HOST + "," + PORT);
client = new Socket(HOST, PORT);
String userName = this.username.getText(); // 获取用户标识
pw = new PrintWriter(client.getOutputStream(), true);
pw.println(userName);
pw.flush();
this.setTitle("用户[" + userName + "]在线...");
new ClientThread().start();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 添加菜单,控制选项,可以设置服务器及端口号
*/
private void addJMenuBar() {
JMenuBar bar = new JMenuBar();
JMenu m = new JMenu("选项");
JMenuItem mi = new JMenuItem("设置");
JMenuItem help = new JMenuItem("帮助");
mi.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final JDialog set = new JDialog(Client.this, true);
set.setBounds(Client.this.getX(), Client.this.getY(), 300, 100);
set.setLayout(new FlowLayout());
JButton btn = new JButton("设置");
final JTextField host = new JTextField(10);
host.setText("127.0.0.1");
final JTextField port = new JTextField(5);
port.setText("9090");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Client.HOST = host.getText();
Client.PORT = Integer.parseInt(port.getText());
System.err.println(Client.HOST);
set.dispose();// 关闭且销毁
}
});
set.add(new JLabel("服务器:"));
set.add(host);
set.add(new JLabel(":"));
set.add(port);
set.add(btn);
set.setVisible(true);
}
});
help.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JDialog set = new JDialog(Client.this, true);
set.setBounds(Client.this.getX(), Client.this.getY(), 300, 100);
JLabel jl = new JLabel("版权所有@李杰.2012-3-6,QQ:270819589");
set.add(jl);
set.setVisible(true);
}
});
bar.add(m);
m.add(mi);
m.add(help);
this.setJMenuBar(bar);
}
/**
* 发送退出的消息 pw client.getOutputStream()
*/
private void sendExitMsg() {
if (client == null) {
System.exit(0);
return;
}
String msg = "exit@#全部@#null@#" + username.getText();
System.err.println("退出:" + msg);
pw.println(msg);
pw.flush();
System.exit(0);
}
/**
* 用以下线程来读取服务器发送来的信息
*/
class ClientThread extends Thread {
public void run() {
try {
// lm = new DefaultListModel();
Scanner sc = new Scanner(client.getInputStream());
while (sc.hasNextLine()) {
String str = sc.nextLine();
System.err.println("客户端接收到的消息 :" + str);
String[] msg = str.split("@#");
if (msg[0].equals("msg")) { // 开头不是msg就是cmd命令
if (msg[1].equals("server")) {
str = "[通知]:" + msg[2];
} else {
str = "[" + msg[1] + "]说:" + msg[2];
}
allMsg.append("\r\n" + str);
} else if (msg[0].equals("cmdAdd")) {
// 说明有人登录
System.err.println("add------------" + msg[2]);
System.err.println(lm.size());
lm.addElement(msg[2]);
} else if (msg[0].equals("cmdRed")) { // 说明有人退出
lm.removeElement(msg[2]);
}
System.err.println(list);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package socketForm;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.Set;
import javax.print.attribute.standard.Severity;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.border.TitledBorder;
public class Server extends JFrame {
private static final int PORT = 9090;
private JTextArea area;
private DefaultListModel lm;//显示在线人员列表
private static Map<String,Object> usersMap = new HashMap<String,Object>();
public Server() throws Exception {
area = new JTextArea();
area.setEditable(true);
lm = new DefaultListModel();
getContentPane().add(new JScrollPane(area));
JList list = new JList(lm);
JScrollPane jc = new JScrollPane(list);
jc.setPreferredSize(new Dimension(100, this.getHeight()));
jc.setBorder(new TitledBorder("在线"));
getContentPane().add(jc,BorderLayout.EAST);
JMenuBar bar = new JMenuBar();
JMenu m = new JMenu("控制");
bar.add(m);
m.setMnemonic('C');//ALT+C,助记符
final JMenuItem run = new JMenuItem("启动");
run.setAccelerator(KeyStroke.getKeyStroke('R',KeyEvent.CTRL_MASK));
//Server 实现了ActionListener接口,在一个接口中响应不同的button事件,cmd用于区分谁触发的
run.setActionCommand("run");
m.add(run);
m.addSeparator();
JMenuItem exit = new JMenuItem("退出");
exit.setAccelerator(KeyStroke.getKeyStroke('E',KeyEvent.CTRL_MASK));
exit.setActionCommand("exit");
m.add(exit);
setJMenuBar(bar);
ActionListener al = new ActionListener(){ //添加一个内部类,以启动服务器
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("run")){
startServer();
run.setEnabled(false);
}else{
System.exit(0);
}
}
};
run.addActionListener(al);
exit.addActionListener(al);
final int winWidth = 500;
final int winHeight = 400;
java.awt.Toolkit toolkit = java.awt.Toolkit.getDefaultToolkit();
//获取屏幕大小
int width = (int) toolkit.getScreenSize().getWidth();
int height = (int) toolkit.getScreenSize().getHeight();
setTitle("这是服务器");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//窗口显示位置
setBounds(width/2-winWidth/2,height/2-winHeight/2,winWidth, winHeight);
setVisible(true);
//pack();
}
private void startServer() {
try {
System.out.println("启动服务...");
ServerSocket server = new ServerSocket(PORT);
area.append("启动服务:"+server);
new ServerThread(server).start();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String args[]) throws Exception {
//JFrame.setDefaultLookAndFeelDecorated(true);
new Server();
}
/*
* 一个server线程,负责启动服务器 socket,监听连接
*/
class ServerThread extends Thread {
private ServerSocket server;
public ServerThread(ServerSocket server) {
this.server = server;
}
public void run() {
try {
while(true) {
Socket socketClient = server.accept();
System.err.println("--------server thread run-------");
Scanner sc = new Scanner(socketClient.getInputStream());
if(sc.hasNextLine()) {
String userName = sc.nextLine();
area.append("\r\n用户:["+userName+"]登录,"+socketClient);
//增加在线人员 名字
lm.addElement(userName);
//开启一个客户端线程
new ClientThread(socketClient);
//更新 已经登录的人 的在线列表,增加当前的这个socket人
msgAll(userName);
//更新当前登录的人 的在线列表
msgSelf(socketClient);
//保存 当前的这个 客户端连接
usersMap.put(userName,socketClient);
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
/*
* 负责与 客户端 交互
*/
class ClientThread extends Thread {
private Socket client;
public ClientThread(Socket client) {
System.err.println("初始。。。。。。。。。");
this.client = client;
//构造的时候 就启动
start();
}
public void run() {
System.err.println("一个线程启动成功。。。。");
try {
Scanner sc = new Scanner(client.getInputStream());
while(sc.hasNextLine()) {
String msg = sc.nextLine();
System.err.println(msg);
String[] msgs = msg.split("@#");
if(msgs[0].equals("exit")) {
System.err.println("退出人:"+msg);
//usersMap<String,Object>
usersMap.remove(msgs[3]);
//去掉在线 人姓名 从显示列表中
lm.removeElement(msgs[3]);
area.append("\r\n用户["+msgs[3]+"] 退出了。");
sendExitMsgToAll(msgs);
break;
} else {
sendMsgToSb(msgs); //发消息给某人
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
/**
* 通知所有人,有人登录
* 写出信息的类型为:
* 类型:通知(cmd)|消息(msg)@#发送者SomeBody@#消息文本MSG
*/
private void msgAll(String userName){
/**
* Set set = map.entrySet();
* Iterator it = set.iterator();
*
* while(it.hasNext())
* Map.Entry<String,Object> entry = it.next();
*
*
*/
//Iterator<Object> it = usersMap.values().iterator();
Iterator<String> names = usersMap.keySet().iterator();
for(Map.Entry<String,Object> entry : usersMap.entrySet()) {
//遍历 拿到客户端 各自 的socket
Socket s = (Socket) entry.getValue();
try {
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
String msg = "msg@#server@#用户["+userName+"]登录了.";
pw.println(msg);
pw.flush();
msg = "cmdAdd@#server@#"+userName;
pw.println(msg);
pw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 通知 当前登录的人 有多少人在线
* 显示在线用户姓名
*/
private void msgSelf(Socket client){
try{
PrintWriter pw = new PrintWriter(client.getOutputStream(),true);
Iterator<String> it = usersMap.keySet().iterator();
while(it.hasNext()){
//key 用户名
String msg = "cmdAdd@#server@#"+it.next();
pw.println(msg);
pw.flush();
}
}catch(IOException e){
e.printStackTrace();
}
}
/**
* 发消息给某人
*/
private void sendMsgToSb(String[] msgs) throws IOException{
if(msgs[1].equals("全部")){ //发消息给所有人
Iterator<String> userNames = usersMap.keySet().iterator();
while(userNames.hasNext()){
String userName = userNames.next();
if(!userName.equals(msgs[3])){ //如果不是自己
Socket s = (Socket)usersMap.get(userName);
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
String str = "msg@#"+msgs[3]+"@#"+msgs[2];
pw.println(str);
pw.flush();
}
}
}else{
Socket s = (Socket)usersMap.get(msgs[1]);
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
//msg[3]---username msg[2]----content
String str = "msg@#"+msgs[3]+"@#"+msgs[2];
pw.println(str);
pw.flush();
}
}
/**
* 通知所有人有人退出
*/
private void sendExitMsgToAll(String[] msgs) throws IOException{
Iterator<String> userNames = usersMap.keySet().iterator();
while(userNames.hasNext()){
String userName = userNames.next();
//拿到用户名对应的socket
Socket s = (Socket)usersMap.get(userName);
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
String str = "msg@#server@#用户["+msgs[3]+"]退出了。";
pw.println(str);
pw.flush();
str = "cmdRed@#server@#"+msgs[3];
pw.println(str);
pw.flush();
}
}
}