package com.chat.client;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import com.chat.util.WindowTool;
/**
* 客户端登录窗口类, 用来验证登录和注册用户.
* @author 刘优
*
*/
public class ClientLogin extends JDialog implements ActionListener,KeyListener {
private static final long serialVersionUID = 1L;
private JTextField username; // 用户名输入框
private JPasswordField password; // 密码输入框
private BufferedReader theReader; // 从服务器读数据的对象
private PrintWriter theWriter; // 往服务器写数据的对象
private ChatClient client; // 此窗口所属的CLIENT对象
/**
* 初始化此窗口
* @param owner
* @param title
* @param modal
* @param theReader
* @param theWriter
* @throws HeadlessException
*/
public ClientLogin(ChatClient owner, String title, boolean modal,BufferedReader theReader,PrintWriter theWriter) throws HeadlessException {
super(owner, title, modal);
this.client = owner;
this.theReader = theReader;
this.theWriter = theWriter;
//初始化图形界面
setLayout(new BorderLayout());
JPanel main = new JPanel(new GridLayout(2,2));
JLabel title1 = new JLabel("登录信息 : ",JLabel.CENTER);
JLabel l1 = new JLabel(" 姓名 : ");
JLabel l2 = new JLabel(" 密码 : ");
username = new JTextField();
password = new JPasswordField();
username.addKeyListener(this);
password.addKeyListener(this);
JButton ok = new JButton("登录");
JButton register = new JButton("注册");
JButton cancel = new JButton("取消");
ok.setActionCommand("login");
register.setActionCommand("register");
cancel.setActionCommand("cancel");
ok.addActionListener(this);
register.addActionListener(this);
cancel.addActionListener(this);
main.add(l1);
main.add(username);
main.add(l2);
main.add(password);
JPanel buttonPane = new JPanel();
buttonPane.add(ok);
buttonPane.add(register);
buttonPane.add(cancel);
add(title1,"North");
add(main);
add(buttonPane,"South");
WindowTool.doView(this,230,130); // 居中显示
}
/**
* 验证用户
* @return String
*/
public int verify() {
return verify(username.getText(),new String(password.getPassword()));
}
/**
* 把登录信息发送到服务器端, 格式为login=用户名=密码
* @param username
* @param password
* @return
*/
private int verify(String username,String password) {
theWriter.println("login=" + username + "=" + password);
theWriter.flush();
String infor = null;
try {
infor = theReader.readLine();
while (!infor.startsWith("login="))
{
infor = theReader.readLine();
}
//System.out.println(infor);
} catch (IOException e) {
e.printStackTrace();
}
int rs = -1;
try {
rs = Integer.parseInt(infor.substring(6));
}
catch (Exception e) {e.printStackTrace();}
return rs;
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("login")) {
login();
}
else if (cmd.equals("cancel")) {
System.exit(0);
}
else if (cmd.equals("register")) {
register();
}
}
/**
* 注册新用户 格式为register=用户名=密码
*
*/
private void register() {
if (username.getText().length() == 0 || password.getPassword().length == 0) {
alarm("姓名和密码不能为空!");
return;
}
theWriter.println("register=" + username.getText() + "=" + new String(password.getPassword()));
theWriter.flush();
String infor = null;
try {
infor = theReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if (infor.startsWith("register=")) {
if (infor.endsWith("1")) {
alarm("注册新用户成功,请登录...");
return;
}
}
alarm("用户名已存在, 注册新用户失败,请重试...");
}
/**
* 登录服务器
*
*/
private void login() {
if (username.getText().length() == 0) {
alarm("姓名不能为空!");
return;
}
if (password.getPassword().length == 0) {
alarm("密码不能为空!");
return;
}
int verifyrs = verify();
if (verifyrs == 0) {
((ChatClient)this.getOwner()).start(username.getText()); // 用户登录成功!启动ChatClient对象
this.dispose(); // 把此窗口销毁
}
else {
String infor = null;
switch (verifyrs) {
case -1 :
infor = "未知错误";
break;
case 1 :
infor = "缺少用户名";
break;
case 2 :
infor = "缺少密码";
break;
case 3 :
infor = "用户已经登录";
break;
case 4 :
infor = "用户名错误";
break;
case 5 :
infor = "密码错误";
break;
}
alarm(infor); // 如果失败, 提示失败详细信息
}
}
private void alarm(String infor) {
client.alarm(infor,true);
}
public void keyTyped(KeyEvent e) {
}
/**
* 键盘监听响应
*/
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10) { // 响应回车, 输完用户名回车, 输入焦点转移到密码框上, 如果输完密码回车, 则直接登录
if (e.getSource() == username) {
password.requestFocus();
}
else {
login();
}
}
}
public void keyReleased(KeyEvent e) {
// 没啥用
}
}
package com.chat.client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Iterator;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import com.chat.util.TimeBean;
import com.chat.util.WindowTool;
/**
* 客户端JFRAME 主窗口
* @author 刘优
*
*/
public class ChatClient extends JFrame implements Runnable,ActionListener,KeyListener {
private static final long serialVersionUID = 1L;
private static final String AUTHOR = "作者:刘优 版本:1.0";
private ClientLogin login;
private JTextArea main; // 显示消息的主窗口
private JTextField input; // 用户输入栏
private JComboBox usersCombo; // 其他所有用户控件
private JScrollBar mainVerticalBar;
private JLabel timer; // 时间显示标签
private int width = 900; // 窗口宽度
private int height = 600; // 窗口高度
private JButton blackButton; // 黑名单按钮
private JButton friendButton; // 好友按钮
private JButton connectButton; // 连接按钮
private JDialog dialog; // 消息子窗口
private Container dialogPane;
private JLabel alarmLabel2; // 消息标签
private JLabel alarmLabel;
private JLabel myNameLabel;
private int dialogRs; // 消息窗口返回值
private JPanel sPane;
private JButton cl; // 消息窗口取消按钮
private String welcome;
private Socket socket; // 套接字
private String ip;
private int port;
private InputStream inputStream;
private OutputStream outputStream;
private BufferedReader theReader; // 字符流输入对象
private PrintWriter theWriter; // 字符流输出对象
private boolean read; // 客户端运行标志
private String myName; // 客户端用户名
private HashSet<String> allusers; // 其他所有用户名
private String toUser = "所有人"; // 发送悄悄话的用户名
private String toUser2 = "所有人";
private String sendFileName; // 要发送的文件名
private int sendFilePort; // 发送文件用的端口
private int bufferSize = 8193; // 发送文件时用的缓冲区大小
private HashSet<String> myfriends; // 我的好友列表
private boolean uiReady;
private boolean connected;
private static String encode = "UTF-8";
public ChatClient(String ip, int port) throws UnknownHostException, IOException {
super("ChatRoom - ");
this.ip = ip;
this.port = port;
createDialog();
connect();
}
private void createDialog() {
// 构建消息窗口, 但不显示, 如果有提示信息则显示.
// 为了使之反应更快
dialog = new JDialog(this,"消息窗口",true);
dialog.setSize(500,150);
dialog.setLocation(this.getX() + 100, this.getY() + 100);
dialog.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
dialog.setResizable(false);
dialogPane = dialog.getContentPane();
alarmLabel = new JLabel("信息",JLabel.CENTER);
dialog.setTitle("信息");
alarmLabel2 = new JLabel("",JLabel.CENTER);
alarmLabel2.setFont(new Font("宋体",Font.BOLD,16));
alarmLabel2.setForeground(Color.red);
dialogPane.add(alarmLabel,"North");
dialogPane.add(alarmLabel2);
JButton b = new JButton("确定");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
dialog.setVisible(false);
sPane.remove(cl);
}
});
sPane = new JPanel();
sPane.add(b);
cl = new JButton("取消");
cl.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
dialogRs = 1;
dialog.setVisible(false);
sPane.remove(cl);
}
});
dialogPane.add(sPane,"South");
}
/**
* 连接服务器, 需要提供IP和端口.
* @throws UnknownHostException
* @throws IOException
*/
private void connect() throws UnknownHostException, IOException {
try {
socket = new Socket(ip,port); // 连接服务器
}
catch (Exception e) {
alarm("服务器未准备就绪!",true); // 后面的TRUE表明提示信息很严重
return;
}
inputStream = socket.getInputStream(); // 获取输入流
outputStream = socket.getOutputStream(); // 获取写服务器的输出流
theReader = new BufferedReader(new InputStreamReader(inputStream,encode)); //包装一下
theWriter = new PrintWriter(new OutputStreamWriter(outputStream,encode),true); //包装一下
welcome = theReader.readLine(); // 读取欢迎信息, 不显示, 没啥用TERM端会有用
welcome = theReader.readLine();
theWriter.println("client=window"); //告诉服务器, 客户端类型为window
theWriter.flush();
// 启动验证登录窗口
login = new ClientLogin(this,"登录",true,theReader,theWriter);
}
private void uiInit() {
uiReady = true;
main = new JTextArea();
main.setEditable(false); // 不允许编辑主窗口
input = new JTextField(30);
input.setFont(new Font("宋体",Font.PLAIN,14));
input.addKeyListener(this);
JScrollPane scroll1 = new JScrollPane(main);
mainVerticalBar = scroll1.getVerticalScrollBar();
JPanel northPane = new JPanel();
JLabel title = new JLabel("欢迎使用");
JLabel timeInherit = new JLabel("系统当前时间 : ");
timer = new JLabel();
TimeBean.setLabel(timer);
northPane.add(title);
northPane.add(timeInherit);
northPane.add(timer);
northPane.add(new JLabel(" 日期 : " + TimeBean.getDate())); // 获取当前时间并显示
JPanel southPane = new JPanel();
usersCombo = new JComboBox(); // 所有用户列表
usersCombo.addActionListener(this);
JButton send = new JButton("消息");
JButton sendfile = new JButton("文件");
friendButton = new JButton("好友");
blackButton = new JButton("屏蔽");
connectButton = new JButton("断开");
sendfile.addActionListener(this);
send.addActionListener(this);
connectButton.addActionListener(this);
friendButton.addActionListener(this);
blackButton.addActionListener(this);
myNameLabel = new JLabel();
southPane.add(myNameLabel);
southPane.add(usersCombo);
southPane.add(input);
southPane.add(send);
southPane.add(sendfile);
southPane.add(friendButton);
southPane.add(blackButton);
southPane.add(connectButton);
this.add(northPane,"North");
this.add(scroll1);
this.add(southPane,"South");
WindowTool.doView(this,width,height);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
close(false,true);
}
});
}
/**
* 用户登录成功开始主方法
* @param username
*/
public void start(String username) {
if (login != null) {
login.dispose();
}
allusers = new HashSet<String>();
myfriends = new HashSet<String>();
myName = username;
if (!uiReady) {
uiInit();
}
setTitle("ChatRoom - " + username + " " + AUTHOR); // 设置标题
myNameLabel.setText("[" + username + "]对:"); // 显示我是谁
connectButton.setText("断开"); // 设置按纽文本
sendFilePort = 9999; // 文件发送和接收端口
work(); // 开始工作啦
}
private void work() {
Thread readThread = new Thread(this); // 新建一个线程来工作
read = true;
readThread.start();
}
/**
* 显示警告信息
* @param infor
*/
public void alarm(String infor) {
dialogRs = 0;
alarm(infor,false);
}
public void alarm(String infor,boolean serious) {
if (serious) {
dialog.setTitle("错误!!");
alarmLabel = new JLabel("发生错误 : ",JLabel.CENTER);
}
alarmLabel2.setText(infor);
WindowTool.doView(dialog,350,110);
}
/**
* 发送INPUT输入的文本
*
*/
private void send() {
if (theWriter == null) {
main.append("服务器的连接已断开!!\n");
alarm("服务器的连接已断开",true);
return;
}
if (input.getText().length() > 0) {
String infor = input.getText();
if (!toUser.equals("所有人")) {
infor = "to=" + toUser + "=" + infor; // 发送悄悄话
}
theWriter.println(infor);
theWriter.flush();
input.setText("");
if (infor.equals("exit")) { // 如果是exit指令则退出系统
close(false,true);
}
}
else {
main.append("您还没输入呢\n"); // 提示用户输入后再发送
mainVerticalBar.setValue(mainVerticalBar.getMaximum()); // 设置主窗口,使之总是能显示最后一行 达到刷新效果
}
}
/**
* 处理按纽事件, 根据上面的文本来响应
*/
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("消息")) {
send();
}
else if (cmd.equals("文件")) {
sendFile();
}
else if (cmd.equals("屏蔽")) {
toBlackList();
}
else if (cmd.equals("解禁")) {
unBlackUser();
}
else if (cmd.equals("好友")) {
toFriendList();
}
else if (cmd.equals("陌生")) {
unFriendUser();
}
else if (cmd.equals("断开")) {
disconnect();
}
else if (cmd.equals("连接")) {
try {
connect();
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
else {
// 处理按钮的文本
Object obj = usersCombo.getSelectedItem();
if (obj != null) {
toUser = obj.toString();
if (!toUser.equals("所有人")) {
if (toUser.startsWith("好友-")) {
toUser = toUser.substring(3);
}
toUser2 = toUser;
if (theWriter == null) return;
theWriter.println("isblack=" + toUser);
theWriter.flush();
for (String user : myfriends) {
if (user.equals(toUser)) {
friendButton.setText("陌生");
break;
}
}
}
else {
blackButton.setText("屏蔽");
friendButton.setText("好友");
}
}
}
input.requestFocus();
}
/**
* 把用户设为好友
*
*/
private void toFriendList() {
if (theWriter == null) {
disconnectAlarm();
return;
}
if (toUser.equals("所有人")) {
alarm("请选择目标用户");
return;
}
theWriter.println("setfriend=" + toUser); // 告诉服务器, 我要把toUser加为发友
theWriter.flush();
}
private void unFriendUser() {
if (theWriter == null) {
disconnectAlarm();
return;
}
if (toUser.equals("所有人")) {
alarm("请选择目标用户");
return;
}
theWriter.println("unfriend=" + toUser); // 告诉服务器, 我要把toUser从好友中删除
theWriter.flush();
}
private void disconnectAlarm() {
main.append("服务器的连接已断开!!\n");
alarm("服务器的连接已断开!!",true);
}
private void unBlackUser() {
if (theWriter == null) {
disconnectAlarm();
return;
}
if (toUser.equals("所有人")) {
alarm("请选择目标用户");
return;
}
theWriter.println("unblack=" + toUser); // 告诉服务器, 要把toUser从黑名单中删除
theWriter.flush();
}
private void toBlackList() {
if (theWriter == null) {
disconnectAlarm();
return;
}
if (toUser.equals("所有人")) {
alarm("请选择目标用户");
return;
}
theWriter.println("black=" + toUser); // 告诉服务器, 要把toUser添加为黑名单
theWriter.flush();
}
/**
* 给其他用户发送文件 格式为sendfile=filename=myname=tousername
*
*/
private void sendFile() {
if (theWriter == null) {
disconnectAlarm();
return;
}
if (toUser.equals("所有人")) {
alarm("请选择目标用户");
return;
}
JFileChooser fileChooser = new JFileChooser();
int returnVal = fileChooser.showOpenDialog(this);
if(returnVal == JFileChooser.APPROVE_OPTION) {
File f = fileChooser.getSelectedFile();
if (!f.canRead()) {
alarm("此文件不可读!"); // 文件不能读, 不能发
return;
}
if (f.length() == 0) {
alarm("文件大小为零"); // 文件大小为0 发也没意思
return;
}
sendFileName = f.getPath();
}
if (sendFileName == null) return;
theWriter.println("sendfile=" + sendFileName.substring(sendFileName.lastIndexOf(File.separator) + 1) + "=" + myName + "=" + toUser);
theWriter.flush();
}
/**
* 主要的处理方法, 读取并处理服务器发送回来的指令
*/
public void run() {
while (read) {
String infor = null;
try {
if (theReader != null) {
infor = theReader.readLine();
if (infor == null || theReader == null) {
close(true,false);
}
if (infor.startsWith("myfriends=")) { // 获取我的好友名单
myfriends.clear();
String s = infor.substring(10).trim();
String[] thenames = s.split(",");
if (s.length() < 2) {
continue;
}
for (int j = 0; j != thenames.length; ++j) {
myfriends.add(thenames[j]);
}
}
else if (infor.startsWith("allusers=")) { // 获取所有用户名
allusers.clear();
String str = infor.substring(9);
String [] allUserArray = str.split(",");
for (int i = 0; i != allUserArray.length; ++i) {
allusers.add(allUserArray[i]);
}
}
else if (infor.startsWith("newlogin=")) { // 新用户登录了
String newer = infor.substring(9);
allusers.add(newer);
handleUsers();
infor = "系统\t[公共] : 用户 : " + newer + " 进入了聊天室";
}
else if (infor.startsWith("logout=")) { // 有用户推出聊天室
String username = infor.substring(7);
if (username.equals(myName)) {
alarm("断开了服务器的连接!!!",true);
main.append("断开了服务器的连接!!!\n");
close(true,false);
break;
}
allusers.remove(username.trim());
handleUsers();
infor = "系统\t[公共] : 用户 : " + username + " 离开了聊天室";
}
else if (infor.startsWith("sendfile=")) { // 有用户给我发送文件
String [] infors = infor.split("=");
String fromUser = infors[2];
String toUser = infors[3];
String filename = infors[1];
sPane.add(cl);
alarm("用户 : " + fromUser + " 想给您发送文件 \"" + filename + "\" 您是否原意接收?");
if (dialogRs == 0) {
Thread recFileThread = null;
try {
recFileThread = new RecFileThread();
recFileThread.start();
} catch (Exception e) {
dialogRs = 1;
e.printStackTrace();
}
}
theWriter.println("recfile=" + filename + "=" + fromUser + "=" + toUser + "=" + dialogRs);
theWriter.flush();
}
else if (infor.startsWith("recfile=")) { // 我给别人发送文件的响应
String [] infors = infor.split("=");
String toUser = infors[3];
String rs = infors[4];
String ip = infors[5].substring(1);
if (rs.equals("0")) {
alarm("用户 : " + toUser + " 同意接收您的文件...");
Thread sendFile = new SendFileThread(ip);
sendFile.start();
}
else {
alarm("用户 : " + toUser + " 拒绝接收您的文件...");
}
}
else if (infor.startsWith("black=")) { // 我被别人踢到黑名单了
String other = infor.substring(6);
alarm("用户 : " + other + " 把您放入了黑名单");
}
else if (infor.startsWith("blacksuccess=")) { // 我把某人踢到黑名单成功
String other = infor.substring(13);
alarm("把用户 : " + other + "添加到黑名单成功!");
blackButton.setText("解禁");
}
else if (infor.startsWith("blacksuccess2=")) {
String other = infor.substring(14);
alarm("用户 : " + other + "已经在黑名单中了!");
}
else if (infor.startsWith("blackfile=")) {
String other = infor.substring(10);
alarm("用户 : " + other + " 把您放入了黑名单! 无法发送文件!");
}
else if (infor.startsWith("isblack=")) {
String rs = infor.substring(8);
if (rs.equals("1")) {
blackButton.setText("解禁");
}
else {
blackButton.setText("屏蔽");
}
}
else if (infor.startsWith("unblack=")) { // 把用户从黑名单中删除
String thename = infor.substring(8);
alarm("用户 : " + thename + " 解禁成功!");
blackButton.setText("屏蔽");
}
else if (infor.startsWith("beblacked=")) { // 我被别人加入了黑名单:(
String thename = infor.substring(10);
alarm("用户 : " + thename + " 把您放入了黑名单中!");
}
else if (infor.startsWith("unblacked=")) { // 别人把我从黑名单中删除了:)
String thename = infor.substring(10);
alarm("用户 : " + thename + " 把您从黑名单中清除!");
}
else if (infor.startsWith("setfriend=")) { // 别人把我加为好友了
String thename = infor.substring(10);
alarm("用户 : " + thename + " 已被您添加为好友!");
myfriends.add(thename);
handleUsers();
}
else if (infor.startsWith("setfriended=")) { // 我要把别人加为好友
String thename = infor.substring(12);
alarm("用户 : " + thename + " 把您添加为好友了!");
handleUsers();
}
else if (infor.startsWith("unfriend=")) { // 我要把别人从好友中删除
String thename = infor.substring(9);
myfriends.remove(thename);
alarm("用户 : " + thename + " 已经从好友中删除了!");
handleUsers();
}
else if (infor.startsWith("unfriended=")) { // 别人把我从好友中删除了:(
String thename = infor.substring(11);
alarm("用户 : " + thename + " 把您从好友中删除了!");
handleUsers();
}
if (infor.indexOf("=") != -1) { // 所有的服务器发送的指令都包含=号, 不显示之
continue;
}
}
else {
alarm("服务器关闭,程序退出!",true);
}
} catch (IOException e) {
e.printStackTrace();
}
main.append(infor + "\t\t[" + TimeBean.getTime() + "]\n"); // 显示新消息(别人的悄悄话, 或是公共消息)
mainVerticalBar.setValue(mainVerticalBar.getMaximum()); // 自动调整主窗口的显示信息, 刷新位置
}
}
/**
* 设置用户名控件,使之总是与服务器同步
*
*/
private void handleUsers() {
usersCombo.removeAllItems();
usersCombo.addItem("所有人");
String toSelect = toUser2;
Iterator<String> iter = allusers.iterator();
while (iter.hasNext()) {
String one = iter.next();
Iterator iter2 = myfriends.iterator();
while (iter2.hasNext()) {
if (one.equals(iter2.next())) {
usersCombo.addItem("好友-" + one);
if (one.equals(toUser2)) {
toSelect = "好友-" + one;
}
break;
}
}
}
iter = allusers.iterator();
outfor :
while (iter.hasNext()) {
String one = iter.next();
Iterator iter2 = myfriends.iterator();
while (iter2.hasNext()) {
if (one.equals(iter2.next())) {
continue outfor;
}
}
if (one.equals(myName)) continue;
usersCombo.addItem(one);
}
usersCombo.setSelectedItem(toSelect);
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
/**
* 断开连接
*
*/
private void disconnect() {
close(false,false);
}
/**
* 退出程序
*
*/
private void close(boolean fromService,boolean toExit) {
if (!fromService) {
if (theWriter != null) {
theWriter.println("exit");
theWriter.flush();
}
}
read = false;
if (theWriter != null) {
theWriter.close();
theWriter = null;
}
if (theReader != null) {
try {
theReader.close();
} catch (IOException e) {
e.printStackTrace();
}
theReader = null;
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
connected = false;
connectButton.setText("连接");
if (toExit) {
System.exit(0); // 退出程序
}
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10) { // 响应回车
send();
}
}
public void keyReleased(KeyEvent e) {
}
public void focusGained(FocusEvent e) {
}
public void focusLost(FocusEvent e) {
input.requestFocus();
}
/**
* 发送文件线程....... 不会影响主程序的运行
*/
class SendFileThread extends Thread {
String toUserIp; // 对方IP
FileInputStream fis = null; // 文件输入流
boolean send = true; // 发送文件状态标志,如果为FALSE则取消发送
JDialog dialog; // 进度显示窗口
JProgressBar theBar; // 文件传输进度显示控件
JPanel mainPane;
JLabel percentLabel = new JLabel(); // 文件传输百分比标签
JLabel bytesSendLabel = new JLabel(); // 传输字节标签
JLabel totalBytesLabel = new JLabel(); // 文件大小标签
JButton ok; // 确定按钮
JButton cancel; // 取消按钮
public SendFileThread(String toUserIp) {
super();
this.toUserIp = toUserIp;
try {
fis = new FileInputStream(sendFileName); // 文件输入流新建好了
} catch (FileNotFoundException e) {
e.printStackTrace();
}
dialog = new JDialog(ChatClient.this,"文件传输进度"); // 进度显示窗口就绪
dialog.setLayout(new BorderLayout());
mainPane = new JPanel();
JPanel northPane = new JPanel(new GridLayout(1,3));
northPane.add(percentLabel); // 百分比标签
northPane.add(bytesSendLabel); // 已发送字节标签
northPane.add(totalBytesLabel); // 文件总大小标签
dialog.add(northPane,"North"); // 设置窗口北边控件
JPanel southPane = new JPanel();
ok = new JButton("确定");
ok.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
dialog.dispose();// 如果文件发送完毕,点击确定关闭此窗口
}
});
southPane.add(ok);
ok.setEnabled(false);
cancel = new JButton("取消");
cancel.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) { // 在发送过程中, 随时可以取消文件发送
send = false;
alarm("您已经取消了文件发送任务!");
}
});
southPane.add(cancel);
dialog.add(southPane,"South");
}
/**
* 文件发送线程
*/
public void run() {
OutputStream out = null;
Socket fileSocket = null;
long fileLeng = -1;
long theBytes = 0;
try {
// 建立SOCKET连接
fileSocket = new Socket(toUserIp,sendFilePort); // 取得连接
out = fileSocket.getOutputStream(); // 获取socket输出流对象,准备往这里写数据
byte[] buffer = new byte[bufferSize]; // 创建一个缓冲区, 发送文件更快
int bytesRead = 0; // 准备变量, 保存每次读取的字节数
fileLeng = new File(sendFileName).length(); // 取得要发送的文件的字节数
totalBytesLabel.setText("/" + fileLeng + ")字节"); // 设置总字节数标签
byte[] lengByte = (fileLeng + "").getBytes(); // 把文件长度信息告诉文件接收方.....
out.write(lengByte.length);
out.write(lengByte);
theBar = new JProgressBar(0,(int)(fileLeng/1024)); // 创建进度条.最大值为文件的总量/1024
theBar.setValue(0);
percentLabel.setText(("0%"));
dialog.add(theBar);
WindowTool.doView(dialog,350,100); // 居中显示此进度显示
// 开始发送文件
while (send) {
bytesRead = fis.read(buffer,0,buffer.length); // 读取文件输入流
if (bytesRead <= 0) { // 文件发送完毕,则退出循环
break;
}
// 设置显示信息
theBytes += bytesRead; // 累计当前已处理的字节数
theBar.setValue((int)(theBytes / 1024)); // 处理进度条 使之正确显示
bytesSendLabel.setText("(" + theBytes);
percentLabel.setText((int)(((double)theBytes / fileLeng) * 100) + "%");// 显示百分比
out.write(buffer,0,bytesRead); // 写入SOCKET输出流...........
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
if (out != null) {
try {
out.close(); // 关闭输出流
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileSocket != null) {
try {
fileSocket.close(); // 关闭socket对象
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close(); // 关闭文件输入流对象
} catch (IOException e) {
e.printStackTrace();
}
}
}
ok.setEnabled(true); // 可以点击确定按纽了
cancel.setEnabled(false);
if (theBytes == fileLeng) {
alarm("文件" + sendFileName + "传输完毕!");
}
else if (send){
alarm("文件接收方中断了接收 !");
}
sendFileName = null;
}
}
/**
* 文件接收线程类
* 类似文件发送线程类
* @author 刘优
*
*/
class RecFileThread extends Thread {
FileOutputStream fos = null; // 文件输出流
boolean recv = true;
JDialog dialog; // 进度显示窗口
JProgressBar theBar; // 进度条
JPanel mainPane;
JLabel percentLabel = new JLabel(); // 百分比标签
JLabel bytesRecvLabel = new JLabel(); // 字节数标签
JLabel totalBytesLabel = new JLabel(); // 总字节数标签
JButton ok; // 确定按纽
JButton cancel; // 取消按纽
File fileToSave; // 要接收保存的文件对象
public RecFileThread() throws Exception {
JFileChooser fileChooser = new JFileChooser();
fileChooser.showSaveDialog(ChatClient.this);
fileToSave = fileChooser.getSelectedFile(); // 取得要保存的文件对象
// 判断写权限
if (!fileToSave.getParentFile().canWrite() && !fileToSave.canWrite()) {
alarm("不能在目录 [" + fileToSave.getParent() + "] 中创建文件");
throw new Exception("没有目录:[" + fileToSave.getParent() + "]的写权限");
}
try {
fos = new FileOutputStream(fileToSave); // 取得文件输出流
} catch (FileNotFoundException e) {
e.printStackTrace();
}
dialog = new JDialog(ChatClient.this,"文件接收进度"); // 进度窗口
dialog.setLayout(new BorderLayout());
JPanel northPane = new JPanel(new GridLayout(1,3));
northPane.add(percentLabel);
northPane.add(bytesRecvLabel);
northPane.add(totalBytesLabel);
dialog.add(northPane,"North");
JPanel southPane = new JPanel();
ok = new JButton("确定");
ok.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
dialog.dispose();
}
});
southPane.add(ok);
ok.setEnabled(false);
cancel = new JButton("取消");
cancel.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
recv = false;
alarm("您已经取消了文件接收任务!");
}
});
southPane.add(cancel);
dialog.add(southPane,"South");
}
/**
* 文件接收线程方法
*/
public void run() {
ServerSocket ss = null;
Socket fileSocket = null;
InputStream in = null;
long fileLeng = 0;
long theBytes = 0;
try {
// 创建服务器端
ss = new ServerSocket(sendFilePort);
// 等待发送方连接
fileSocket = ss.accept();
in = fileSocket.getInputStream(); // 取得socket输入流
byte[] buffer = new byte[bufferSize]; // 缓冲区
int bytesRead = 0;
int leng = in.read();
in.read(buffer,0,leng);
String s = new String(buffer,0,leng);
fileLeng = Long.parseLong(s); // 先取得文件的总大小
totalBytesLabel.setText("/" + s + ")字节");
theBar = new JProgressBar(0,(int)(fileLeng/1024));
theBar.setValue(0);
percentLabel.setText(("0%"));
dialog.add(theBar);
WindowTool.doView(dialog,350,100); // 居中显示窗口
// 开始接收文件
while (recv) {
bytesRead = in.read(buffer,0,buffer.length); // 读取SOCKET输入流
if (bytesRead <= 0) {
break;
}
// 设置显示信息
theBytes += bytesRead;
theBar.setValue((int)(theBytes / 1024)); // 设置显示的进度条
bytesRecvLabel.setText(" ( " + theBytes);
percentLabel.setText((int)(((double)theBytes / fileLeng) * 100) + "%");//设置百分比标签
fos.write(buffer,0,bytesRead); // 写入文件输出流
}
} catch (IOException e) {
e.printStackTrace();
}
finally {
// 关闭资源
if (in != null) {
try {
in.close(); //关闭socket输入流对象
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close(); // 关闭文件输出流对象
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileSocket != null) {
try {
fileSocket.close(); // 关闭socket对象
} catch (IOException e) {
e.printStackTrace();
}
}
if (ss != null) {
try {
ss.close(); // 关闭文件接收服务
} catch (IOException e) {
e.printStackTrace();
}
}
}
ok.setEnabled(true);
cancel.setEnabled(false);
if (theBytes == fileLeng) {
alarm("文件接收完毕,文件保存为:" + fileToSave.getPath()+ " !");
return;
}
else if (recv){
alarm("文件接收被发送方中断 !");
}
fileToSave.delete();// 如果是非正常中断,则把已经接收的片断文件删除..........
sendFileName = null;
}
}
}
package com.chat.main;
import java.io.IOException;
import java.net.*;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import com.chat.client.ChatClient;
/**
* 客户端启动主程序
* @author 刘优
*
*/
public class ClientMain {
public static void main(String[] args) {
try {
String ip = getMyIp();
//System.out.println(ip);
int port = 8888;
if (args.length > 0)
{
ip = args[0];
if (args.length > 1)
{
try {
port = Integer.parseInt(args[1]);
}
catch (Exception e) {
System.out.println("port number : " + args[1] + " is not a integer!");
}
}
}
ChatClient client = new ChatClient(ip,port); // IP 和 端口
} catch (Exception ee) {
ee.printStackTrace();
}
}
private static String getMyIp() throws Exception
{
Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
InetAddress ip = null;
while (allNetInterfaces.hasMoreElements())
{
NetworkInterface netInterface = (NetworkInterface)allNetInterfaces.nextElement();
//System.out.println(netInterface.getName());
Enumeration addresses = netInterface.getInetAddresses();
while (addresses.hasMoreElements())
{
ip = (InetAddress)addresses.nextElement();
if (ip != null && ip instanceof Inet4Address && !(ip.getHostAddress().startsWith("127.")))
{
//System.out.println("本机的IP = " + ip.getHostAddress());
break;
}
}
}
return ip.getHostAddress();
}
}
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import com.chat.util.WindowTool;
/**
* 客户端登录窗口类, 用来验证登录和注册用户.
* @author 刘优
*
*/
public class ClientLogin extends JDialog implements ActionListener,KeyListener {
private static final long serialVersionUID = 1L;
private JTextField username; // 用户名输入框
private JPasswordField password; // 密码输入框
private BufferedReader theReader; // 从服务器读数据的对象
private PrintWriter theWriter; // 往服务器写数据的对象
private ChatClient client; // 此窗口所属的CLIENT对象
/**
* 初始化此窗口
* @param owner
* @param title
* @param modal
* @param theReader
* @param theWriter
* @throws HeadlessException
*/
public ClientLogin(ChatClient owner, String title, boolean modal,BufferedReader theReader,PrintWriter theWriter) throws HeadlessException {
super(owner, title, modal);
this.client = owner;
this.theReader = theReader;
this.theWriter = theWriter;
//初始化图形界面
setLayout(new BorderLayout());
JPanel main = new JPanel(new GridLayout(2,2));
JLabel title1 = new JLabel("登录信息 : ",JLabel.CENTER);
JLabel l1 = new JLabel(" 姓名 : ");
JLabel l2 = new JLabel(" 密码 : ");
username = new JTextField();
password = new JPasswordField();
username.addKeyListener(this);
password.addKeyListener(this);
JButton ok = new JButton("登录");
JButton register = new JButton("注册");
JButton cancel = new JButton("取消");
ok.setActionCommand("login");
register.setActionCommand("register");
cancel.setActionCommand("cancel");
ok.addActionListener(this);
register.addActionListener(this);
cancel.addActionListener(this);
main.add(l1);
main.add(username);
main.add(l2);
main.add(password);
JPanel buttonPane = new JPanel();
buttonPane.add(ok);
buttonPane.add(register);
buttonPane.add(cancel);
add(title1,"North");
add(main);
add(buttonPane,"South");
WindowTool.doView(this,230,130); // 居中显示
}
/**
* 验证用户
* @return String
*/
public int verify() {
return verify(username.getText(),new String(password.getPassword()));
}
/**
* 把登录信息发送到服务器端, 格式为login=用户名=密码
* @param username
* @param password
* @return
*/
private int verify(String username,String password) {
theWriter.println("login=" + username + "=" + password);
theWriter.flush();
String infor = null;
try {
infor = theReader.readLine();
while (!infor.startsWith("login="))
{
infor = theReader.readLine();
}
//System.out.println(infor);
} catch (IOException e) {
e.printStackTrace();
}
int rs = -1;
try {
rs = Integer.parseInt(infor.substring(6));
}
catch (Exception e) {e.printStackTrace();}
return rs;
}
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("login")) {
login();
}
else if (cmd.equals("cancel")) {
System.exit(0);
}
else if (cmd.equals("register")) {
register();
}
}
/**
* 注册新用户 格式为register=用户名=密码
*
*/
private void register() {
if (username.getText().length() == 0 || password.getPassword().length == 0) {
alarm("姓名和密码不能为空!");
return;
}
theWriter.println("register=" + username.getText() + "=" + new String(password.getPassword()));
theWriter.flush();
String infor = null;
try {
infor = theReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
if (infor.startsWith("register=")) {
if (infor.endsWith("1")) {
alarm("注册新用户成功,请登录...");
return;
}
}
alarm("用户名已存在, 注册新用户失败,请重试...");
}
/**
* 登录服务器
*
*/
private void login() {
if (username.getText().length() == 0) {
alarm("姓名不能为空!");
return;
}
if (password.getPassword().length == 0) {
alarm("密码不能为空!");
return;
}
int verifyrs = verify();
if (verifyrs == 0) {
((ChatClient)this.getOwner()).start(username.getText()); // 用户登录成功!启动ChatClient对象
this.dispose(); // 把此窗口销毁
}
else {
String infor = null;
switch (verifyrs) {
case -1 :
infor = "未知错误";
break;
case 1 :
infor = "缺少用户名";
break;
case 2 :
infor = "缺少密码";
break;
case 3 :
infor = "用户已经登录";
break;
case 4 :
infor = "用户名错误";
break;
case 5 :
infor = "密码错误";
break;
}
alarm(infor); // 如果失败, 提示失败详细信息
}
}
private void alarm(String infor) {
client.alarm(infor,true);
}
public void keyTyped(KeyEvent e) {
}
/**
* 键盘监听响应
*/
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10) { // 响应回车, 输完用户名回车, 输入焦点转移到密码框上, 如果输完密码回车, 则直接登录
if (e.getSource() == username) {
password.requestFocus();
}
else {
login();
}
}
}
public void keyReleased(KeyEvent e) {
// 没啥用
}
}
package com.chat.client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Iterator;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import com.chat.util.TimeBean;
import com.chat.util.WindowTool;
/**
* 客户端JFRAME 主窗口
* @author 刘优
*
*/
public class ChatClient extends JFrame implements Runnable,ActionListener,KeyListener {
private static final long serialVersionUID = 1L;
private static final String AUTHOR = "作者:刘优 版本:1.0";
private ClientLogin login;
private JTextArea main; // 显示消息的主窗口
private JTextField input; // 用户输入栏
private JComboBox usersCombo; // 其他所有用户控件
private JScrollBar mainVerticalBar;
private JLabel timer; // 时间显示标签
private int width = 900; // 窗口宽度
private int height = 600; // 窗口高度
private JButton blackButton; // 黑名单按钮
private JButton friendButton; // 好友按钮
private JButton connectButton; // 连接按钮
private JDialog dialog; // 消息子窗口
private Container dialogPane;
private JLabel alarmLabel2; // 消息标签
private JLabel alarmLabel;
private JLabel myNameLabel;
private int dialogRs; // 消息窗口返回值
private JPanel sPane;
private JButton cl; // 消息窗口取消按钮
private String welcome;
private Socket socket; // 套接字
private String ip;
private int port;
private InputStream inputStream;
private OutputStream outputStream;
private BufferedReader theReader; // 字符流输入对象
private PrintWriter theWriter; // 字符流输出对象
private boolean read; // 客户端运行标志
private String myName; // 客户端用户名
private HashSet<String> allusers; // 其他所有用户名
private String toUser = "所有人"; // 发送悄悄话的用户名
private String toUser2 = "所有人";
private String sendFileName; // 要发送的文件名
private int sendFilePort; // 发送文件用的端口
private int bufferSize = 8193; // 发送文件时用的缓冲区大小
private HashSet<String> myfriends; // 我的好友列表
private boolean uiReady;
private boolean connected;
private static String encode = "UTF-8";
public ChatClient(String ip, int port) throws UnknownHostException, IOException {
super("ChatRoom - ");
this.ip = ip;
this.port = port;
createDialog();
connect();
}
private void createDialog() {
// 构建消息窗口, 但不显示, 如果有提示信息则显示.
// 为了使之反应更快
dialog = new JDialog(this,"消息窗口",true);
dialog.setSize(500,150);
dialog.setLocation(this.getX() + 100, this.getY() + 100);
dialog.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
dialog.setResizable(false);
dialogPane = dialog.getContentPane();
alarmLabel = new JLabel("信息",JLabel.CENTER);
dialog.setTitle("信息");
alarmLabel2 = new JLabel("",JLabel.CENTER);
alarmLabel2.setFont(new Font("宋体",Font.BOLD,16));
alarmLabel2.setForeground(Color.red);
dialogPane.add(alarmLabel,"North");
dialogPane.add(alarmLabel2);
JButton b = new JButton("确定");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
dialog.setVisible(false);
sPane.remove(cl);
}
});
sPane = new JPanel();
sPane.add(b);
cl = new JButton("取消");
cl.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
dialogRs = 1;
dialog.setVisible(false);
sPane.remove(cl);
}
});
dialogPane.add(sPane,"South");
}
/**
* 连接服务器, 需要提供IP和端口.
* @throws UnknownHostException
* @throws IOException
*/
private void connect() throws UnknownHostException, IOException {
try {
socket = new Socket(ip,port); // 连接服务器
}
catch (Exception e) {
alarm("服务器未准备就绪!",true); // 后面的TRUE表明提示信息很严重
return;
}
inputStream = socket.getInputStream(); // 获取输入流
outputStream = socket.getOutputStream(); // 获取写服务器的输出流
theReader = new BufferedReader(new InputStreamReader(inputStream,encode)); //包装一下
theWriter = new PrintWriter(new OutputStreamWriter(outputStream,encode),true); //包装一下
welcome = theReader.readLine(); // 读取欢迎信息, 不显示, 没啥用TERM端会有用
welcome = theReader.readLine();
theWriter.println("client=window"); //告诉服务器, 客户端类型为window
theWriter.flush();
// 启动验证登录窗口
login = new ClientLogin(this,"登录",true,theReader,theWriter);
}
private void uiInit() {
uiReady = true;
main = new JTextArea();
main.setEditable(false); // 不允许编辑主窗口
input = new JTextField(30);
input.setFont(new Font("宋体",Font.PLAIN,14));
input.addKeyListener(this);
JScrollPane scroll1 = new JScrollPane(main);
mainVerticalBar = scroll1.getVerticalScrollBar();
JPanel northPane = new JPanel();
JLabel title = new JLabel("欢迎使用");
JLabel timeInherit = new JLabel("系统当前时间 : ");
timer = new JLabel();
TimeBean.setLabel(timer);
northPane.add(title);
northPane.add(timeInherit);
northPane.add(timer);
northPane.add(new JLabel(" 日期 : " + TimeBean.getDate())); // 获取当前时间并显示
JPanel southPane = new JPanel();
usersCombo = new JComboBox(); // 所有用户列表
usersCombo.addActionListener(this);
JButton send = new JButton("消息");
JButton sendfile = new JButton("文件");
friendButton = new JButton("好友");
blackButton = new JButton("屏蔽");
connectButton = new JButton("断开");
sendfile.addActionListener(this);
send.addActionListener(this);
connectButton.addActionListener(this);
friendButton.addActionListener(this);
blackButton.addActionListener(this);
myNameLabel = new JLabel();
southPane.add(myNameLabel);
southPane.add(usersCombo);
southPane.add(input);
southPane.add(send);
southPane.add(sendfile);
southPane.add(friendButton);
southPane.add(blackButton);
southPane.add(connectButton);
this.add(northPane,"North");
this.add(scroll1);
this.add(southPane,"South");
WindowTool.doView(this,width,height);
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
close(false,true);
}
});
}
/**
* 用户登录成功开始主方法
* @param username
*/
public void start(String username) {
if (login != null) {
login.dispose();
}
allusers = new HashSet<String>();
myfriends = new HashSet<String>();
myName = username;
if (!uiReady) {
uiInit();
}
setTitle("ChatRoom - " + username + " " + AUTHOR); // 设置标题
myNameLabel.setText("[" + username + "]对:"); // 显示我是谁
connectButton.setText("断开"); // 设置按纽文本
sendFilePort = 9999; // 文件发送和接收端口
work(); // 开始工作啦
}
private void work() {
Thread readThread = new Thread(this); // 新建一个线程来工作
read = true;
readThread.start();
}
/**
* 显示警告信息
* @param infor
*/
public void alarm(String infor) {
dialogRs = 0;
alarm(infor,false);
}
public void alarm(String infor,boolean serious) {
if (serious) {
dialog.setTitle("错误!!");
alarmLabel = new JLabel("发生错误 : ",JLabel.CENTER);
}
alarmLabel2.setText(infor);
WindowTool.doView(dialog,350,110);
}
/**
* 发送INPUT输入的文本
*
*/
private void send() {
if (theWriter == null) {
main.append("服务器的连接已断开!!\n");
alarm("服务器的连接已断开",true);
return;
}
if (input.getText().length() > 0) {
String infor = input.getText();
if (!toUser.equals("所有人")) {
infor = "to=" + toUser + "=" + infor; // 发送悄悄话
}
theWriter.println(infor);
theWriter.flush();
input.setText("");
if (infor.equals("exit")) { // 如果是exit指令则退出系统
close(false,true);
}
}
else {
main.append("您还没输入呢\n"); // 提示用户输入后再发送
mainVerticalBar.setValue(mainVerticalBar.getMaximum()); // 设置主窗口,使之总是能显示最后一行 达到刷新效果
}
}
/**
* 处理按纽事件, 根据上面的文本来响应
*/
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("消息")) {
send();
}
else if (cmd.equals("文件")) {
sendFile();
}
else if (cmd.equals("屏蔽")) {
toBlackList();
}
else if (cmd.equals("解禁")) {
unBlackUser();
}
else if (cmd.equals("好友")) {
toFriendList();
}
else if (cmd.equals("陌生")) {
unFriendUser();
}
else if (cmd.equals("断开")) {
disconnect();
}
else if (cmd.equals("连接")) {
try {
connect();
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
else {
// 处理按钮的文本
Object obj = usersCombo.getSelectedItem();
if (obj != null) {
toUser = obj.toString();
if (!toUser.equals("所有人")) {
if (toUser.startsWith("好友-")) {
toUser = toUser.substring(3);
}
toUser2 = toUser;
if (theWriter == null) return;
theWriter.println("isblack=" + toUser);
theWriter.flush();
for (String user : myfriends) {
if (user.equals(toUser)) {
friendButton.setText("陌生");
break;
}
}
}
else {
blackButton.setText("屏蔽");
friendButton.setText("好友");
}
}
}
input.requestFocus();
}
/**
* 把用户设为好友
*
*/
private void toFriendList() {
if (theWriter == null) {
disconnectAlarm();
return;
}
if (toUser.equals("所有人")) {
alarm("请选择目标用户");
return;
}
theWriter.println("setfriend=" + toUser); // 告诉服务器, 我要把toUser加为发友
theWriter.flush();
}
private void unFriendUser() {
if (theWriter == null) {
disconnectAlarm();
return;
}
if (toUser.equals("所有人")) {
alarm("请选择目标用户");
return;
}
theWriter.println("unfriend=" + toUser); // 告诉服务器, 我要把toUser从好友中删除
theWriter.flush();
}
private void disconnectAlarm() {
main.append("服务器的连接已断开!!\n");
alarm("服务器的连接已断开!!",true);
}
private void unBlackUser() {
if (theWriter == null) {
disconnectAlarm();
return;
}
if (toUser.equals("所有人")) {
alarm("请选择目标用户");
return;
}
theWriter.println("unblack=" + toUser); // 告诉服务器, 要把toUser从黑名单中删除
theWriter.flush();
}
private void toBlackList() {
if (theWriter == null) {
disconnectAlarm();
return;
}
if (toUser.equals("所有人")) {
alarm("请选择目标用户");
return;
}
theWriter.println("black=" + toUser); // 告诉服务器, 要把toUser添加为黑名单
theWriter.flush();
}
/**
* 给其他用户发送文件 格式为sendfile=filename=myname=tousername
*
*/
private void sendFile() {
if (theWriter == null) {
disconnectAlarm();
return;
}
if (toUser.equals("所有人")) {
alarm("请选择目标用户");
return;
}
JFileChooser fileChooser = new JFileChooser();
int returnVal = fileChooser.showOpenDialog(this);
if(returnVal == JFileChooser.APPROVE_OPTION) {
File f = fileChooser.getSelectedFile();
if (!f.canRead()) {
alarm("此文件不可读!"); // 文件不能读, 不能发
return;
}
if (f.length() == 0) {
alarm("文件大小为零"); // 文件大小为0 发也没意思
return;
}
sendFileName = f.getPath();
}
if (sendFileName == null) return;
theWriter.println("sendfile=" + sendFileName.substring(sendFileName.lastIndexOf(File.separator) + 1) + "=" + myName + "=" + toUser);
theWriter.flush();
}
/**
* 主要的处理方法, 读取并处理服务器发送回来的指令
*/
public void run() {
while (read) {
String infor = null;
try {
if (theReader != null) {
infor = theReader.readLine();
if (infor == null || theReader == null) {
close(true,false);
}
if (infor.startsWith("myfriends=")) { // 获取我的好友名单
myfriends.clear();
String s = infor.substring(10).trim();
String[] thenames = s.split(",");
if (s.length() < 2) {
continue;
}
for (int j = 0; j != thenames.length; ++j) {
myfriends.add(thenames[j]);
}
}
else if (infor.startsWith("allusers=")) { // 获取所有用户名
allusers.clear();
String str = infor.substring(9);
String [] allUserArray = str.split(",");
for (int i = 0; i != allUserArray.length; ++i) {
allusers.add(allUserArray[i]);
}
}
else if (infor.startsWith("newlogin=")) { // 新用户登录了
String newer = infor.substring(9);
allusers.add(newer);
handleUsers();
infor = "系统\t[公共] : 用户 : " + newer + " 进入了聊天室";
}
else if (infor.startsWith("logout=")) { // 有用户推出聊天室
String username = infor.substring(7);
if (username.equals(myName)) {
alarm("断开了服务器的连接!!!",true);
main.append("断开了服务器的连接!!!\n");
close(true,false);
break;
}
allusers.remove(username.trim());
handleUsers();
infor = "系统\t[公共] : 用户 : " + username + " 离开了聊天室";
}
else if (infor.startsWith("sendfile=")) { // 有用户给我发送文件
String [] infors = infor.split("=");
String fromUser = infors[2];
String toUser = infors[3];
String filename = infors[1];
sPane.add(cl);
alarm("用户 : " + fromUser + " 想给您发送文件 \"" + filename + "\" 您是否原意接收?");
if (dialogRs == 0) {
Thread recFileThread = null;
try {
recFileThread = new RecFileThread();
recFileThread.start();
} catch (Exception e) {
dialogRs = 1;
e.printStackTrace();
}
}
theWriter.println("recfile=" + filename + "=" + fromUser + "=" + toUser + "=" + dialogRs);
theWriter.flush();
}
else if (infor.startsWith("recfile=")) { // 我给别人发送文件的响应
String [] infors = infor.split("=");
String toUser = infors[3];
String rs = infors[4];
String ip = infors[5].substring(1);
if (rs.equals("0")) {
alarm("用户 : " + toUser + " 同意接收您的文件...");
Thread sendFile = new SendFileThread(ip);
sendFile.start();
}
else {
alarm("用户 : " + toUser + " 拒绝接收您的文件...");
}
}
else if (infor.startsWith("black=")) { // 我被别人踢到黑名单了
String other = infor.substring(6);
alarm("用户 : " + other + " 把您放入了黑名单");
}
else if (infor.startsWith("blacksuccess=")) { // 我把某人踢到黑名单成功
String other = infor.substring(13);
alarm("把用户 : " + other + "添加到黑名单成功!");
blackButton.setText("解禁");
}
else if (infor.startsWith("blacksuccess2=")) {
String other = infor.substring(14);
alarm("用户 : " + other + "已经在黑名单中了!");
}
else if (infor.startsWith("blackfile=")) {
String other = infor.substring(10);
alarm("用户 : " + other + " 把您放入了黑名单! 无法发送文件!");
}
else if (infor.startsWith("isblack=")) {
String rs = infor.substring(8);
if (rs.equals("1")) {
blackButton.setText("解禁");
}
else {
blackButton.setText("屏蔽");
}
}
else if (infor.startsWith("unblack=")) { // 把用户从黑名单中删除
String thename = infor.substring(8);
alarm("用户 : " + thename + " 解禁成功!");
blackButton.setText("屏蔽");
}
else if (infor.startsWith("beblacked=")) { // 我被别人加入了黑名单:(
String thename = infor.substring(10);
alarm("用户 : " + thename + " 把您放入了黑名单中!");
}
else if (infor.startsWith("unblacked=")) { // 别人把我从黑名单中删除了:)
String thename = infor.substring(10);
alarm("用户 : " + thename + " 把您从黑名单中清除!");
}
else if (infor.startsWith("setfriend=")) { // 别人把我加为好友了
String thename = infor.substring(10);
alarm("用户 : " + thename + " 已被您添加为好友!");
myfriends.add(thename);
handleUsers();
}
else if (infor.startsWith("setfriended=")) { // 我要把别人加为好友
String thename = infor.substring(12);
alarm("用户 : " + thename + " 把您添加为好友了!");
handleUsers();
}
else if (infor.startsWith("unfriend=")) { // 我要把别人从好友中删除
String thename = infor.substring(9);
myfriends.remove(thename);
alarm("用户 : " + thename + " 已经从好友中删除了!");
handleUsers();
}
else if (infor.startsWith("unfriended=")) { // 别人把我从好友中删除了:(
String thename = infor.substring(11);
alarm("用户 : " + thename + " 把您从好友中删除了!");
handleUsers();
}
if (infor.indexOf("=") != -1) { // 所有的服务器发送的指令都包含=号, 不显示之
continue;
}
}
else {
alarm("服务器关闭,程序退出!",true);
}
} catch (IOException e) {
e.printStackTrace();
}
main.append(infor + "\t\t[" + TimeBean.getTime() + "]\n"); // 显示新消息(别人的悄悄话, 或是公共消息)
mainVerticalBar.setValue(mainVerticalBar.getMaximum()); // 自动调整主窗口的显示信息, 刷新位置
}
}
/**
* 设置用户名控件,使之总是与服务器同步
*
*/
private void handleUsers() {
usersCombo.removeAllItems();
usersCombo.addItem("所有人");
String toSelect = toUser2;
Iterator<String> iter = allusers.iterator();
while (iter.hasNext()) {
String one = iter.next();
Iterator iter2 = myfriends.iterator();
while (iter2.hasNext()) {
if (one.equals(iter2.next())) {
usersCombo.addItem("好友-" + one);
if (one.equals(toUser2)) {
toSelect = "好友-" + one;
}
break;
}
}
}
iter = allusers.iterator();
outfor :
while (iter.hasNext()) {
String one = iter.next();
Iterator iter2 = myfriends.iterator();
while (iter2.hasNext()) {
if (one.equals(iter2.next())) {
continue outfor;
}
}
if (one.equals(myName)) continue;
usersCombo.addItem(one);
}
usersCombo.setSelectedItem(toSelect);
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
/**
* 断开连接
*
*/
private void disconnect() {
close(false,false);
}
/**
* 退出程序
*
*/
private void close(boolean fromService,boolean toExit) {
if (!fromService) {
if (theWriter != null) {
theWriter.println("exit");
theWriter.flush();
}
}
read = false;
if (theWriter != null) {
theWriter.close();
theWriter = null;
}
if (theReader != null) {
try {
theReader.close();
} catch (IOException e) {
e.printStackTrace();
}
theReader = null;
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
connected = false;
connectButton.setText("连接");
if (toExit) {
System.exit(0); // 退出程序
}
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 10) { // 响应回车
send();
}
}
public void keyReleased(KeyEvent e) {
}
public void focusGained(FocusEvent e) {
}
public void focusLost(FocusEvent e) {
input.requestFocus();
}
/**
* 发送文件线程....... 不会影响主程序的运行
*/
class SendFileThread extends Thread {
String toUserIp; // 对方IP
FileInputStream fis = null; // 文件输入流
boolean send = true; // 发送文件状态标志,如果为FALSE则取消发送
JDialog dialog; // 进度显示窗口
JProgressBar theBar; // 文件传输进度显示控件
JPanel mainPane;
JLabel percentLabel = new JLabel(); // 文件传输百分比标签
JLabel bytesSendLabel = new JLabel(); // 传输字节标签
JLabel totalBytesLabel = new JLabel(); // 文件大小标签
JButton ok; // 确定按钮
JButton cancel; // 取消按钮
public SendFileThread(String toUserIp) {
super();
this.toUserIp = toUserIp;
try {
fis = new FileInputStream(sendFileName); // 文件输入流新建好了
} catch (FileNotFoundException e) {
e.printStackTrace();
}
dialog = new JDialog(ChatClient.this,"文件传输进度"); // 进度显示窗口就绪
dialog.setLayout(new BorderLayout());
mainPane = new JPanel();
JPanel northPane = new JPanel(new GridLayout(1,3));
northPane.add(percentLabel); // 百分比标签
northPane.add(bytesSendLabel); // 已发送字节标签
northPane.add(totalBytesLabel); // 文件总大小标签
dialog.add(northPane,"North"); // 设置窗口北边控件
JPanel southPane = new JPanel();
ok = new JButton("确定");
ok.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
dialog.dispose();// 如果文件发送完毕,点击确定关闭此窗口
}
});
southPane.add(ok);
ok.setEnabled(false);
cancel = new JButton("取消");
cancel.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) { // 在发送过程中, 随时可以取消文件发送
send = false;
alarm("您已经取消了文件发送任务!");
}
});
southPane.add(cancel);
dialog.add(southPane,"South");
}
/**
* 文件发送线程
*/
public void run() {
OutputStream out = null;
Socket fileSocket = null;
long fileLeng = -1;
long theBytes = 0;
try {
// 建立SOCKET连接
fileSocket = new Socket(toUserIp,sendFilePort); // 取得连接
out = fileSocket.getOutputStream(); // 获取socket输出流对象,准备往这里写数据
byte[] buffer = new byte[bufferSize]; // 创建一个缓冲区, 发送文件更快
int bytesRead = 0; // 准备变量, 保存每次读取的字节数
fileLeng = new File(sendFileName).length(); // 取得要发送的文件的字节数
totalBytesLabel.setText("/" + fileLeng + ")字节"); // 设置总字节数标签
byte[] lengByte = (fileLeng + "").getBytes(); // 把文件长度信息告诉文件接收方.....
out.write(lengByte.length);
out.write(lengByte);
theBar = new JProgressBar(0,(int)(fileLeng/1024)); // 创建进度条.最大值为文件的总量/1024
theBar.setValue(0);
percentLabel.setText(("0%"));
dialog.add(theBar);
WindowTool.doView(dialog,350,100); // 居中显示此进度显示
// 开始发送文件
while (send) {
bytesRead = fis.read(buffer,0,buffer.length); // 读取文件输入流
if (bytesRead <= 0) { // 文件发送完毕,则退出循环
break;
}
// 设置显示信息
theBytes += bytesRead; // 累计当前已处理的字节数
theBar.setValue((int)(theBytes / 1024)); // 处理进度条 使之正确显示
bytesSendLabel.setText("(" + theBytes);
percentLabel.setText((int)(((double)theBytes / fileLeng) * 100) + "%");// 显示百分比
out.write(buffer,0,bytesRead); // 写入SOCKET输出流...........
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
if (out != null) {
try {
out.close(); // 关闭输出流
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileSocket != null) {
try {
fileSocket.close(); // 关闭socket对象
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close(); // 关闭文件输入流对象
} catch (IOException e) {
e.printStackTrace();
}
}
}
ok.setEnabled(true); // 可以点击确定按纽了
cancel.setEnabled(false);
if (theBytes == fileLeng) {
alarm("文件" + sendFileName + "传输完毕!");
}
else if (send){
alarm("文件接收方中断了接收 !");
}
sendFileName = null;
}
}
/**
* 文件接收线程类
* 类似文件发送线程类
* @author 刘优
*
*/
class RecFileThread extends Thread {
FileOutputStream fos = null; // 文件输出流
boolean recv = true;
JDialog dialog; // 进度显示窗口
JProgressBar theBar; // 进度条
JPanel mainPane;
JLabel percentLabel = new JLabel(); // 百分比标签
JLabel bytesRecvLabel = new JLabel(); // 字节数标签
JLabel totalBytesLabel = new JLabel(); // 总字节数标签
JButton ok; // 确定按纽
JButton cancel; // 取消按纽
File fileToSave; // 要接收保存的文件对象
public RecFileThread() throws Exception {
JFileChooser fileChooser = new JFileChooser();
fileChooser.showSaveDialog(ChatClient.this);
fileToSave = fileChooser.getSelectedFile(); // 取得要保存的文件对象
// 判断写权限
if (!fileToSave.getParentFile().canWrite() && !fileToSave.canWrite()) {
alarm("不能在目录 [" + fileToSave.getParent() + "] 中创建文件");
throw new Exception("没有目录:[" + fileToSave.getParent() + "]的写权限");
}
try {
fos = new FileOutputStream(fileToSave); // 取得文件输出流
} catch (FileNotFoundException e) {
e.printStackTrace();
}
dialog = new JDialog(ChatClient.this,"文件接收进度"); // 进度窗口
dialog.setLayout(new BorderLayout());
JPanel northPane = new JPanel(new GridLayout(1,3));
northPane.add(percentLabel);
northPane.add(bytesRecvLabel);
northPane.add(totalBytesLabel);
dialog.add(northPane,"North");
JPanel southPane = new JPanel();
ok = new JButton("确定");
ok.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
dialog.dispose();
}
});
southPane.add(ok);
ok.setEnabled(false);
cancel = new JButton("取消");
cancel.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae) {
recv = false;
alarm("您已经取消了文件接收任务!");
}
});
southPane.add(cancel);
dialog.add(southPane,"South");
}
/**
* 文件接收线程方法
*/
public void run() {
ServerSocket ss = null;
Socket fileSocket = null;
InputStream in = null;
long fileLeng = 0;
long theBytes = 0;
try {
// 创建服务器端
ss = new ServerSocket(sendFilePort);
// 等待发送方连接
fileSocket = ss.accept();
in = fileSocket.getInputStream(); // 取得socket输入流
byte[] buffer = new byte[bufferSize]; // 缓冲区
int bytesRead = 0;
int leng = in.read();
in.read(buffer,0,leng);
String s = new String(buffer,0,leng);
fileLeng = Long.parseLong(s); // 先取得文件的总大小
totalBytesLabel.setText("/" + s + ")字节");
theBar = new JProgressBar(0,(int)(fileLeng/1024));
theBar.setValue(0);
percentLabel.setText(("0%"));
dialog.add(theBar);
WindowTool.doView(dialog,350,100); // 居中显示窗口
// 开始接收文件
while (recv) {
bytesRead = in.read(buffer,0,buffer.length); // 读取SOCKET输入流
if (bytesRead <= 0) {
break;
}
// 设置显示信息
theBytes += bytesRead;
theBar.setValue((int)(theBytes / 1024)); // 设置显示的进度条
bytesRecvLabel.setText(" ( " + theBytes);
percentLabel.setText((int)(((double)theBytes / fileLeng) * 100) + "%");//设置百分比标签
fos.write(buffer,0,bytesRead); // 写入文件输出流
}
} catch (IOException e) {
e.printStackTrace();
}
finally {
// 关闭资源
if (in != null) {
try {
in.close(); //关闭socket输入流对象
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close(); // 关闭文件输出流对象
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileSocket != null) {
try {
fileSocket.close(); // 关闭socket对象
} catch (IOException e) {
e.printStackTrace();
}
}
if (ss != null) {
try {
ss.close(); // 关闭文件接收服务
} catch (IOException e) {
e.printStackTrace();
}
}
}
ok.setEnabled(true);
cancel.setEnabled(false);
if (theBytes == fileLeng) {
alarm("文件接收完毕,文件保存为:" + fileToSave.getPath()+ " !");
return;
}
else if (recv){
alarm("文件接收被发送方中断 !");
}
fileToSave.delete();// 如果是非正常中断,则把已经接收的片断文件删除..........
sendFileName = null;
}
}
}
package com.chat.main;
import java.io.IOException;
import java.net.*;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import com.chat.client.ChatClient;
/**
* 客户端启动主程序
* @author 刘优
*
*/
public class ClientMain {
public static void main(String[] args) {
try {
String ip = getMyIp();
//System.out.println(ip);
int port = 8888;
if (args.length > 0)
{
ip = args[0];
if (args.length > 1)
{
try {
port = Integer.parseInt(args[1]);
}
catch (Exception e) {
System.out.println("port number : " + args[1] + " is not a integer!");
}
}
}
ChatClient client = new ChatClient(ip,port); // IP 和 端口
} catch (Exception ee) {
ee.printStackTrace();
}
}
private static String getMyIp() throws Exception
{
Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
InetAddress ip = null;
while (allNetInterfaces.hasMoreElements())
{
NetworkInterface netInterface = (NetworkInterface)allNetInterfaces.nextElement();
//System.out.println(netInterface.getName());
Enumeration addresses = netInterface.getInetAddresses();
while (addresses.hasMoreElements())
{
ip = (InetAddress)addresses.nextElement();
if (ip != null && ip instanceof Inet4Address && !(ip.getHostAddress().startsWith("127.")))
{
//System.out.println("本机的IP = " + ip.getHostAddress());
break;
}
}
}
return ip.getHostAddress();
}
}