主要实现:
1.群聊
2.私聊
3.发送文字(可选择字体,颜色)
4.发送图片
5.发送文件,支持多个文件同时发送/接收。
消息对象:
package com.socket.tcp.basechat;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 包装发送的消息对象。
* @author lucky star.
*
*/
public class MsgInfo implements Serializable{
/**
* serialVersionUID
*/
private static final long serialVersionUID = 2817564515497626133L;
private String clientId; // 标示每个客户端
// 私聊人
private String user;
// 是否私聊,设置USER时自动设置为TRUE,否则为FALSE。
private boolean isSL = false;
// 文件大小
private int fileLen = -1;
public int getFileLen() {
return fileLen;
}
public void setFileLen(int fileLen) {
this.fileLen = fileLen;
}
// 文件列表
private String[] files = {};
public String[] getFiles() {
return files;
}
public void setFiles(String[] files) {
this.files = files;
}
public boolean isSL() {
return isSL;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
isSL = true;
}
// 文本消息
private String msgContent;
// 图片消息
private List<File> imgs = new ArrayList<File>();
// 消息发送时间
private Date sendTime;
// 发送人
private String sender;
// online list
private List<String> onlines = new ArrayList<String>();
public List<String> getOnlines() {
return onlines;
}
public void setOnlines(List<String> onlines) {
this.onlines = onlines;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
// 一个客户端下线后通知其他客户端时,new一个空的消息对象。
// 仅携带onlines,通知其他客户端更新任意列表
public MsgInfo() {
super();
}
public MsgInfo(String msgContent, List<File> imgs, List<File> attaches,
Date sendTime, String sender) {
super();
this.msgContent = msgContent;
this.imgs = imgs;
this.sendTime = sendTime;
this.sender = sender;
}
// 私聊时用到这个构造方法。
public MsgInfo(String msgContent, List<File> imgs, List<File> attaches,
Date sendTime, String sender,String user) {
this(msgContent,imgs,attaches,sendTime,sender);
this.user = user;
}
public MsgInfo(String msgContent) {
this.msgContent = msgContent;
}
public String getMsgContent() {
return msgContent;
}
public void setMsgContent(String msgContent) {
this.msgContent = msgContent;
}
public List<File> getImgs() {
return imgs;
}
public void setImgs(List<File> imgs) {
this.imgs = imgs;
}
public Date getSendTime() {
return sendTime;
}
public void setSendTime(Date sendTime) {
this.sendTime = sendTime;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public boolean isEmpty() {
boolean a = msgContent == null || msgContent.equals("");
boolean b = imgs == null || imgs.size() == 0;
boolean d = files == null || files.length == 0;
if (a && b && d) return true;
return false;
}
public boolean isMsg() {
if (isEmpty()) {
if (sender != null && !"".equals(sender)) {
return false;
}
}
return true;
}
}
服务端:
package com.socket.tcp.basechat;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Server端。
* @author lucky star
*
*/
public class Server {
private ServerSocket ss = null;
private boolean isStarted = false;
private Map<String,RecvThread> clients = new HashMap<String,Server.RecvThread>();
public void start() {
try {
ss = new ServerSocket(8888);
isStarted = true;
System.out.println("Tcp Server started.");
} catch (BindException e) {
System.out.println("端口被占用,请关闭相关程序。");
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while (isStarted) {
try {
Socket s = ss.accept();
RecvThread rt = new RecvThread(s);
new Thread(rt).start();
} catch (IOException e) {
System.out.println(e.getMessage());
// e.printStackTrace();
}
}
}
private class RecvThread implements Runnable {
private Socket s = null;
private ObjectInputStream ois = null;
private ObjectOutputStream oos = null;
private boolean isConnected = false;
private String user;
public RecvThread(Socket s) {
this.s = s;
try {
ois = new ObjectInputStream(s.getInputStream());
oos = new ObjectOutputStream(s.getOutputStream());
// System.out.println("oos:" + oos);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
isConnected = true;
}
// 发送对象
public void sendMsg(MsgInfo mi) {
try {
oos.writeObject(mi);
oos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 转发消息给各客户端
public void zfMsg(MsgInfo mi) {
if (mi.isSL()) { // 私聊
// 发给对方。
// 根据用户名得到对方的RecvThread
RecvThread rt = clients.get(mi.getUser());
rt.sendMsg(mi);
System.out.println("sl.");
} else { // 群聊
System.out.println("群聊。" + clients.size());
List<String> onlines = new ArrayList<String>();
Set<String> keys = clients.keySet();
Iterator<String> itKeys = keys.iterator();
while (itKeys.hasNext()) {
String key = itKeys.next();
onlines.add(key);
}
mi.setOnlines(onlines);
mi.setClientId(this.s.toString());
// 转发消息给各个客户端
itKeys = keys.iterator();
while (itKeys.hasNext()) {
String key = itKeys.next();
RecvThread rt = clients.get(key);
if (mi.isMsg()) { // 发送消息时不转发给自己
if (rt != this) {
rt.sendMsg(mi);
}
}
else { // 获取在线用户列表。
rt.sendMsg(mi);
}
System.out.println("转发消息给 " + rt.user);
}
}
}
public void run() {
while (isConnected) {
try {
// 读取信息
MsgInfo mi = (MsgInfo) ois.readObject();
// 激活连接
if (!mi.isMsg()) { // 建立连接后发送一条空的信息,将用户名带到服务器。
clients.put(mi.getSender(), this);
user = mi.getSender();
System.out.println(mi.getSender() + " Join.");
}
// 转发给客户端
zfMsg(mi);
} catch (IOException e) {
// disconnect();
disConnect();
// e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
// 发送对象信息使用
public void disConnect() {
try {
System.out.println("Client " + user
+ " exit.");
isConnected = false;
if (ois != null)
ois.close();
if (oos != null)
oos.close();
if (s != null)
s.close();
clients.remove(this.user);
// 一个客户端退出后,通知其他客户端
MsgInfo mi = new MsgInfo();
zfMsg(mi);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* @param args
*/
public static void main(String[] args) {
new Server().start();
}
}
客户端:
package com.socket.tcp.basechat;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.Panel;
import java.awt.PopupMenu;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.ListSelectionModel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.TableModel;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
/**
* 实现群、私聊、发送文字、图片、文件。
* 文件支持单次同时发送7个,多线程接收。
* @author lucky star
*
*/
public class Main extends JFrame {
private JPanel contentPane;
private JTextPane sendPane = null;
private JTextPane recvPane = null;
private JList onlineList = null;
private JButton sendFileBtn = null, recvFileBtn = null;
// 发送和接收的进度条
// private JProgressBar sendProgressBar = null, recvProgressBa