chatRoom.zip
聊天室聊天室服务端
package chatroom;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
/**
* 聊天室服务器端 ChatRoomServer类
*
* @version 1.01, 09/04/10
*/
public class ChatRoomServer {
private ServerSocket ss;
/* 存放Socket的集合hs */
private HashSet<Socket> hs;
public ChatRoomServer() {
JFrame jf = new JFrame();
do {
/*
* 弹出输入对话框,提示输入服务器需要绑定的端口号
*/
int port = Integer.parseInt(JOptionPane.showInputDialog(jf,
"bind port:"));
try {
ss = new ServerSocket(port);
System.out.println("server start success,now listening port:"
+ port);
} catch (Exception e) {
/* 弹出确认框进行确认 */
int op = JOptionPane.showConfirmDialog(jf, // 指定是在jf中弹出确认框
"bind fail,retry?", // 框体内容
"bind fail", // 对话框的标题
JOptionPane.YES_NO_OPTION); // 确认框按钮项
/* 如果选择'否',则退出程序 */
if (op == JOptionPane.NO_OPTION) {
System.exit(1);
}
/* 打印异常栈信息 */
e.printStackTrace();
}
} while (ss == null);
/* 创建HashSet,用来存放Socket对象 */
hs = new HashSet<Socket>();
while (true) {
try {
/* 获得Socket,网络阻塞,等待客户端的连接 */
Socket s = ss.accept();
/* 一旦客户端连接上,则加入HashSet,便于维护 */
hs.add(s);
/* 启动一个服务线程,为客户端服务 */
new ServerThread(s).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 成员内部类,服务线程类
*/
class ServerThread extends Thread {
private Socket s;
public ServerThread(Socket s) {
this.s = s;
}
@Override
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(s
.getInputStream()));
while (true) {
/* 从网络中读取客户端发出的消息 */
String str = br.readLine();
/*
* 客户退出时的处理逻辑 规则:以"%EXIT_CHATROOM%"开头的消息为客户退出标记
*/
if (str.charAt(0) == '%') {
String com = str.split("%")[1];
if (com.equals("EXIT_CHATROOM")) {
hs.remove(s);
printMessage(str.split("%")[2] + ",leave!");
s.close();
break;
}
} else {
/* 正常情况,直接向客户端群发消息 */
printMessage(str);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 负责为客户端群发消息
*/
private void printMessage(String mes) {
System.out.println(mes);
try {
/* 遍历所有Socket */
for (Socket s : hs) {
PrintStream ps = new PrintStream(s.getOutputStream());
/* 产生发消息的时刻 */
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
/* 向客户端发消息,消息结构为mes [yyyy-MM-dd HH:mm:ss] */
ps.println(mes + " [" + sdf.format(date) + "]");
/* 注意需要及时flush,清空缓冲区 */
ps.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 主方法,启动聊天室服务器端
*/
public static void main(String[] args) {
new ChatRoomServer();
}
}
客户端窗体
package chatroom;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
* 客户端窗口ChatRoomClientFrame类 负责客户端的视图表示、事件处理等逻辑 作为一个窗口,所以本类继承自JFrame
* 为了实现事件处理,本类实现了ActionListener接口
*
* @version 1.01, 09/04/10
*/
public class ChatRoomClientFrame extends JFrame implements ActionListener {
private static final long serialVersionUID = 3484437496861281646L;
private JTextArea jta; // 多行文本域
private JLabel label; // 文本标签
private JTextField jtf; // 单行文本框
private JButton jb; // 按钮
private Socket socket; // socket 套接字
private String name; // 用户名
/*
* ChatRoomClientFrame构造方法,负责初始化以及添加事件处理
*/
public ChatRoomClientFrame(String name, Socket socket) {
super("chatroom");
Font f = new Font("楷体", Font.BOLD, 20); /* 设置字体 */
jta = new JTextArea(10, 10); /* 创建10行10列的空多行文本域 */
jta.setEditable(false); /* 设置多行文本域为不可编辑 */
jta.setFont(f); /* 设置多行文本域字体 */
label = new JLabel(name + ":"); /* 创建带有用户名的文本标签 */
label.setFont(f); /* 设置文本标签字体 */
jtf = new JTextField(25); /* 创建单行文本框 */
jtf.setFont(f); /* 设置单行文本框字体 */
jb = new JButton("send"); /* 创建按钮 */
this.name = name;
this.socket = socket;
init(); /* 初始化,设置组件关系 */
addEventHandler(); /* 为组件添加事件监听 */
}
/*
* 初始化组件关系方法,供构造方法调用
*/
private void init() {
JScrollPane jsp = new JScrollPane(jta);
this.add(jsp, BorderLayout.CENTER);
JPanel jp = new JPanel();
jp.add(label);
jp.add(jtf);
jp.add(jb);
this.add(jp, BorderLayout.SOUTH);
}
/*
* 负责为各组件添加事件处理机制
*/
private void addEventHandler() {
jb.addActionListener(this); // 为jb添加事件监听,JButton点击触发事件
jtf.addActionListener(this); // 为jtf添加事件监听,JTextField敲回车触发事件
/* 设置JFrame默认关闭状态:DO_NOTHING_ON_CLOSE */
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
/* 为JFrame添加窗口事件监听 */
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent arg0) {
/* 弹出确认框进行交互 */
int op = JOptionPane.showConfirmDialog(
ChatRoomClientFrame.this, "exit?", "exit",
JOptionPane.YES_NO_OPTION);
if (op == JOptionPane.YES_OPTION) {
try {
PrintStream ps = new PrintStream(socket
.getOutputStream());
/* 向服务器发送以%EXIT_CHATROOM%name为格式的串,代表退出信号 */
ps.println("%EXIT_CHATROOM%" + name);
/* 注意需要及时flush */
ps.flush();
try {
Thread.sleep(168); // 进行延时控制,防止提前关闭socket
} catch (InterruptedException e) {
e.printStackTrace();
}
socket.close(); // 关闭客户端socket
ps.close(); // 关闭流
} catch (IOException e) {
e.printStackTrace();
}
System.exit(1); // 退出程序
}
}
});
}
/*
* 实现事件处理方法
*/
public void actionPerformed(ActionEvent ae) {
try {
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println(name + ": " + jtf.getText());
ps.flush();
/* 清空jtf中内容 */
jtf.setText("");
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 显示并执行窗口逻辑
*/
public void showMe() {
this.pack(); // 自动调整此窗口的大小
this.setVisible(true); // 设置窗口可见
/*
* 启动线程,负责接收服务器端发来的消息
*/
new Thread() {
@Override
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
while (true) {
/* 从网络中读取服务器发出的数据 */
String str = br.readLine();
/* 对JTextArea进行追加消息 */
jta.append(str + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
客户端
package chatroom;
import java.io.PrintStream;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
/**
* 聊天室客户端 ChatRoomClient
*
* @version 1.01, 09/04/10
*/
public class ChatRoomClient {
private String name; // 用户名
private Socket socket; // Socket 套接字
private ChatRoomClientFrame frame; // 组合复用 ChatRoomClientFrame
/*
* ChatRoomClient构造方法,负责构造客户端表示逻辑
*/
public ChatRoomClient() {
JFrame jf = new JFrame();
/*
* 弹出用户输入对话框,提示用户输入服务器的IP地址 返回相应字符串形式,存于变量serverIP,缺省值127.0.0.1
*/
String serverIP = JOptionPane.showInputDialog(jf, "server IP:",
"127.0.0.1");
/*
* 弹出用户输入对话框,提示用户输入服务器端口号 转化为int形式返回,存于变量serverPort
*/
int serverPort = Integer.parseInt(JOptionPane.showInputDialog(jf,
"port:"));
/*
* 弹出用户输入对话框,提示用户输入用户名,存于成员属性name
*/
name = JOptionPane.showInputDialog(jf, "your name:");
try {
/* 通过IP和Port,与服务器端建立连接 */
socket = new Socket(serverIP, serverPort);
/* 给服务器发消息 */
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println(name + ",login !");
} catch (Exception e) {
JOptionPane.showMessageDialog(jf, "fail,check the connection!");
e.printStackTrace();
System.exit(1);
}
/*
* 创建ChatRoomClientFrame,进行客户端主窗口的显示
*/
frame = new ChatRoomClientFrame(name, socket);
frame.showMe();
}
/*
* 主方法,启动聊天室客户端
*/
public static void main(String[] args) {
new ChatRoomClient();
}
}