这是我09年写的第一个Java程序,一个类QQ聊天软件,当时是为了练习,现在把源码拿出来,当时水平有限,现在也没动力去优化了,希望能对Java初学者有一些帮助!说老实话,好多东西都忘了,但是仍有好多人找我,说要“交作业”和“毕业设计写论文”的,请大家先学好java基础,课本上的基础知识是有的,但是课本上却没告诉我们怎么把这些知识组合起来成为一个软件,这才是做作业、写论文的最大的价值所在,这才也是中国教育目前最大的悲哀。
实现思路:
首先每登录一个用户都会先到主持人这边登记,会记录每个用户的联系方式(这些都是只有主持人知道),然后主持人会通知已经登记的用户又有新用户加入;
李四想和张三说话,就把想说的话告诉主持人,由主持人帮你传话给张三,李四是不知道怎么才能联系到张三,但是主持人有每个用户的联系方式;
私聊是一对一聊,如果群里只有一个两个人,那这个群聊也就是私聊;
一共包含六个类:
Client.java
Clientframe.java
ClientUtil.java
Message.java
Server.java
Usermessage.java
Client.java:

import java.awt.Color;
import java.awt.FileDialog;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
/**
 *
 * @author tian QQ客户端
 */
public class Client extends Clientframe {
	Message receivemsg; // 在线用户之间的聊天信息
	Usermessage usermsg; // 用户信息
	Socket s;
	ObjectOutputStream oos;
	String ipaddress; // 服务器IP
	ArrayList al = new ArrayList();
	String cmd;
	boolean flag = false;
	/**
	 * 构造方法
	 */
	public Client() {
		usermsg = new Usermessage();
		try {
			usermsg.ip = InetAddress.getLocalHost().getHostAddress();
		} catch (UnknownHostException e3) {
			e3.printStackTrace();
		}
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//上线时触发,发送用户信息(usermsg)给服务器
		jb1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				String ipaddress = jtfname2.getText(); // 得到服务器IP
					try {
						if (flag == false) {
						s = new Socket(ipaddress, 9999);
						oos = new ObjectOutputStream(s.getOutputStream());
						flag = true;
						}
					} catch (UnknownHostException e2) {
						e2.printStackTrace();
					} catch (IOException e2) {
						e2.printStackTrace();
					}
					try {
						usermsg.username = jtfname.getText();
						usermsg.password = jtfpwd.getText();
						usermsg.flag1 = true; // 登陆是设为true
						oos.writeObject(usermsg);
						oos.reset();
					} catch (UnknownHostException e1) {
						e1.printStackTrace();
					} catch (IOException e1) {
						e1.printStackTrace();
					}
					ClientThread ct = new ClientThread();
					ct.start();
				}
		});
//		  监听用户下线信息
		jfb.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				Usermessage usermsg1 = new Usermessage();
				usermsg1.flag1 = false; // 表明用户下线
				usermsg1.username = usermsg.username;
				usermsg1.password = usermsg.password;
				try {
					usermsg1.ip = InetAddress.getLocalHost().getHostAddress();
				} catch (UnknownHostException e2) {
					e2.printStackTrace();
				}
				// 将用户信息发给服务器说明该客户要下线
				try {
					oos.writeObject(usermsg1);
					System.exit(0);
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});
//		  监听双击在线用户列表事件
		jli.addMouseListener(new MouseListener() {
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2) {
					JList source = (JList) e.getSource();
					Object[] selection = source.getSelectedValues();
					cmd = (String) selection[0];
					String[] ss = cmd.split("\s+");
					jfaa.setTitle("与" + ss[0] + "聊天");
					jfaa.setVisible(true);
				}
			}
			public void mouseEntered(MouseEvent e) {
			}
			public void mouseExited(MouseEvent e) {
			}
			public void mousePressed(MouseEvent e) {
			}
			public void mouseReleased(MouseEvent e) {
			}
		});
		//监听私聊send按钮
		jbaa.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				ClientUtil cu = new ClientUtil();
				Message m = new Message();
				m.fromname = usermsg.username;
				m.msg = jtabb.getText();
				String[] ss = cmd.split("\s+");
				m.toname = ss[0];
				m.flag = true;
				try {
					oos.writeObject(m);
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				jtabb.setText("");
//				 调用ClientUtil中的dealmessage()方法对信息进行处理
				ArrayList msglist = ClientUtil.dealmessage(m.msg);
				// 调用ClientUtil中的printmsg()将信息显示到面板上
				cu.printmsg(msglist, jtaaa,m);
			}
		});
		 // 监听群聊send按钮
		jba.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Message m = new Message();
				m.fromname = usermsg.username;
				m.msg = textPane1.getText();
				m.toname = "alluser";
				try {
					oos.writeObject(m);
				} catch (IOException e1) {
					// e1.printStackTrace();
				}
				textPane1.setText("");
			}
		});
		// 监听Transportfile按钮,实现文件传输,只能传输小文件
		jbbc.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				FileDialog f = new FileDialog(jfaa, "文件传送", FileDialog.LOAD);
				f.setVisible(true);
				String filename = f.getDirectory() + f.getFile();
				File file = new File(filename);
				FileInputStream fis;
				try {
					fis = new FileInputStream(file);
					int len = (int) file.length();
					byte[] data = new byte[len];
					fis.read(data);
					Message m = new Message();
					m.data = new byte[len];
					m.fromname = usermsg.username;
					String[] ss = cmd.split("\s+");
					m.toname = ss[0];
					m.flag = false;
					for (int i = 0; i < data.length; i++) {
						m.data[i] = data[i];
					}
					oos.writeObject(m);
				} catch (FileNotFoundException e1) {
					e1.printStackTrace();
				} catch (IOException e2) {
					e2.printStackTrace();
				}
			}
		});
		// 监听"群聊"按钮
		jb.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				jfa.setVisible(true);
			}
		});
	}
	 //显示在线用户列表(由服务器传入)
	class ClientThread extends Thread {
		ObjectInputStream ois;
		public void run() {
			ClientUtil cu = new ClientUtil();
			try {
				ois = new ObjectInputStream(s.getInputStream());
				while (true) {
					Object[] o = (Object[]) ois.readObject();
					// 处理“用户名重名”信息
					if (o[0] instanceof String) {
						String s1 = (String) o[0];
						if (s1.equals("用户名重复请重新填写")) {
							jtfname3.setText(s1);
						} else {
							jfb.setTitle(jtfname.getText());
							jfb.setVisible(true);
							jf.setVisible(false);
						}
					}
					// 处理用户信息
					if (o[0] instanceof Usermessage) {
						Usermessage u = (Usermessage) o[0];
						// 处理用户上线信息
						if (u.flag1) {
							dlm.clear();
							for (int i = 0; i < o.length; i++) {
								dlm.addElement(o[i].toString());
							}
						} else {
							// 处理用户下线信息
							for (int i = 0; i < dlm.size(); i++) {
								String s = dlm.get(i).toString();
								String[] ss = s.split("\s+");
								if (ss[0].equals(u.username)) {
									dlm.remove(i);
									break;
								}
							}
						}
					}
					// 处理聊天信息
					if (o[0] instanceof Message) {
						Message u = (Message) o[0];
						// 处理群聊信息
						if (u.toname.equals("alluser")) {
							// 调用ClientUtil中的dealmessage()方法对信息进行处理
							ArrayList msglist = ClientUtil.dealmessage(u.msg);
							// 调用ClientUtil中的printmsg()将信息显示到面板上
							cu.printmsg(msglist, textPane,u);
						}
						// 处理私聊信息
						if (u.toname.equals(usermsg.username)) {
							if (u.flag) {
								jfaa.setVisible(true);
								jfaa.setTitle("与" + u.fromname + "聊天");
//								 调用ClientUtil中的dealmessage()方法对信息进行处理
								ArrayList msglist = ClientUtil.dealmessage(u.msg);
								// 调用ClientUtil中的printmsg()将信息显示到面板上
								cu.printmsg(msglist, jtaaa,u);
								cmd = u.fromname;
							} else {
//								处理接收文件
								FileDialog f = new FileDialog(jfaa,
										"文件接收", FileDialog.SAVE);
								f.setVisible(true);
								String filename = f.getDirectory()
										+ f.getFile();
								FileOutputStream fos = new FileOutputStream(
										filename);
								byte[] buf = u.data;
								fos.write(buf);
								fos.close();
							}
						}
					}
				}
			} catch (IOException e) {
				 e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) {
		Client c = new Client();
	}
}

Clientframe.java:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import sun.awt.image.ToolkitImage;
import sun.reflect.generics.scope.Scope;
/**
 *
 * @author tian
 *
 */
public class Clientframe {
	//	登陆窗口
	JFrame jf;
	JPanel jp1;
	JPanel jp2;
	JLabel jl1;
	JLabel jl2;
	JLabel jl3;
	JLabel jl4;
	JLabel jl5;
	JLabel jl6;
	JLabel jl7;
	Checkbox cb;
	Checkbox cb2;
	Choice ci ;
	ImageIcon im;
	JLabel jl8;
	JTextField jtfname;
	JPasswordField jtfpwd;
	JTextField jtfname2;
	JTextField jtfname3;
	JButton jb1;
	JButton jb2;
//	  群聊窗口
	JFrame jfa;
	JPanel jpa;
	JPanel jp;
	JPanel jpl;
	TextArea jtaa;
	TextArea jtab;
	JButton jba;
	JButton jbb;
	JButton jbc;
	JTextPane textPane;
	JTextPane textPane1;
	ImageIcon iic;
//	  私聊窗口
	JFrame jfaa;
	JPanel jpaa;
	JPanel jpp;
	JPanel jpll;
	JTextPane jtaaa;
	JTextPane jtabb;
	JButton jbaa;
	JButton jbbb;
	JButton jbbc;
	ImageIcon iic2;
//	  上线用户列表
	JFrame jfb;
	JPanel jpan;
	JPanel jpan1;
	DefaultListModel dlm;
	JList jli;
	JButton jb;
	ImageIcon ii;
	ImageIcon iii;
	JLabel jl;
//	群聊表情窗口
	JFrame jfc;
	JPanel jpbq;
	/**
	 *
	 */
	public Clientframe(){
//		初始化群聊表情窗口
		jfc = new JFrame("表情");
		jpbq = new JPanel();
		jfc.add(jpbq, BorderLayout.CENTER);
		jpbq.setLayout(new GridLayout(5,5));
		jfc.add(jpbq);
		URL qqurl = this.getClass().getClassLoader().getResource("Image/qq.jpg");
		jfc.setIconImage(Toolkit.getDefaultToolkit().createImage(qqurl));
		ArrayList al = new ArrayList();
		for (int i = 10; i < 35; i++) {
			URL url = this.getClass().getClassLoader().getResource( "Image/"+i+".gif");
			ImageIcon ii=new ImageIcon(url);
			JLabel jl = new JLabel(ii);
			al.add(jl);
		}
		for (int j = 0; j < al.size(); j++) { 			JLabel jl = (JLabel) al.get(j); 			jl.addMouseListener(new MouseListener(){ 				public void mouseClicked(MouseEvent e) { 					StyledDocument doc = textPane1.getStyledDocument(); 					SimpleAttributeSet attr = new SimpleAttributeSet(); 					StyleConstants.setForeground(attr, Color.red); 					String s = e.getSource().toString(); 					Pattern p = Pattern.compile(".gif"); 					Matcher m = p.matcher(s); 					while(m.find()){ 							int start = m.start(); 							int end = m.end(); 							String subs = s.substring(start-2,end); 							String path = "d:\"+subs; 							textPane1.setCaretPosition(doc.getLength()); // 设置插入位置 							File file = new File(path); 							Icon image = new ImageIcon(file.getAbsoluteFile().toString()); 							textPane1.insertIcon(image); 							try { 								doc.insertString(doc.getLength(), file.getName(), attr); 							} catch (BadLocationException e1) { 								 								e1.printStackTrace(); 							} 						} 				} 				public void mouseEntered(MouseEvent e) { 					 				} 			 				public void mouseExited(MouseEvent e) { 					 				} 				public void mousePressed(MouseEvent e) { 					 				} 				public void mouseReleased(MouseEvent e) { 					 				} 			}); 			jpbq.add(jl); 		} 		 		jfc.setVisible(false); 		jfc.setSize(250,250); 		 //		初始化登陆窗口 		 		jf = new JFrame("QQ用户登陆"); 		jp1 = new JPanel(); 		jp2 = new JPanel(); 		jtfname = new JTextField("",15); 		jtfpwd = new JPasswordField("",15); 		jtfname2 = new JTextField("",15); 		jl1 = new JLabel("QQ账号:"); 		jl2 = new JLabel("QQ密码:"); 		jl3 = new JLabel("ServerIP:"); 		jl4 = new JLabel("申请账号"); 	    jl5 = new JLabel("忘了密码"); 		jl6 = new JLabel("QQ状态:"); 		jl7=new JLabel("动感地带 "); 		cb = new Checkbox("自动登录"); 	    cb2 = new Checkbox("记住密码"); 		ci = new Choice(); 		jb1 = new JButton("登录");         jb2 = new JButton("退出");         URL jmurl =this.getClass().getClassLoader().getResource("Image/jm.jpg");         im = new ImageIcon(jmurl);         jl8=new JLabel(im); 		jf.add(jp1,BorderLayout.CENTER); 		jf.add(jp2,BorderLayout.SOUTH); 		jp1.add(jl8); 		jp1.add(jl1); 		jp1.add(jtfname); 		jp1.add(jl4); 		jp1.add(jl2); 		jp1.add(jtfpwd); 		jp1.add(jl5); 		jp1.add(jl3); 		jp1.add(jtfname2); 		jp1.add(jl7); 		jp1.add(jl6); 		jp1.add(ci); 		jp1.add(cb2); 		jp1.add(cb); 		jp2.add(jb1); 		jp2.add(jb2); 		jf.setBackground(new Color(100, 253, 98)); 		jp1.setBackground(new Color(176, 224, 230 )); 		jp2.setBackground(new Color(127, 255, 212 )); 		ci.add("在线"); 		ci.add("Q我吧"); 		ci.add("忙碌"); 		ci.add("离开"); 		ci.add("静音"); 		ci.add("隐身"); 		jl1.setForeground(Color.BLUE); 		jl2.setForeground(Color.BLUE); 		jl3.setForeground(Color.BLUE); 		jl4.setForeground(Color.BLUE); 		jl5.setForeground(Color.BLUE); 		jl7.setForeground(Color.BLUE); 		jb1.setSize(20, 10); 		jb2.setSize(20, 10);         jf.setSize(333, 250); 		jf.setIconImage(Toolkit.getDefaultToolkit().createImage(qqurl)); 		jf.setLocation(500, 250); 		jf.setResizable(false); 		jf.setVisible(true); 		 		jb2.addActionListener(new ActionListener(){ 			public void actionPerformed(ActionEvent e) { 				System.exit(1); 				 			} 			 		}); 		 //		初始化群聊窗口 		jfa = new JFrame("QQ聊天室"); 		jpa = new JPanel(); 		jp = new JPanel(); 		jpl = new JPanel(); 		dlm = new DefaultListModel(); 		URL iicurl =this.getClass().getClassLoader().getResource("Image/12.gif"); 		iic=new ImageIcon(iicurl); 		textPane = new JTextPane(); 		textPane1 = new JTextPane(); 		textPane.setPreferredSize(new Dimension(360, 200)); 		textPane1.setPreferredSize(new Dimension(360, 100)); 		textPane.setFont(new Font("Serif",Font.BOLD,14)); 		textPane1.setFont(new Font("Serif",Font.BOLD,14)); 		JScrollPane scrollPane = new JScrollPane(textPane); 		JScrollPane scrollPane1 = new JScrollPane(textPane1); 		textPane1.setForeground(Color.red); 		jba = new JButton("发送"); 		jbb = new JButton("清空"); 		jbc = new JButton("表情",iic); 		jbc.setMnemonic(KeyEvent.VK_F);//给“表情”按钮设置快捷键 		jba.setForeground(Color.BLUE); 		jbb.setForeground(Color.BLUE); 		jbc.setForeground(Color.BLUE); 		jfa.add(jpa, BorderLayout.NORTH); 		jfa.add(jp, BorderLayout.CENTER); 		jfa.add(jpl, BorderLayout.SOUTH); 		jfa.setIconImage(Toolkit.getDefaultToolkit().createImage(qqurl)); 		jpa.add(scrollPane); 		jp.add(scrollPane1); 		jpl.add(jba); 		jpl.add(jbc); 		jpl.add(jbb); 		jpa.setBackground(new Color(100, 253, 98)); 		jp.setBackground(new Color(100, 253, 98)); 		jpl.setBackground(new Color(100, 253, 98)); 		jfa.setSize(380, 400); 		jfa.setLocation(500, 150); 		jfa.setResizable(false); 		jfa.setVisible(false); 		jbb.addActionListener(new ActionListener() { 			public void actionPerformed(ActionEvent e) { 				textPane1.setText(""); 			} 		}); 		 		 //		 监听群聊表情按钮 		jbc.addActionListener(new ActionListener() { 			public void actionPerformed(ActionEvent e) { 				jfc.setVisible(true); 				jfc.setLocation(500, 150); 			} 		}); 		 //		初始化私聊窗口 		jfaa = new JFrame("私聊");			 		jpaa = new JPanel();			 		jpp = new JPanel(); 		jpll =new JPanel(); 		URL transcurl =this.getClass().getClassLoader().getResource("Image/trans.gif"); 		iic2 =new ImageIcon(transcurl); 		jtaaa = new JTextPane();	 		jtabb = new JTextPane(); 		jtaaa.setPreferredSize(new Dimension(360, 200)); 		jtabb.setPreferredSize(new Dimension(360, 100)); 		JScrollPane scrollPanea = new JScrollPane(jtaaa); 		JScrollPane scrollPaneb = new JScrollPane(jtabb); 		jtabb.setForeground(Color.red); 		jbaa = new JButton("发送");		 		jbbb = new JButton("清空");		 		jbbc = new JButton("",iic2); 		jbc.setMnemonic(KeyEvent.VK_F); 		jtaaa.setFont(new Font("Serif",Font.BOLD,14)); 		jtabb.setFont(new Font("Serif",Font.BOLD,14)); 	    jbaa.setForeground(Color.BLACK); 		jbbb.setForeground(Color.BLACK); 		jbbc.setForeground(Color.BLACK); 		jfaa.add(jpaa,BorderLayout.NORTH); 		jfaa.add(jpp,BorderLayout.CENTER); 		jfaa.add(jpll,BorderLayout.SOUTH); 		jfaa.setIconImage(Toolkit.getDefaultToolkit().createImage(qqurl)); 		jpaa.add(scrollPanea); 		jpp.add(scrollPaneb); 		jpll.add(jbaa); 		jpll.add(jbbc); 		jpll.add(jbbb); 		jf.setBackground(new Color(100, 253, 98)); 		jpaa.setBackground(new Color(100, 253, 98));		 		jpp.setBackground(new Color(100, 253, 98));		  		jpll.setBackground(new Color(100, 253, 98));		  		jfaa.setSize(380, 400); 		jfaa.setLocation(500, 150); 		jfaa.setResizable(false); 		jfaa.setVisible(false);	 		 	    		jbbb.addActionListener(new ActionListener(){ 			public void actionPerformed(ActionEvent e) { 				jtabb.setText(""); 			} 			 		}); 		 //		初始化上线用户列表 		jfb = new JFrame(); 		jpan = new JPanel(); 		dlm = new DefaultListModel(); 		jli = new JList(dlm); 		jb = new JButton(">>>群聊模式");
		jli.setForeground(Color.BLUE);
		jb.setSize(15, 20);
		jb.setForeground(Color.BLUE);
		jfb.add(jpan);
		jpan.setLayout(new BorderLayout());
		jpan.add(jli, BorderLayout.CENTER);
		jpan.add(jb, BorderLayout.SOUTH);
		jpan.add(jli);
		jfb.setIconImage(Toolkit.getDefaultToolkit().createImage(qqurl));
		jfb.setResizable(false);
		jfb.setLocation(500, 200);
		jfb.setSize(200, 450);
		jfb.setVisible(false);
	}
}

ClientUtil.java:

import java.awt.Color;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
public class ClientUtil {
	/**
	 * 处理已接收信息,将表情与字符分开
	 */
	public static ArrayList dealmessage(String message) {
		String msg = message;
		// 处理字符串
		ArrayList list = new ArrayList();
		ArrayList substrlist = new ArrayList();
		list.add(0);
		Pattern p = Pattern.compile(".gif");
		Matcher m = p.matcher(msg);
		while (m.find()) {
			int start = m.start() - 2;
			list.add(start);
			start = start + 6;
			if (start > msg.length()) {
				break;
			}
			list.add(start);
		}
		for (int i = 0; i < list.size(); i++) {
			if (i < list.size()-1) {
				int begin = Integer.parseInt(list.get(i).toString());
				int over = Integer.parseInt(list.get(i + 1).toString());
				String subs = msg.substring(begin, over);
				substrlist.add(subs);
			}else{
				String subs = msg.substring(Integer.parseInt(list.get(i).toString()));
				substrlist.add(subs);
			}
		}
		return substrlist;
	}
	/**
	 * 将处理过的信息显示在聊天面板上
	 */
	public void printmsg(ArrayList list,JTextPane tPane,Message u){
		Icon image = null;
		JTextPane textPane = tPane;
		StyledDocument doc = textPane.getStyledDocument();
		SimpleAttributeSet attr = new SimpleAttributeSet();
		StyleConstants.setForeground(attr, Color.red);
		ArrayList msglist = list;
		Pattern p = Pattern.compile(".gif");
		try {
			doc.insertString(doc.getLength(), u.toString() ,
					attr);
		} catch (BadLocationException e1) {
			e1.printStackTrace();
		}
		for (int i = 0; i < msglist.size(); i++) {
			String sub = msglist.get(i).toString();
			Matcher m = p.matcher(sub);
			if(m.find()){
				URL url = this.getClass().getClassLoader().getResource("Image/" + sub);
				textPane.setCaretPosition(doc.getLength()); // 设置插入位置
				image = new ImageIcon(url);
				textPane.insertIcon(image);
			}else{
				try {
					doc.insertString(doc.getLength(), sub ,
							attr);
				} catch (BadLocationException e) {
					e.printStackTrace();
				}
			}
		}
		try {
			doc.insertString(doc.getLength(), "n" ,
					attr);
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
	}
}

Message.java:

import java.io.Serializable;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
/**
 *
 * @author tian
 *	在线用户之间的聊天信息
 */
public class Message implements Serializable{
	public String fromname;
	public String toname;
	public String msg;
	public boolean flag;
	byte[] data = null;//用于传送文件
	int len;   //信息会随着文件的长度而变化
	public Message(){
	}
	public int getLen() {
		return len;
	}
	public void setLen(int len) {
		this.len = len;
	}
	public String toString() {
		Date today = new Date();
		Locale cnLocale = new Locale("zh", "CN");
		DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, cnLocale);
		return fromname+" 于"+df.format(today)+"说:"+"n";
	}
}

Server.java:

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
 *
 * @author tian	QQ服务器端
 *
 */
public class Server {
	JFrame jf = new JFrame("QQ服务器");
	JPanel jp = new JPanel();
	JPanel jp1 = new JPanel();
	JTextArea jta = new JTextArea();
	JTextArea jta1 = new JTextArea();
	JScrollPane jsp = new JScrollPane(jta1);
	JLabel jl = new JLabel("QQ在线列表");
	protected ServerSocket ss;
	DefaultListModel dlm = new DefaultListModel();
	JList jli = new JList(dlm);
	ArrayList uml = new ArrayList();
	HashMap mp = new HashMap();		//存储socket对应的oos
	HashMap mp1 = new HashMap();	//存储用户所对应的socket
//	 初始化服务器
	public Server() {
		jf.add(jp, BorderLayout.NORTH);
		jp.setLayout(new FlowLayout(FlowLayout.LEFT));
		jp.add(jl);
		jf.add(jli, BorderLayout.CENTER);
		jf.add(jsp, BorderLayout.SOUTH);
		jta1.setSize(250, 300);
		jsp.setSize(250, 300);
		jf.setSize(250, 500);
		jf.setLocation(500, 150);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setVisible(true);
		try {
			ss = new ServerSocket(9999);
			Thread t = new ServerThread();
			t.start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	/**
	 *
	 * 监听线程
	 *
	 */
	class ServerThread extends Thread {
		public void run() {
			try {
				while (true) {
					Socket sc = ss.accept();
					ObjectOutputStream oos = new ObjectOutputStream(sc
							.getOutputStream());
					mp.put(sc, oos);
					SocketThread st = new SocketThread(sc);
					st.start();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	/**
	 *
	 * 读线程
	 *
	 */
	class SocketThread extends Thread {
		Socket sc;
		public SocketThread(Socket sc) {
			this.sc = sc;
		}
		public void run() {
			try {
				ObjectInputStream ois = new ObjectInputStream(sc
						.getInputStream());
				while (true) {
					Object m = ois.readObject();
					// 处理用户信息
					if (m instanceof Usermessage) {
						Usermessage u = (Usermessage) m;
						// 如果flag1为true,则是登陆信息
						if (u.flag1 && Server.checkuser(u,uml,mp)) {
							mp1.put(u.username, sc);
							dlm.addElement(sc.getRemoteSocketAddress() + "n");
							uml.add(u);
							Object[] o = uml.toArray();
							Iterator i = mp.keySet().iterator();
							while (i.hasNext()) {
								Socket sc = (Socket) i.next();
								((ObjectOutputStream) mp.get(sc))
										.writeObject(o);
							}
						} else if(!u.flag1){
							ois.close();
							ObjectOutputStream oos = (ObjectOutputStream) mp
									.get(sc);
							oos.close();
							sc.close();
							mp.remove(sc);
							for (int i = 0; i < uml.size(); i++) { // 通过for循环可以遍历ArrayList
								Usermessage um = (Usermessage) uml.get(i); // 通过下标输出ArrayList里面的值
								if (um.username.equals(u.username)) {
									uml.remove(i);
									break;
								}
							}
							Object[] o = new Object[1];
							o[0] = u;
							Iterator i = mp.keySet().iterator();
							while (i.hasNext()) {
								Socket sc = (Socket) i.next();
								((ObjectOutputStream) mp.get(sc))
										.writeObject(o);
							}
						}
					}
					// 处理聊天信息
					if (m instanceof Message) {
						Message u = (Message) m;
						//处理群聊
						if (u.toname.equals("alluser")) {
							ArrayList al = new ArrayList();
							al.add(u);
							Object[] o1 = al.toArray();
							Iterator i = mp.keySet().iterator();
							while (i.hasNext()) {
								Socket sc = (Socket) i.next();
								((ObjectOutputStream) mp.get(sc))
										.writeObject(o1);
							}
						}else{
							//处理私聊和文件传送
							ArrayList al = new ArrayList();
							al.add(u);
							Object[] o1 = al.toArray();
							((ObjectOutputStream) mp.get(mp1.get(u.toname))).writeObject(o1);
						}
					}
				}
			} catch (IOException e1) {
//				e1.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
	}
	//检测用户名是否重复
	public static boolean checkuser(Usermessage u,ArrayList uml,HashMap mp){
		ArrayList uml1 = uml;
		int i;
		for (i = 0; i < uml1.size(); i++) { // 通过for循环可以遍历ArrayList 			Usermessage um = (Usermessage) uml1.get(i); // 通过下标输出ArrayList里面的值 			if (um.username.equals(u.username)) { 				String s = "用户名重复请重新填写"; 				Object[] o = new Object[1]; 				o[0] = s; 				Iterator it = mp.keySet().iterator(); 				while (it.hasNext()) { 					try { 						Socket sc = (Socket) it.next(); 						((ObjectOutputStream) mp.get(sc)) 								.writeObject(o); 					} catch (IOException e) { 						e.printStackTrace(); 					} 				} 			break;	 			} 		} 		if(i>=uml1.size()){
		String s = "success";
		Object[] o = new Object[1];
		o[0] = s;
		Iterator it = mp.keySet().iterator();
		while (it.hasNext()) {
			try {
				Socket sc = (Socket) it.next();
				((ObjectOutputStream) mp.get(sc))
						.writeObject(o);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return true;
		}else{
			return false;
		}
	}
	public static void main(String[] args) {
		Server s = new Server();
	}
}

Usermessage.java:

import java.io.Serializable;
import java.net.ServerSocket;
import java.net.Socket;
/**
 *
 * @author tian
 *	用于传递用户信息
 */
public class Usermessage implements Serializable{
	public String username;		//用户名称
	public String password;		//用户密码
	public String ip;			//用户IP
	public boolean flag1;		//标志用户上线还是下线,为true表示为登陆
	public String ipaddress;	//服务器IP
	public Usermessage(){
	}
	public boolean equals(Usermessage user) {
		boolean flag= false;
		if(ip.equals(user.ip)){
			flag = true;
		}
		return flag;
	}
	public String toString() {
		return username + "  "+ ip;
	}
}

打包好的程序和图片资源下载地址:
http://download.csdn.net/detail/xionglong/1638561
效果:



表情窗口