基于 Java 的聊天室设计与实现
一、 设计目的:
信息技术的高速发展给人类生产,生活带来了广泛而深远的影响。“上网冲浪”、“数码相机”“PDA”、“彩屏手机”、“蓝牙技术”等新产品,新概念层出不穷,不断冲击着人们的视听。这些高科技成果为人们带来便捷、快乐的同时,也促进了人机交互技术的发展。人机交互是关于设计、评价和实现供人们使用的交互式计算机系统,并围绕相关的主要现象进行研究的科学。它主要研究人与计算机之间的信息交换,涉及计算机科学,心理学,认知科学等诸多学科,是信息技术的一个重要组成部分,并将继续对信息技术的发展产生巨大影响。
聊天室是一项应用广泛,并且实用性强的一个非常典型的人机交互系统。在信息世界里,聊天工具的使用是非常频繁的,如 MSN、QQ、校内通,之类的聊天工具许多人都正在使用。此次设计的聊天室系统虽然其功能简单,但是在开发技术和过程中,能够充分学习和使用 Java 的技术,进一步培养学生面向对象程序设计的思想,加深对面向对象程序设计四大特性的理解。
二、需求分析
人们日常生活中越来越多地使用聊天室这项应用来为自己的工作和学习服务。一个操作简单,界面友好、运行稳定的聊天室对于小型局域网的用户可以起到很好的交流作用。所谓聊天室就是一个可以让许多用户同时互相通信的网上园地,个人用户可以通过聊天室将自己融入整个主流社会中。
根据目前使用聊天室的用户需求显示,用户对于聊天室主要有以下几点要求:
(1)、聊天功能:可以实时接收和发送信息,并能支持公聊和私聊的的方式,能够查看聊天记录,聊天过程中能对字体、表情等进行设定等。
(2)、用户功能:可以随时注册和登录及注销,并能选择性加好友和删除好友以及查看对方的开放信息,还能看到对方的登录状态。
(3)、文件传输功能:用户可以共享资源,能发送及接收文件。
(4)、保密功能:用户可以设定自己的保密状态,使某些信息对于其他用户是不可见的。
(5)、系统稳定:客户端与服务端的连接稳定,不出现或者少出现连接不上的情况。
该简易聊天室是为许多喜爱聊天的用户开发设计的,和许多大型的网络聊天室相比该小聊天室功能比较简单,但能实现基本的聊天功能,点对多、点对点聊天和基本的用户功能,登录、注销、查看在线用户等。
三、软件功能描述
(1)、用户设置:
功能描述:用户自定义自己的登录名,成功设置后,服务端能接收到新设置的用户名;
(2)、登录功能:
功能描述:在服务端开启的情况下,用户可以以自己自定义或者默认的登录名和登录地址端口登录到客户端,并能在服务器端显示登录状态;
(3)、公聊:
功能描述:用户在聊天室中可以选择与“所有人”聊天,聊天室中的其他在线用户可以正常接收并正确显示公聊消息。
(4)、私聊:
功能描述:用户可以选择与聊天室的在线用户列表中的其他用户聊天。双方的聊天记录对于聊天室的其他用户是不可见的。
(5)、聊天心情:
功能描述:用户在聊天的过程中可以选择相应的心情来表达自己的情感。
(6)、系统消息:
功能描述:服务端正常运行的过程中,在聊天室的每一个用户都能接收服务器端发送的信息提示信息。
(7)、用户的状态.
功能描述:每一个客户端用户都可以实时看到目前其他在线用户的状态。
(8)、用户注销:
功能描述:用户可以注销自己的账号,并重新登记登录名和地址。
(9)、用户退出:
功能描述:用户可以在正常情况下退出聊天室。
(10)、服务端控制:
功能描述:只有在服务端开启的情况下,用户才能登陆成功和聊天;用户设置的地址端口也必须与服务端的侦听端口一致,否则不能登录。
四: 主要模块描述及流程图
1、 模块描述
本聊天室分为聊天室服务端和聊天室客户端,服务端与客户端之间通过套接口 Socket(TCP)连接。在 Java 中使用套接口相当简单,Java API 为处理套接口的通信提供了一个类 java.net.Socket.,使得编写网络应用程序相对容易.服务器采用多线程以满足多用户的请求,通过 JDBC 与后台数据库连接,并通过创建一个 ServerSocket 对象来监听来自客户的连接请求,默认 IP 地址为:127.0.0.1 默认端口为 8888。
(1)聊天室服务端模块图:
端口服务:输入要侦听的端口,默认为 8888;
启动服务:启动服务器,并开始在设置的端口中侦听,客户端用户可以登录并开始聊天;
停止服务:关闭服务器,侦听结束。客户端用户不能再聊天;
发送系统消息:服务器端给所有人或者个别用户发送消息。
消息显示:可以显示所有用户的聊天记录,和上线、下线信息。
退出服务器:退出程序,并停止服务;
(2)聊天室客户端模块图:
用户设置:用户可以设置自己的用户名进行聊天;
连接设置:用户可以设置自己的服务器的 IP 地址和端口号,默认的连接地址为:127.0.0.1:8888;
用户登录:只能在聊天室服务端已开启的情况下,用户才能登录,用户登录之后才能开始聊天,登录后不能再修改用户设置和连接设置;
用户注销:退出聊天,可以继续修改用户设置和连接设置;
发送和接收聊天信息:用户可以给所有人或者个别用户发送信息,并能看到其他用户发送给自己或所有人的消息;
退出:关闭所有聊天并退出客户端。
2、 流程图
五.主要类 UML 关系图:
1、聊天室服务器端设计:
聊天室服务端主要包括 7 个文件,它们的功能如下:
(1)ChatServer.java:包含名为 ChatServer 的 public 类,其主要功能为定义服务器端的界面,添加事件侦听与事件处理。调用 ServerListen 类来实现服务端用户上线与下线的侦听,调用 ServerReceive 类来实现服务器端的消息的收发。
(2)ServerListen.java:该类实现服务端用户上线与下线的侦听。该类对用户上线下线的侦听是通过调用用户链表类(UserLinkList)来实现的,当用户上线与下线情况发生变化时,该类会对主类的界面进行相应的修改。
(3)ServerReceive.java:该类是实现服务器端的消息的收发的类。该类分别定义了向某用户及所有人发送消息的方法,发送的消息会显示在主界面类的界面上。
(4)PortConf.java:该类继承自 JDialog,是用户对服务器端侦听端口进行修改配置的类。
(5)Node.java:用户链表的节点类,定义了链表中的用户。该类与前面所讲的链表节点 Node 类的功能相当。
(6)UserLinkList.java:用户链表节点的具体实现类。该类通过构造函数构造用户链表,定义了添加用户,删除用户、返回用户数、根据用户名查找用户、根据索引查找用户这 5 个方法。
(7)Help.java:服务端程序的帮助类。
2.聊天室客户端设计:
聊天室客户端主要包括 5 个文件,它们的功能如下:
(1)ChatClient.java:包含名为 ChatClient 的 public 类,其主要功能为定义客户端的界面,添加事件侦听与事件处理。该类定义了 Connect()与 DisConnect()方法实现与服务器的连接与断开连接。当登录到指定的服务器时,调用 ClientReceive 类实现消息收发,同时该类还定义了 SendMessage()方法来向其他用户发送带有表情的消息或者悄悄话。
(2)ClientReceive.java:该类是实现服务器端与客户端消息收发的类。
(3)ConnectConf.java:该类继承自 JDialog,是用户对所要连接的服务器 IP 及侦听端口进行修改配置的类。
(4)UserConf.java:该类继承自 JDialog,是用户对连接到服务器所显示的用户名进行修改配置的类。
(5)Help.java:客户端程序的帮助类。
六.主要程序代码清单和注释说明:
1.主要程序代码
ChatServer.java
public void actionPerformed(ActionEvent e) {
Object obj = e.getSource();
if (obj == startServer || obj == startItem) { //启动服务端
startService();
}
else if (obj == stopServer || obj == stopItem) { //停止服务端
int j=JOptionPane.showConfirmDialog(
this,"真的停止服务吗?","停止服务",
JOptionPane.YES_OPTION,JOptionPane.QUESTION_MESSAGE);
if (j == JOptionPane.YES_OPTION){
stopService();
}
}
else if (obj == portSet || obj == portItem) { //端口设置
//调出端口设置的对话框
PortConf portConf = new PortConf(this);
portConf.show();
}
else if (obj == exitButton || obj == exitItem) { //退出程序
int j=JOptionPane.showConfirmDialog(
this,"真的要退出吗?","退出",
JOptionPane.YES_OPTION,JOptionPane.QUESTION_MESSAGE);
if (j == JOptionPane.YES_OPTION){
stopService();
System.exit(0);
}
}
else if (obj == helpItem) { //菜单栏中的帮助
//调出帮助对话框
Help helpDialog = new Help(this);
helpDialog.show();
}
else if (obj == sysMessage || obj == sysMessageButton) { //发送系统消息
sendSystemMessage();
}
}
/**
* 启动服务端
*/
public void startService(){
try{
serverSocket = new ServerSocket(port,10);
messageShow.append("服务端已经启动,在"+port+"端口侦听...\n");
startServer.setEnabled(false);
startItem.setEnabled(false);
portSet.setEnabled(false);
portItem.setEnabled(false);
stopServer .setEnabled(true);
stopItem .setEnabled(true);
sysMessage.setEnabled(true);
}
catch (Exception e){
//System.out.println(e);
}
userLinkList = new UserLinkList();
listenThread = new ServerListen(serverSocket,combobox,
messageShow,showStatus,userLinkList);
listenThread.start();
}
/**
* 关闭服务端
*/
public void stopService(){
try{
//向所有人发送服务器关闭的消息
sendStopToAll();
listenThread.isStop = true;
serverSocket.close();
int count = userLinkList.getCount();
int i =0;
while( i < count){
Node node = userLinkList.findUser(i);
node.input .close();
node.output.close();
node.socket.close();
i ++;
}
stopServer .setEnabled(false);
stopItem .setEnabled(false);
startServer.setEnabled(true);
startItem.setEnabled(true);
portSet.setEnabled(true);
portItem.setEnabled(true);
sysMessage.setEnabled(false);
messageShow.append("服务端已经关闭\n");
combobox.removeAllItems();
combobox.addItem("所有人");
}
catch(Exception e){
//System.out.println(e);
}
}
/**
* 向所有人发送服务器关闭的消息
*/
public void sendStopToAll(){
int count = userLinkList.getCount();
int i = 0;
while(i < count){
Node node = userLinkList.findUser(i);
if(node == null) {
i ++;
continue;
}
try{
node.output.writeObject("服务关闭");
node.output.flush();
}
catch (Exception e){
//System.out.println("$$$"+e);
}
i++;
}
}
/**
* 向所有人发送消息
*/
public void sendMsgToAll(String msg){
int count = userLinkList.getCount();//用户总数
int i = 0;
while(i < count){
Node node = userLinkList.findUser(i);
if(node == null) {
i ++;
continue;
}
try{
node.output.writeObject("系统信息");
node.output.flush();
node.output.writeObject(msg);
node.output.flush();
}
catch (Exception e){
//System.out.println("@@@"+e);
}
i++;
}
sysMessage.setText("");
}
/**
* 向客户端用户发送消息
*/
public void sendSystemMessage(){
String toSomebody = combobox.getSelectedItem().toString();
String message = sysMessage.getText() + "\n";
messageShow.append(message);
//向所有人发送消息
if(toSomebody.equalsIgnoreCase("所有人")){
sendMsgToAll(message);
}
else{
//向某个用户发送消息
Node node = userLinkList.findUser(toSomebody);
try{
node.output.writeObject("系统信息");
node.output.flush();
node.output.writeObject(message);
node.output.flush();
}
catch(Exception e){
//System.out.println("!!!"+e);
}
sysMessage.setText("");//将发送消息栏的消息清空
}
}
public static void main(String[] args) {
ChatServer app = new ChatServer();
}
ServerListen.java
public class ServerListen extends Thread {
ServerSocket server;
JComboBox combobox;
JTextArea textarea;
JTextField textfield;
UserLinkList userLinkList;//用户链表
Node client;
ServerReceive recvThread;
public boolean isStop;
/*
* 聊天服务端的用户上线于下线侦听类
*/
public ServerListen(ServerSocket server,JComboBox combobox,
JTextArea textarea,JTextField textfield,UserLinkList userLinkList){
this.server = server;
this.combobox = combobox;
this.textarea = textarea;
this.textfield = textfield;
this.userLinkList = userLinkList;
isStop = false;
}
public void run(){
while(!isStop && !server.isClosed()){
try{
client = new Node();
client.socket = server.accept();
client.output = new ObjectOutputStream(client.socket.getOutputStream());
client.output.flush();
client.input = new ObjectInputStream(client.socket.getInputStream());
client.username = (String)client.input.readObject();
//显示提示信息
combobox.addItem(client.username);
userLinkList.addUser(client);
textarea.append("用户 " + client.username + " 上线" + "\n");
textfield.setText("在线用户" + userLinkList.getCount() + "人\n");
recvThread = new ServerReceive(textarea,textfield,
combobox,client,userLinkList);
recvThread.start();
}
catch(Exception e){
}
}
}
}
ServerReceive.java
/*
* 服务器收发消息的类
*/
public class ServerReceive extends Thread {
JTextArea textarea;
JTextField textfield;
JComboBox combobox;
Node client;
UserLinkList userLinkList;//用户链表
public boolean isStop;
public ServerReceive(JTextArea textarea,JTextField textfield,
JComboBox combobox,Node client,UserLinkList userLinkList){
this.textarea = textarea;
this.textfield = textfield;
this.client = client;
this.userLinkList = userLinkList;
this.combobox = combobox;
isStop = false;
}
public void run(){
//向所有人发送用户的列表
sendUserList();
while(!isStop && !client.socket.isClosed()){
try{
String type = (String)client.input.readObject();
if(type.equalsIgnoreCase("聊天信息")){
String toSomebody = (String)client.input.readObject();
String status = (String)client.input.readObject();
String action = (String)client.input.readObject();
String message = (String)client.input.readObject();
String msg = client.username
+" "+ action
+ "对 "
+ toSomebody
+ " 说 : "
+ message
+ "\n";
if(status.equalsIgnoreCase("悄悄话")){
msg = " [悄悄话] " + msg;
}
textarea.append(msg);
if(toSomebody.equalsIgnoreCase("所有人")){
sendToAll(msg);//向所有人发送消息
}
else{
try{
client.output.writeObject("聊天信息");
client.output.flush();
client.output.writeObject(msg);
client.output.flush();
}
catch (Exception e){
//System.out.println("###"+e);
}
Node node = userLinkList.findUser(toSomebody);
if(node != null){
node.output.writeObject("聊天信息");
node.output.flush();
node.output.writeObject(msg);
node.output.flush();
}
}
}
else if(type.equalsIgnoreCase("用户下线")){
Node node = userLinkList.findUser(client.username);
userLinkList.delUser(node);
String msg = "用户 " + client.username + " 下线\n";
int count = userLinkList.getCount();
combobox.removeAllItems();
combobox.addItem("所有人");
int i = 0;
while(i < count){
node = userLinkList.findUser(i);
if(node == null) {
i ++;
continue;
}
combobox.addItem(node.username);
i++;
}
combobox.setSelectedIndex(0);
textarea.append(msg);
textfield.setText("在线用户" + userLinkList.getCount() + "人\n");
sendToAll(msg);//向所有人发送消息
sendUserList();//重新发送用户列表,刷新
break;
}
}
catch (Exception e){
//System.out.println(e);
}
}
}
/*
* 向所有人发送消息
*/
public void sendToAll(String msg){
int count = userLinkList.getCount();
int i = 0;
while(i < count){
Node node = userLinkList.findUser(i);
if(node == null) {
i ++;
continue;
}
try{
node.output.writeObject("聊天信息");
node.output.flush();
node.output.writeObject(msg);
node.output.flush();
}
catch (Exception e){
//System.out.println(e);
}
i++;
}
}
/*
* 向所有人发送用户的列表
*/
public void sendUserList(){
String userlist = "";
int count = userLinkList.getCount();
int i = 0;
while(i < count){
Node node = userLinkList.findUser(i);
if(node == null) {
i ++;
continue;
}
userlist += node.username;
userlist += '\n';
i++;
}
i = 0;
while(i < count){
Node node = userLinkList.findUser(i);
if(node == null) {
i ++;
continue;
}
try{
node.output.writeObject("用户列表");
node.output.flush();
node.output.writeObject(userlist);
node.output.flush();
}
catch (Exception e){
//System.out.println(e);
}
i++;
}
}
}
PortConf.java
public class PortConf extends JDialog {
JPanel panelPort = new JPanel();
JButton save = new JButton();
JButton cancel = new JButton();
public static JLabel DLGINFO=new JLabel(
" 默认端口号为:8888");
JPanel panelSave = new JPanel();
JLabel message = new JLabel();
public static JTextField portNumber ;
public PortConf(JFrame frame) {
super(frame, true);
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
//设置运行位置,使对话框居中
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation( (int) (screenSize.width - 400) / 2 + 50,
(int) (screenSize.height - 600) / 2 + 150);
this.setResizable(false);
}
private void jbInit() throws Exception {
this.setSize(new Dimension(300, 120));
this.setTitle("端口设置");
message.setText("请输入侦听的端口号:");
portNumber = new JTextField(10);
portNumber.setText(""+ChatServer.port);
save.setText("保存");
cancel.setText("取消");
panelPort.setLayout(new FlowLayout());
panelPort.add(message);
panelPort.add(portNumber);
panelSave.add(new Label(" "));
panelSave.add(save);
panelSave.add(cancel);
panelSave.add(new Label(" "));
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(panelPort, BorderLayout.NORTH);
contentPane.add(DLGINFO, BorderLayout.CENTER);
contentPane.add(panelSave, BorderLayout.SOUTH);
//保存按钮的事件处理
save.addActionListener(
new ActionListener() {
public void actionPerformed (ActionEvent a) {
int savePort;
try{
savePort=Integer.parseInt(PortConf.portNumber.getText());
if(savePort<1 || savePort>65535){
PortConf.DLGINFO.setText(" 侦听端口必须是0-65535之间的整数!");
PortConf.portNumber.setText("");
return;
}
ChatServer.port = savePort;
dispose();
}
catch(NumberFormatException e){
PortConf.DLGINFO.setText(" 错误的端口号,端口号请填写整数!");
PortConf.portNumber.setText("");
return;
}
}
}
);
//关闭对话框时的操作
this.addWindowListener(
new WindowAdapter(){
public void windowClosing(WindowEvent e){
DLGINFO.setText(" 默认端口号为:8888");
}
}
);
//取消按钮的事件处理
cancel.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
DLGINFO.setText(" 默认端口号为:8888");
dispose();
}
});
}
}
Node.java
/**
* 用户链表的结点类
*/
public class Node {
String username = null;
Socket socket = null;
ObjectOutputStream output = null;
ObjectInputStream input = null;
Node next = null;
}
UserLinkList.java
/**
* 用户链表
*/
public class UserLinkList {
Node root;
Node pointer;
int count;
/**
* 构造用户链表
*/
public UserLinkList(){
root = new Node();
root.next = null;
pointer = null;
count = 0;
}
/**
* 添加用户
*/
public void addUser(Node n){
pointer = root;
while(pointer.next != null){
pointer = pointer.next;
}
pointer.next = n;
n.next = null;
count++;
}
/**
* 删除用户
*/
public void delUser(Node n){
pointer = root;
while(pointer.next != null){
if(pointer.next == n){
pointer.next = n.next;
count --;
break;
}
pointer = pointer.next;
}
}
/**
* 返回用户数
*/
public int getCount(){
return count;
}
/**
* 根据用户名查找用户
*/
public Node findUser(String username){
if(count == 0) return null;
pointer = root;
while(pointer.next != null){
pointer = pointer.next;
if(pointer.username.equalsIgnoreCase(username)){
return pointer;
}
}
return null;
}
/**
* 根据索引查找用户
*/
public Node findUser(int index){
if(count == 0) {
return null;
}
if(index < 0) {
return null;
}
pointer = root;
int i = 0;
while(i < index + 1){
if(pointer.next != null){
pointer = pointer.next;
}
else{
return null;
}
i++;
}
return pointer;
}
}
Help.java
/**
* 生成设置对话框的类
*/
public class Help extends JDialog {
JPanel titlePanel = new JPanel();
JPanel contentPanel = new JPanel();
JPanel closePanel = new JPanel();
JButton close = new JButton();
JLabel title = new JLabel("聊天室服务端帮助");
JTextArea help = new JTextArea();
Color bg = new Color(255,255,255);
public Help(JFrame frame) {
super(frame, true);
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
//设置运行位置,使对话框居中
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation( (int) (screenSize.width - 400) / 2,
(int) (screenSize.height - 320) / 2);
this.setResizable(false);
}
private void jbInit() throws Exception {
this.setSize(new Dimension(400, 200));
this.setTitle("帮助");
titlePanel.setBackground(bg);;
contentPanel.setBackground(bg);
closePanel.setBackground(bg);
help.setText("1、设置服务端的侦听端口(默认端口为8888)。\n"+
"2、点击 启动服务 按钮便可在指定的端口启动服务。\n"+
"3、选择需要接受消息的用户,在消息栏中写入消息,之后便可发送消息。\n"+
"4、信息状态栏中显示服务器当前的启动与停止状态、"+
"用户发送的消息和\n 服务器端发送的系统消息。");
help.setEditable(false);
titlePanel.add(new Label(" "));
titlePanel.add(title);
titlePanel.add(new Label(" "));
contentPanel.add(help);
closePanel.add(new Label(" "));
closePanel.add(close);
closePanel.add(new Label(" "));
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(titlePanel, BorderLayout.NORTH);
contentPane.add(contentPanel, BorderLayout.CENTER);
contentPane.add(closePanel, BorderLayout.SOUTH);
close.setText("关闭");
//事件处理
close.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
dispose();
}
});
}
}
ChatClient.java
/*
* 聊天客户端的主框架类
*/
public class ChatClient extends JFrame implements ActionListener{
/**
*
*/
private static final long serialVersionUID = 1L;
String ip = "127.0.0.1";//连接到服务端的ip地址
int port = 8888;//连接到服务端的端口号
String userName = "狂狂";//用户名
int type = 0;//0表示未连接,1表示已连接
JComboBox combobox;//选择发送消息的接受者
JTextArea messageShow;//客户端的信息显示
JScrollPane messageScrollPane;//信息显示的滚动条
JLabel express,sendToLabel,messageLabel ;
JTextField clientMessage;//客户端消息的发送
JCheckBox checkbox;//悄悄话
JComboBox actionlist;//表情选择
JButton clientMessageButton;//发送消息
JTextField showStatus;//显示用户连接状态
Socket socket;
ObjectOutputStream output;//网络套接字输出流
ObjectInputStream input;//网络套接字输入流
ClientReceive recvThread;
//建立菜单栏
JMenuBar jMenuBar = new JMenuBar();
//建立菜单组
JMenu operateMenu = new JMenu ("操作(O)");
//建立菜单项
JMenuItem loginItem = new JMenuItem ("用户登录(I)",new ImageIcon("face/98.gif"));
JMenuItem logoffItem = new JMenuItem ("用户注销(L)",new ImageIcon("face/icon.gif"));
JMenuItem exitItem=new JMenuItem ("退出(X)",new ImageIcon("face/smile.gif"));
JMenu conMenu=new JMenu ("设置(C)");
JMenuItem userItem=new JMenuItem ("用户设置(U)",new ImageIcon("face/messenger_big.gif"));
JMenuItem connectItem=new JMenuItem ("连接设置(C)",new ImageIcon("face/Uabrand.gif"));
JMenu helpMenu=new JMenu ("帮助(H)");
JMenuItem helpItem=new JMenuItem ("帮助(H)",new ImageIcon("face/HelpCenter.gif"));
//建立工具栏
JToolBar toolBar = new JToolBar();
//建立工具栏中的按钮组件
JButton loginButton;//用户登录
JButton logoffButton;//用户注销
JButton userButton;//用户信息的设置
JButton connectButton;//连接设置
JButton exitButton;//退出按钮
//框架的大小
Dimension faceSize = new Dimension(400, 600);
JPanel downPanel ;
GridBagLayout girdBag;
GridBagConstraints girdBagCon;
public ChatClient(){
init();//初始化程序
//添加框架的关闭事件处理
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
//设置框架的大小
this.setSize(faceSize);
this.setVisible(true);
setIconImage(getToolkit().getImage("face/love.gif"));
//设置运行时窗口的位置
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation( (int) (screenSize.width - faceSize.getWidth()) / 2,
(int) (screenSize.height - faceSize.getHeight()) / 2);
this.setResizable(false);
this.setTitle("聊天室客户端"); //设置标题
//为操作菜单栏设置热键'V'
operateMenu.setMnemonic('O');
//为用户登录设置快捷键为ctrl+i
loginItem.setMnemonic ('I');
loginItem.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_I,InputEvent.CTRL_MASK));
//为用户注销快捷键为ctrl+l
logoffItem.setMnemonic ('L');
logoffItem.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_L,InputEvent.CTRL_MASK));
//为退出快捷键为ctrl+x
exitItem.setMnemonic ('X');
exitItem.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_X,InputEvent.CTRL_MASK));
//为设置菜单栏设置热键'C'
conMenu.setMnemonic('C');
//为用户设置设置快捷键为ctrl+u
userItem.setMnemonic ('U');
userItem.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_U,InputEvent.CTRL_MASK));
//为连接设置设置快捷键为ctrl+c
connectItem.setMnemonic ('C');
connectItem.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_C,InputEvent.CTRL_MASK));
//为帮助菜单栏设置热键'H'
helpMenu.setMnemonic('H');
//为帮助设置快捷键为ctrl+p
helpItem.setMnemonic ('H');
helpItem.setAccelerator (KeyStroke.getKeyStroke (KeyEvent.VK_H,InputEvent.CTRL_MASK));
}
/**
* 程序初始化函数
*/
public void init(){
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
//添加菜单栏
operateMenu.add (loginItem);
operateMenu.add (logoffItem);
operateMenu.addSeparator();
operateMenu.add (exitItem);
jMenuBar.add (operateMenu);
conMenu.add (userItem);
conMenu.addSeparator();
conMenu.add (connectItem);
jMenuBar.add (conMenu);
helpMenu.add (helpItem);
jMenuBar.add (helpMenu);
setJMenuBar (jMenuBar);
//初始化按钮
loginButton = new JButton("登录");
logoffButton = new JButton("注销");
userButton = new JButton("用户设置" );
connectButton = new JButton("连接设置" );
exitButton = new JButton("退出" );
//当鼠标放上显示信息
loginButton.setToolTipText("连接到指定的服务器");
logoffButton.setToolTipText("与服务器断开连接");
userButton.setToolTipText("设置用户信息");
connectButton.setToolTipText("设置所要连接到的服务器信息");
//将按钮添加到工具栏
toolBar.add(userButton);
toolBar.add(connectButton);
toolBar.addSeparator();//添加分隔栏
toolBar.add(loginButton);
toolBar.add(logoffButton);
toolBar.addSeparator();//添加分隔栏
toolBar.add(exitButton);
contentPane.add(toolBar,BorderLayout.NORTH);
checkbox = new JCheckBox("悄悄话");
checkbox.setSelected(false);
actionlist = new JComboBox();
actionlist.addItem("微笑地");
actionlist.addItem("高兴地");
actionlist.addItem("轻轻地");
actionlist.addItem("生气地");
actionlist.setSelectedIndex(0);
//初始时
loginButton.setEnabled(true);
logoffButton.setEnabled(false);
//为菜单栏添加事件监听
loginItem.addActionListener(this);
logoffItem.addActionListener(this);
exitItem.addActionListener(this);
userItem.addActionListener(this);
connectItem.addActionListener(this);
helpItem.addActionListener(this);
//添加按钮的事件侦听
loginButton.addActionListener(this);
logoffButton.addActionListener(this);
userButton.addActionListener(this);
connectButton.addActionListener(this);
exitButton.addActionListener(this);
combobox = new JComboBox();
combobox.insertItemAt("所有人",0);
combobox.setSelectedIndex(0);
messageShow = new JTextArea();
messageShow.setEditable(false);
//添加滚动条
messageScrollPane = new JScrollPane(messageShow,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
messageScrollPane.setPreferredSize(new Dimension(400,400));
messageScrollPane.revalidate();
clientMessage = new JTextField(23);
clientMessage.setEnabled(false);
clientMessageButton = new JButton();
clientMessageButton.setText("发送");
//添加系统消息的事件侦听
clientMessage.addActionListener(this);
clientMessageButton.addActionListener(this);
sendToLabel = new JLabel("发送至:");
express = new JLabel(" 表情: ");
messageLabel = new JLabel("发送消息:");
downPanel = new JPanel();
girdBag = new GridBagLayout();
downPanel.setLayout(girdBag);
girdBagCon = new GridBagConstraints();
girdBagCon.gridx = 0;
girdBagCon.gridy = 0;
girdBagCon.gridwidth = 5;
girdBagCon.gridheight = 2;
girdBagCon.ipadx = 5;
girdBagCon.ipady = 5;
JLabel none = new JLabel(" ");
girdBag.setConstraints(none,girdBagCon);
downPanel.add(none);
girdBagCon = new GridBagConstraints();
girdBagCon.gridx = 0;
girdBagCon.gridy = 2;
girdBagCon.insets = new Insets(1,0,0,0);
//girdBagCon.ipadx = 5;
//girdBagCon.ipady = 5;
girdBag.setConstraints(sendToLabel,girdBagCon);
downPanel.add(sendToLabel);
girdBagCon = new GridBagConstraints();
girdBagCon.gridx =1;
girdBagCon.gridy = 2;
girdBagCon.anchor = GridBagConstraints.LINE_START;
girdBag.setConstraints(combobox,girdBagCon);
downPanel.add(combobox);
girdBagCon = new GridBagConstraints();
girdBagCon.gridx =2;
girdBagCon.gridy = 2;
girdBagCon.anchor = GridBagConstraints.LINE_END;
girdBag.setConstraints(express,girdBagCon);
downPanel.add(express);
girdBagCon = new GridBagConstraints();
girdBagCon.gridx = 3;
girdBagCon.gridy = 2;
girdBagCon.anchor = GridBagConstraints.LINE_START;
//girdBagCon.insets = new Insets(1,0,0,0);
//girdBagCon.ipadx = 5;
//girdBagCon.ipady = 5;
girdBag.setConstraints(actionlist,girdBagCon);
downPanel.add(actionlist);
girdBagCon = new GridBagConstraints();
girdBagCon.gridx = 4;
girdBagCon.gridy = 2;
girdBagCon.insets = new Insets(1,0,0,0);
//girdBagCon.ipadx = 5;
//girdBagCon.ipady = 5;
girdBag.setConstraints(checkbox,girdBagCon);
downPanel.add(checkbox);
girdBagCon = new GridBagConstraints();
girdBagCon.gridx = 0;
girdBagCon.gridy = 3;
girdBag.setConstraints(messageLabel,girdBagCon);
downPanel.add(messageLabel);
girdBagCon = new GridBagConstraints();
girdBagCon.gridx = 1;
girdBagCon.gridy = 3;
girdBagCon.gridwidth = 3;
girdBagCon.gridheight = 1;
girdBag.setConstraints(clientMessage,girdBagCon);
downPanel.add(clientMessage);
girdBagCon = new GridBagConstraints();
girdBagCon.gridx = 4;
girdBagCon.gridy = 3;
girdBag.setConstraints(clientMessageButton,girdBagCon);
downPanel.add(clientMessageButton);
showStatus = new JTextField(35);
showStatus.setEditable(false);
girdBagCon = new GridBagConstraints();
girdBagCon.gridx = 0;
girdBagCon.gridy = 5;
girdBagCon.gridwidth = 5;
girdBag.setConstraints(showStatus,girdBagCon);
downPanel.add(showStatus);
contentPane.add(messageScrollPane,BorderLayout.CENTER);
contentPane.add(downPanel,BorderLayout.SOUTH);
//关闭程序时的操作
this.addWindowListener(
new WindowAdapter(){
public void windowClosing(WindowEvent e){
if(type == 1){
DisConnect();
}
System.exit(0);
}
});
}
/**
* 事件处理
*/
public void actionPerformed(ActionEvent e) {
Object obj = e.getSource();
if (obj == userItem || obj == userButton) { //用户信息设置
//调出用户信息设置对话框
UserConf userConf = new UserConf(this,userName);
userConf.setVisible(true);
userName = userConf.userInputName;
}
else if (obj == connectItem || obj == connectButton) { //连接服务端设置
//调出连接设置对话框
ConnectConf conConf = new ConnectConf(this,ip,port);
conConf.setVisible(true);
ip = conConf.userInputIp;
port = conConf.userInputPort;
}
else if (obj == loginItem || obj == loginButton) { //登录
Connect();
}
else if (obj == logoffItem || obj == logoffButton) { //注销
DisConnect();
showStatus.setText("");
}
else if (obj == clientMessage || obj == clientMessageButton) { //发送消息
SendMessage();
clientMessage.setText("");
}
else if (obj == exitButton || obj == exitItem) { //退出
int j=JOptionPane.showConfirmDialog(
this,"真的要退出吗?","退出",
JOptionPane.YES_OPTION,JOptionPane.QUESTION_MESSAGE);
if (j == JOptionPane.YES_OPTION){
if(type == 1){
DisConnect();
}
System.exit(0);
}
}
else if (obj == helpItem) { //菜单栏中的帮助
//调出帮助对话框
Help helpDialog = new Help(this);
helpDialog.setVisible(true);
}
}
public void Connect(){
try{
socket = new Socket(ip,port);
}
catch (Exception e){
JOptionPane.showConfirmDialog(
this,"不能连接到指定的服务器。\n请确认连接设置是否正确。","提示",
JOptionPane.DEFAULT_OPTION,JOptionPane.WARNING_MESSAGE);
return;
}
try{
output = new ObjectOutputStream(socket.getOutputStream());
output.flush();
input = new ObjectInputStream(socket.getInputStream() );
output.writeObject(userName);
output.flush();
recvThread = new ClientReceive(socket,output,input,combobox,messageShow,showStatus);
recvThread.start();
loginButton.setEnabled(false);
loginItem.setEnabled(false);
userButton.setEnabled(false);
userItem.setEnabled(false);
connectButton.setEnabled(false);
connectItem.setEnabled(false);
logoffButton.setEnabled(true);
logoffItem.setEnabled(true);
clientMessage.setEnabled(true);
messageShow.append("连接服务器 "+ip+":"+port+" 成功...\n");
type = 1;//标志位设为已连接
}
catch (Exception e){
System.out.println(e);
return;
}
}
public void DisConnect(){
loginButton.setEnabled(true);
loginItem.setEnabled(true);
userButton.setEnabled(true);
userItem.setEnabled(true);
connectButton.setEnabled(true);
connectItem.setEnabled(true);
logoffButton.setEnabled(false);
logoffItem.setEnabled(false);
clientMessage.setEnabled(false);
if(socket.isClosed()){
return ;
}
try{
output.writeObject("用户下线");
output.flush();
input.close();
output.close();
socket.close();
messageShow.append("已经与服务器断开连接...\n");
type = 0;//标志位设为未连接
}
catch (Exception e){
//
}
}
public void SendMessage(){
String toSomebody = combobox.getSelectedItem().toString();
String status = "";
if(checkbox.isSelected()){
status = "悄悄话";
}
Icon face=new ImageIcon("face/smile.gif");
String action = actionlist.getSelectedItem().toString();
String message = clientMessage.getText();
if(socket.isClosed()){
return ;
}
try{
output.writeObject("聊天信息");
output.flush();
output.writeObject(toSomebody);
output.flush();
output.writeObject(action);
output.flush();
output.writeObject(action);
output.flush();
output.writeObject(message);
output.flush();
}
catch (Exception e){
//
}
}
public static void main(String[] args) {
new ChatClient();
}
}
ClientReceive.java
/*
* 聊天客户端消息收发类
*/
public class ClientReceive extends Thread {
private JComboBox combobox;
private JTextArea textarea;
Socket socket;
ObjectOutputStream output;
ObjectInputStream input;
JTextField showStatus;
public ClientReceive(Socket socket,ObjectOutputStream output,
ObjectInputStream input,JComboBox combobox,JTextArea textarea,JTextField showStatus){
this.socket = socket;
this.output = output;
this.input = input;
this.combobox = combobox;
this.textarea = textarea;
this.showStatus = showStatus;
}
public void run(){
while(!socket.isClosed()){
try{
String type = (String)input.readObject();
if(type.equalsIgnoreCase("系统信息")){
String sysmsg = (String)input.readObject();
textarea.append("系统信息: "+sysmsg);
}
else if(type.equalsIgnoreCase("服务关闭")){
output.close();
input.close();
socket.close();
textarea.append("服务器已关闭!\n");
break;
}
else if(type.equalsIgnoreCase("聊天信息")){
String message = (String)input.readObject();
textarea.append(message);
}
else if(type.equalsIgnoreCase("用户列表")){
String userlist = (String)input.readObject();
String usernames[] = userlist.split("\n");
combobox.removeAllItems();
int i =0;
combobox.addItem("所有人");
while(i < usernames.length){
combobox.addItem(usernames[i]);
i ++;
}
combobox.setSelectedIndex(0);
showStatus.setText("在线用户 " + usernames.length + " 人");
}
}
catch (Exception e ){
System.out.println(e);
}
}
}
}
ConnectConf.java
/**
* 生成连接信息输入的对话框
* 让用户输入连接服务器的IP和端口
*/
public class ConnectConf extends JDialog {
/**
*
*/
private static final long serialVersionUID = 1L;
JPanel panelUserConf = new JPanel();
JButton save = new JButton();
JButton cancel = new JButton();
JLabel DLGINFO=new JLabel(" 默认连接设置为 127.0.0.1:8888");
JPanel panelSave = new JPanel();
JLabel message = new JLabel();
String userInputIp;
int userInputPort;
JTextField inputIp;
JTextField inputPort;
public ConnectConf(JFrame frame,String ip,int port) {
super(frame, true);
this.userInputIp = ip;
this.userInputPort = port;
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
//设置运行位置,使对话框居中
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation( (int) (screenSize.width - 400) / 2 + 50,
(int) (screenSize.height - 600) / 2 + 150);
this.setResizable(false);
}
private void jbInit() throws Exception {
this.setSize(new Dimension(300, 130));
this.setTitle("连接设置");
message.setText(" 请输入服务器的IP地址:");
inputIp = new JTextField(10);
inputIp.setText(userInputIp);
inputPort = new JTextField(4);
inputPort.setText(""+userInputPort);
save.setText("保存");
cancel.setText("取消");
panelUserConf.setLayout(new GridLayout(2,2,1,1));
panelUserConf.add(message);
panelUserConf.add(inputIp);
panelUserConf.add(new JLabel(" 请输入服务器的端口号:"));
panelUserConf.add(inputPort);
panelSave.add(new Label(" "));
panelSave.add(save);
panelSave.add(cancel);
panelSave.add(new Label(" "));
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(panelUserConf, BorderLayout.NORTH);
contentPane.add(DLGINFO, BorderLayout.CENTER);
contentPane.add(panelSave, BorderLayout.SOUTH);
//保存按钮的事件处理
save.addActionListener(
new ActionListener() {
public void actionPerformed (ActionEvent a) {
int savePort;
//判断端口号是否合法
try{
userInputIp = "" + InetAddress.getByName(inputIp.getText());
userInputIp = userInputIp.substring(1);
}
catch(UnknownHostException e){
DLGINFO.setText(
" 错误的IP地址!");
return;
}
//userInputIp = inputIP;
//判断端口号是否合法
try{
savePort = Integer.parseInt(inputPort.getText());
if(savePort<1 || savePort>65535){
DLGINFO.setText(" 侦听端口必须是0-65535之间的整数!");
inputPort.setText("");
return;
}
userInputPort = savePort;
dispose();
}
catch(NumberFormatException e){
DLGINFO.setText(" 错误的端口号,端口号请填写整数!");
inputPort.setText("");
return;
}
}
}
);
//关闭对话框时的操作
this.addWindowListener(
new WindowAdapter(){
public void windowClosing(WindowEvent e){
DLGINFO.setText(" 默认连接设置为 127.0.0.1:8888");
}
}
);
//取消按钮的事件处理
cancel.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
DLGINFO.setText(" 默认连接设置为 127.0.0.1:8888");
dispose();
}
});
}
}
UserConf.java
/**
* 生成用户信息输入对话框的类
* 让用户输入自己的用户名
*/
public class UserConf extends JDialog {
/**
*
*/
private static final long serialVersionUID = 1L;
JPanel panelUserConf = new JPanel();
JButton save = new JButton();
JButton cancel = new JButton();
JLabel DLGINFO=new JLabel(" 默认用户名为:狂狂");
JPanel panelSave = new JPanel();
JLabel message = new JLabel();
String userInputName;
JTextField userName ;
public UserConf(JFrame frame,String str) {
super(frame, true);
this.userInputName = str;
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
//设置运行位置,使对话框居中
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation( (int) (screenSize.width - 400) / 2 + 50,
(int) (screenSize.height - 600) / 2 + 150);
this.setResizable(false);
}
private void jbInit() throws Exception {
this.setSize(new Dimension(300, 120));
this.setTitle("用户设置");
message.setText("请输入用户名:");
userName = new JTextField(10);
userName.setText(userInputName);
save.setText("保存");
cancel.setText("取消");
panelUserConf.setLayout(new FlowLayout());
panelUserConf.add(message);
panelUserConf.add(userName);
panelSave.add(new Label(" "));
panelSave.add(save);
panelSave.add(cancel);
panelSave.add(new Label(" "));
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(panelUserConf, BorderLayout.NORTH);
contentPane.add(DLGINFO, BorderLayout.CENTER);
contentPane.add(panelSave, BorderLayout.SOUTH);
//保存按钮的事件处理
save.addActionListener(
new ActionListener() {
public void actionPerformed (ActionEvent a) {
if(userName.getText().equals("")){
DLGINFO.setText(
" 用户名不能为空!");
userName.setText(userInputName);
return;
}
else if(userName.getText().length() > 15){
DLGINFO.setText(" 用户名长度不能大于15个字符!");
userName.setText(userInputName);
return;
}
userInputName = userName.getText();
dispose();
}
}
);
//关闭对话框时的操作
this.addWindowListener(
new WindowAdapter(){
public void windowClosing(WindowEvent e){
DLGINFO.setText(" 默认用户名为:狂狂");
}
}
);
//取消按钮的事件处理
cancel.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e){
DLGINFO.setText(" 默认用户名为:狂狂");
dispose();
}
});
}
}
Help.java
/**
* 生成设置对话框的类
*/
public class Help extends JDialog {
/**
*
*/
private static final long serialVersionUID = 1L;
JPanel titlePanel = new JPanel();
JPanel contentPanel = new JPanel();
JPanel closePanel = new JPanel();
JButton close = new JButton();
JLabel title = new JLabel("聊天室客户端帮助");
JTextArea help = new JTextArea();
Color bg = new Color(255,255,255);
public Help(JFrame frame) {
super(frame, true);
try {
jbInit();
}
catch (Exception e) {
e.printStackTrace();
}
//设置运行位置,使对话框居中
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setLocation( (int) (screenSize.width - 400) / 2 + 25,
(int) (screenSize.height - 320) / 2);
this.setResizable(false);
}
private void jbInit() throws Exception {
this.setSize(new Dimension(350, 270));
this.setTitle("帮助");
titlePanel.setBackground(bg);;
contentPanel.setBackground(bg);
closePanel.setBackground(bg);
help.setText("1、设置所要连接服务端的IP地址和端口"+
"(默认设置为\n 127.0.0.1:8888)。\n"+
"2、输入你的用户名(默认设置为:狂狂)。\n"+
"3、点击“登录”便可以连接到指定的服务器;\n"+
" 点击“注销”可以和服务器端开连接。\n"+
"4、选择需要接受消息的用户,在消息栏中写入消息,\n"+
" 同时选择表情,之后便可发送消息。\n");
help.setEditable(false);
titlePanel.add(new Label(" "));
titlePanel.add(title);
titlePanel.add(new Label(" "));
contentPanel.add(help);
closePanel.add(new Label(" "));
closePanel.add(close);
closePanel.add(new Label(" "));
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(titlePanel, BorderLayout.NORTH);
contentPane.add(contentPanel, BorderLayout.CENTER);
contentPane.add(closePanel, BorderLayout.SOUTH);
close.setText("关闭");
//事件处理
close.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
dispose();
}
});
}
软件使用方法和操作步骤
(1)首先运行 ChatServer 工程文件,会出现如下画面
(2)、选择端口设置,可以设置您要侦听的端口号,不设置将默认端口号为 8888;
(3)、选择启动服务,服务器开始在您刚才设置的端口中侦听;
(4)、运行 ChatClient 工程文件,打开如下画面
(5)、选择用户设置可以设置自己的登录名
(6)、选择连接设置可以设置您的 IP 地址和端口号
(7)、选择登录进入聊天室并可以开始跟所有人或者个别其他用户聊天,其中可以选择心情等;
(8)、在服务端也可以选择给所有人或者个别用户发送消息;
注意:客户端只有在服务端开启的情况下才能正常聊天。