java 聊天室

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();
			
		}
	}
}


  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值