江西农业大学
聊天室程序设计报告
题 目:聊天室程序设计
院系名称: 软件学院
专业名称: 软件工程嵌入式方向
班 级: 099班
学生姓名: 余连俊
学号(8位):20091962
指导教师: 何火娇
一. 设计目的
在当今信息时代,越来越多的聊天工具被应用,java语言是当今流行的网络编程语言,它具有面向对象,与平台无关,安全,多线程等特点。使用java语言不仅可以实现大型企业级的分布式应用系统,还能够为小型的的,嵌入式设备进行应用程序的开发。面向对象的开发是当今世界最流行的开发方法,它不仅具有更贴近自然地语义,而且有利于软件的维护和继承,锻炼我们熟练地应用面向对象的思想和设计方法解决实际问题的能力。
此次设计的题目为聊天室,可以巩固所学的java语言基础知识,增进java语言的编辑基本功,拓宽常用类库的应用,使我们通过教学环节与手段,把所学的课程及相关知识加以融会贯通,全面掌握java语言的编程思想及面向对象程序设计的方法,为今后从事实际工作打下坚实的基础。
二. 设计内容
运用java程序编写聊天室,实现简单的聊天功能。它是图形界面,线程,流与文件系统等技术的综合应用。其界面主要采用了java.awt包,java.swing包等。程序实现了聊天室的基本功能,其中有:
1.启动服务器。
2.启动登陆界面。
3.进入客户端界面。
4.发送信息。
5.离开客户端界面。
6.关闭服务器。
三.概要设计
1.首先通过继承JFrame类构造服务器界面。使服务器能够为每一个客户启动一个线程,记录客户的上下线行为、登陆的ip,放送消息的时刻,监视客户的聊天记录,对于出现不文明的话语,在客户端进行屏蔽,并统计出现不文明话语的次数,超过三次提示警告信息,并将其踢出聊天室。
2.设计客户端程序使其能够完成正常的通信功能,并且可以更改背景颜色,设置字体颜色。
3.设计登录程序,使其显示登录的ip地址端口号,以及用户昵称,通过登录按钮可以调用客户端程序,实现聊天功能。
1.功能模块图;
2.各个模块详细的功能描述。
1,服务器端功能描述:记录用户的上下线,显示系统时间,显示在线用户的用户名和ip地址,监听客户的聊天记录,刷新列表,启动服务,对于重复启动,显示警告信息,屏蔽用户的不文明言语,对于多次使用不文明用语的用户将其踢出聊天室,并向客户端放送相应信息。向个客户端广播系统消息。
2.登录界面
显示登录的端口号,ip地址,昵称,通过按动登录按钮进入客户端界面,或取消登录操作。
3.客户端功能描述;显示登录昵称,显示聊天记录,更改背景颜色,设置字体颜色,放送信息,关闭聊天窗口。
四.详细设计
程序中引入的包:
import java.net.*;
import java.io.*;
import java.util.*;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
服务器端代码中用户自定义的类:
类名;server
作用:服务器的启动
继承的类:JFrame,构造服务器界面。
定义的对象:
TextArea 文本域
java.awt.List 在线客户列表
ServerSocket 服务器
Socket 客户端
主要的成员方法:
void init() 构造服务器端的界面
public void actionPerformed(ActionEvent e) 按钮监听器
public void keyPressed(KeyEvent e) 键盘监听器
类名:start
作用: 启动服务器的线程
继承的类名:thread
主要的成员方法:
public void run() 启动服务器的线程
类名; SgService
继承的类名:thread
主要的成员方法:public void run() 处理单个客户的线程
(2)用户登录相关的类:
类名:login
作用:构造登录界面
继承的类:JFrame
实现的接口: ActionListener
构造方法; public Login()
主要的成员方法:public void actionPerformed(ActionEvent e) 实现"登录"和"取消"按钮的监听器
类名:LoginStart
继承的类:Thread
主要的方法; public void run()
作用:如果登录成功,则弹出客户端聊天窗口,如果登录失败,则打印出"登录失
败",以告知客户
(3)客户端相关的类
类名:Client
继承的类:JFrame
构造方法; public Client(PrintStream out,BufferedReader in,String name)
主要的成员方法:public void receive()接受服务端发来的信息
1.功能函数的调用关系图
2.各功能函数的数据流程图
互相通信
3.重点设计及编码
//聊天室服务器
import java.net.*;
import java.io.*;
import java.util.*;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
class Server extends JFrame {
private TextArea msgA = new TextArea();//服务器用来输入的文本域
private TextArea area = new TextArea();//服务器显示消息的文本域
private TextField portT = new TextField("8888");
private java.awt.List list = new java.awt.List();//在线客户列表
private ServerSocket server;//服务器
private Socket client;//客户端
private Map<String, Socket> container = new HashMap<String, Socket>();//HashMap
void init() {
//设置几个面板//
Panel p1 = new Panel();
Panel p2 = new Panel();
Panel p3 = new Panel();
Panel p4 = new Panel();
Panel p5 = new Panel();
Panel p6 = new Panel();
Panel p7 = new Panel();
Panel p8 = new Panel();
Panel p9 = new Panel();
Panel p10 = new Panel();
//设置两个主要的文本框
Label lb1 = new Label("服务器界面");
lb1.setBackground(new Color(250,120,150));
Label lb2 = new Label("port");
BorderLayout border1 = new BorderLayout();
p1.setLayout(border1);
p1.add(BorderLayout.CENTER, area);
BorderLayout border2 = new BorderLayout();
p5.setLayout(border2);
p5.add(BorderLayout.NORTH, p3);
p5.add(BorderLayout.CENTER, msgA);
BorderLayout border3 = new BorderLayout();
p7.setLayout(border3);
p7.add(BorderLayout.CENTER, p1);
p7.add(BorderLayout.SOUTH, p5);
p7.setBackground(new Color(250,150,200));
JButton startButton = new JButton("启动服务");
JButton sendButton = new JButton("发送");
JButton flushButton = new JButton("刷新列表");
FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
p8.setLayout(fl);
p8.add(lb2);
p8.add(portT);
p8.add(startButton);
p8.add(sendButton);
p8.add(flushButton);
p8.setBackground(new Color(250,120,150));
BorderLayout border4 = new BorderLayout();
p9.setLayout(border4);
p9.add(BorderLayout.CENTER, p7);
p9.add(BorderLayout.SOUTH, p8);
p10.setLayout(new BorderLayout());
Label lb3 = new Label("在线用户");
lb3.setBackground(new Color(250,120,150));
p10.add(lb3,BorderLayout.NORTH);
p10.add(list,BorderLayout.CENTER);
area.setEditable(false);
BorderLayout border = new BorderLayout();
this.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent arg0) {
System.exit(0);
}
});
this.setLayout(border);
this.add(BorderLayout.NORTH, lb1);
this.add(BorderLayout.CENTER, p9);
this.add(BorderLayout.EAST, p10);
this.setTitle("聊天服务器 ");
this.setBounds(100, 100, 600, 550);
this.setVisible(true);
//"启动服务"按钮监听器
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Thread t = new Start();
t.start();
}
});//启动按钮监听器----end
//发送按钮监听器
sendButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String msg = msgA.getText();
if (msg != null && msg != "") {//如果消息不为空
Calendar ca = new GregorianCalendar();
int hh = ca.get(Calendar.HOUR_OF_DAY);
int mm = ca.get(Calendar.MINUTE);
int ss = ca.get(Calendar.SECOND);
String time = new String(hh + ":" + mm + ":" + ss + " ");
broadcast(time + "[系统消息]: " + msg);//那么广播这条系统消息
area.append(time + "[系统消息]: " + msg + "\n");
msgA.setText("");
}
}
});//end
//刷新按钮监听器
flushButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (list != null) {
list.removeAll();//清空在线用户列表,以便刷新
}
Set<Map.Entry<String, Socket>> mySet = container.entrySet();
for (Iterator<Map.Entry<String, Socket>> it = mySet.iterator(); it.hasNext();) {//刷新列表
Map.Entry<String, Socket> me = it.next();
String aClientName = me.getKey();
Socket aClient = me.getValue();
String aClientAddress = aClient.getInetAddress().getHostAddress();
list.add(aClientName + "----" + aClientAddress);
}//end of for
}//end of actionPerformed
});//end
msgA.addKeyListener(new KeyAdapter(){//键盘监听器---按下Enter发送信息
public void keyPressed(KeyEvent e){
int value = e.getKeyCode();
if(value == KeyEvent.VK_ENTER){
String msg = msgA.getText();
Calendar ca = new GregorianCalendar();
int hh = ca.get(Calendar.HOUR_OF_DAY);
int mm = ca.get(Calendar.MINUTE);
int ss = ca.get(Calendar.SECOND);
String time = new String(hh + ":" + mm + ":" + ss + " ");
broadcast(time + "[系统消息]: " + msg);//那么广播这条系统消息
area.append(time + "[系统消息]: " + msg + "\n");
msgA.setText("");
}
}
});
}//end of init()
class Start extends Thread //启动服务器的线程
{
public void run() {
try {
if(server != null && !server.isClosed()){
area.append("警告!服务器已在启动中!\n");
}
else{
server = new ServerSocket(Integer.parseInt(portT.getText()));//启动服务器
area.append((new Date()).toString() +'\n'+ "[恭喜!^_^]"+"服务器启动成功!" + "\n\n");
}
while(true){
client = server.accept();
Thread sgS = new SgService(client);//为每位登录的客户创建一个线程
sgS.start();//启动客户线程
}
}
catch (Exception e) {
System.out.println("cuowu 1");
e.printStackTrace();
}
}//end of run()
} //end of Start
//处理单个客户的线程
class SgService extends Thread//SgService---begin
{
private Socket client = null;//客户套接字
private BufferedReader br = null;//输入流
private PrintStream ps = null;
private int counter = 0;//记录是第几次接受用户的信息
private int mark = 0;//记录用户说不文明用语的次数
private String name = null;
SgService(Socket client) {//构造方法
this.client = client;
public void run() {//run()----begin
try {
br = new BufferedReader(new InputStreamReader(client.getInputStream()));
ps = new PrintStream(client.getOutputStream());
String msg = null;
while ((msg = br.readLine()) != null) {//无限循环,意味着可以和客户进行无限次交互
if(counter == 0){
name = msg;
container.put(name,client);
list.add(name + "----" + client.getInetAddress().getHostAddress());
Calendar ca = new GregorianCalendar();
int hh = ca.get(Calendar.HOUR_OF_DAY);
int mm = ca.get(Calendar.MINUTE);
int ss = ca.get(Calendar.SECOND);
String time = hh + ":" + mm + ":" + ss + " ";
broadcast(time + name + "上线了!");
area.append(time + name + "上线了!\n\n");
counter ++;
}
else{
if(msg.equals("你是个笨蛋")){
mark = mark + 1;
if(mark < 3){
Calendar ca = new GregorianCalendar();
int hh = ca.get(Calendar.HOUR_OF_DAY);
int mm = ca.get(Calendar.MINUTE);
int ss = ca.get(Calendar.SECOND);
String time = new String(hh + ":" + mm + ":" + ss + " ");
singleCom(time +"[系统消息]: 警告!请注意文明用语!" ,client);
//在服务器端显示这个用户说不文明语言的次数
area.append(time + "[系统消息]: " + name +"第" + mark+ "次使用了不文明用语,特此警告!");
}
else{//如果客户第三次说不文明的话,系统就把他踢出聊天室
Calendar ca = new GregorianCalendar();
int hh = ca.get(Calendar.HOUR_OF_DAY);
int mm = ca.get(Calendar.MINUTE);
int ss = ca.get(Calendar.SECOND);
String time = new String(hh + ":" + mm + ":" + ss + " ");
if(mark == 3){
singleCom(time + "[系统消息]: 由于您多次使用了不文明用语,经警告无效,现已被踢出聊天室!" ,client);//告诉他本人
}
removeClient(name,client);//从系统列表中删除这个人的信息
//并向所有人广播这一消息
broadcast(time + "[系统消息]: " + name + "由于多次使用了不文明用语,经警告无效,现已被踢出聊天室!");
area.append(time + "[系统消息]: " + name + "由于多次使用了不文明用语,经警告无效,现已被踢出聊天室!\n");
}
}
else{
Calendar ca = new GregorianCalendar();
int hh = ca.get(Calendar.HOUR_OF_DAY);
int mm = ca.get(Calendar.MINUTE);
int ss = ca.get(Calendar.SECOND);
String time = new String(hh + ":" + mm + ":" + ss + " ");
area.append(time + name+"说:"+msg + "\n\n");//将客户发送的消息显示在服务器的窗口中
broadcast(time + name+"说:"+msg+"\n");//同时向每个在线客户广播这则消息
}
}
}//end of while
}
catch (Exception ex) {
Calendar ca = new GregorianCalendar();
int hh = ca.get(Calendar.HOUR_OF_DAY);
int mm = ca.get(Calendar.MINUTE);
int ss = ca.get(Calendar.SECOND);
String time = new String(hh + ":" + mm + ":" + ss + " ");
area.append(time + name + "下线了!\n");
broadcast(time + name + "下线了!\n");
list.remove(name + "----" + client.getInetAddress().getHostAddress());
container.remove(name);
}
}//run()---end
}//SgService-----end
void removeClient(String name,Socket client){//踢人
list.remove(name + "----" + client.getInetAddress().getHostAddress());
container.remove(name);
}
//广播
void broadcast(String msg) {//broadcast()-----begin
Set<Map.Entry<String, Socket>> mySet = container.entrySet();
Iterator<Map.Entry<String, Socket>> it = mySet.iterator();
String message = msg;
while (it.hasNext()) {//如果列表中还有客户
Map.Entry<String, Socket> me = it.next();
Socket tempClient = me.getValue();
singleCom(message, tempClient);//就把消息向发给这个客户
}
}//broadcast()-----end
//向单个客户发送消息
private void singleCom(String msg, Socket aClient) {//singleCommunication------begin
try {
PrintStream pw = new PrintStream(aClient.getOutputStream());
String message = msg;
pw.println(msg);
} catch (Exception ex) {
ex.printStackTrace();
}
}//singleCommunication----end
public static void main(String[] args) {
new Server().init(); //启动服务器窗口
System.out.println("服务器:");
}
}//end of Server
//登录界面:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
classLoginextends JFrame implements ActionListener
{
TextField txtPort;
TextField txtIP;
TextField txtName;
JButton btnLogin;
JButton btnCancel;
Socket toServer;
BufferedReader in;
PrintStream out;
public Login(){
Panel p1=new Panel();
Panel p2=new Panel();
Panel p3=new Panel();
Panel p4=new Panel();
Panel p5=new Panel();
Label l1=new Label("port");
Label l2=new Label("IP");
Label l3=new Label("昵称");
txtPort=new TextField("8888",15);
txtIP=new TextField("192.168.55.1",15);
txtName=new TextField("小敏",15);
p2.add(l1);
p2.add(txtPort);
p3.add(l2);
p3.add(txtIP);
p4.add(l3);
p4.add(txtName);
GridLayout g=new GridLayout(3,1);//三行一列的网格布局管理器
p1.setLayout(g);//设置布局管理器
p1.add(p2);//将组件添加到面板中
p1.add(p3);
p1.add(p4);
p1.setBackground(new Color(100,250,100));//更改面板颜色
btnLogin=new JButton("登陆");
btnCancel=new JButton("取消");
btnLogin.addActionListener(this);//添加监听器
btnCancel.addActionListener(this);
BorderLayout border1=new BorderLayout();//边界布局管理器
this.setLayout(border1);
p5.add(btnLogin);
p5.add(btnCancel);
this.add(BorderLayout.SOUTH,p5);
this.add(BorderLayout.CENTER,p1);
this.setSize(350,250);
this.setTitle("用户登录");
this.setResizable(false);
this.setLocation(450,250);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口
}
//实现"登录"和"取消"按钮的监听器
publicvoid actionPerformed(ActionEvent e){
JButton button = (JButton)e.getSource();
if(button.equals(btnCancel)){
dispose();//如果客户按了"取消"按钮,那么登录窗口就关闭
}
else {//如果客户按了"登录",那么就连接服务器
LoginStart ls = new LoginStart();
ls.start();
this.dispose();
}
}
class LoginStart extends Thread
{
publicvoid run(){
int port = Integer.parseInt(txtPort.getText());//取得端口号
String ip = txtIP.getText();//取得要连接的服务器的IP地址
String name = txtName.getText();//取得客户姓名
try {
toServer = new Socket(ip,port); //连接服务端 socket("主机名",端口号);
in = new BufferedReader(new InputStreamReader
(toServer.getInputStream())); //建立输入流
out = new PrintStream(toServer.getOutputStream());//输出流
new Client(out,in,name);//如果登录成功,则弹出客户端聊天窗口
dispose(); //同时关闭登录窗口
}
catch (Exception e2) {
System.out.println("登录失败!");//如果登录失败,则打印出"登录失败",以告知客户
e2.printStackTrace();
}
}
}
publicstaticvoid main(String[] args){
new Login();//启动登录窗口
}
}
//聊天室客户端
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
import java.awt.*;
class Client extends JFrame
{
PrintStream out; //输出流
BufferedReader in; //输入流
String name;
TextArea area = new TextArea();
TextArea msgA = new TextArea();
TextField frdNameT = new TextField(10);
List list =new List();
Panel p10=new Panel();
JButton sendButton = new JButton("发送");
JButton closeButton = new JButton("关闭");
JButton jb1 = new JButton("设置背景颜色");
JButton jb2 = new JButton("设置字体颜色");
Label lb1 = new Label("昵称:");
void init(){
this.pack();
//设置几个面板
Panel p1=new Panel();
Panel p2=new Panel();
Panel p3=new Panel();
Panel p4=new Panel();
Panel p5=new Panel();
Panel p6=new Panel();
Panel p7=new Panel();
Panel p8=new Panel();
Panel p9=new Panel();
lb1.setBackground(Color.pink);
//设置两个主要的文本框
BorderLayout border1=new BorderLayout();
p1.setLayout(border1);
p1.add(BorderLayout.CENTER,area);
area.setEditable(false);
FlowLayout f2=new FlowLayout(FlowLayout.LEFT);
p3.setLayout(f2);
p3.add(jb1);
p3.add(jb2);
BorderLayout border2=new BorderLayout();
p5.setLayout(border2);
p5.add(BorderLayout.NORTH,p3);
p5.add(BorderLayout.CENTER,msgA);
p5.setBackground(Color.pink);
BorderLayout border3=new BorderLayout();
p7.setLayout(border3);
p7.add(BorderLayout.CENTER,p1);
p7.add(BorderLayout.SOUTH,p5);
FlowLayout fl=new FlowLayout(FlowLayout.RIGHT);
p8.setLayout(fl);
p8.add(sendButton);
p8.add(closeButton);
p8.setBackground(Color.pink);
BorderLayout border4=new BorderLayout();
p9.setLayout(border4);
p9.add(BorderLayout.CENTER,p7);
p9.add(BorderLayout.SOUTH,p8);
//窗口关闭
this.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent arg0) {
System.exit(0);
}
});
BorderLayout border5=new BorderLayout();
p10.setLayout(border5);
p10.add(BorderLayout.CENTER,list);
p10.setBackground(Color.pink);
//添加背景颜色的监听器
jb1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
JColorChooser chooser4=new JColorChooser();
Color color=chooser4.showDialog(Client.this,"我的调色盘",Color.yellow);
area.setBackground(color);
msgA.setBackground(color);
list.setBackground(color);
}
});
//添加字体颜色的监听器
jb2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
JColorChooser chooser4=new JColorChooser();
Color color=chooser4.showDialog(Client.this,"我的调色盘",Color.yellow);
area.setForeground(color);
msgA.setForeground(color);
list.setForeground(color);
}
});
BorderLayout border=new BorderLayout();
this.setLayout(border);
this.add(BorderLayout.NORTH,lb1);
this.add(BorderLayout.CENTER,p9);
this.add(BorderLayout.EAST,p10);
this.setTitle("聊天室客户端");
this.setSize(700,600);
this.setVisible(true);
sendButton.addActionListener(new ActionListener(){//"发送"按钮监听器
public void actionPerformed(ActionEvent e){
String msg = msgA.getText();
msgA.setText("");//清空输入文本域
out.println(msg);
}
});
msgA.addKeyListener(new KeyAdapter(){//键盘监听器---按下Enter发送信息
public void keyPressed(KeyEvent e){
int value = e.getKeyCode();
if(value == KeyEvent.VK_ENTER){
String msg = msgA.getText();
msgA.setText("");//清空输入文本域
out.println(msg);
}
}
});
closeButton.addActionListener(new ActionListener(){//"关闭"按钮监听器
public void actionPerformed(ActionEvent e){
System.exit(100);
}
});
}//end of init()
public Client(PrintStream out,BufferedReader in,String name) {//构造方法
this.out = out;
this.in = in;
this.name = name;
lb1.setText("昵称:"+ name);
init();//弹出客户端窗口
out.println(name);
receive();//开始与服务器通信
}
public void receive() { //接受服务端发来的信息。
try {
String text = null;
while ((text = in.readLine()) != null) {
this.area.append(text + "\n"); //在area上添加读入的内容
}
}
catch (IOException e) {
e.printStackTrace();// TODO Auto-generated catch block
}
finally{
try
{
in.close();
out.close();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
}
五.测试数据及运行结果
服务器端界面
登录界面:
客户端界面:
1.正常测试数据和运行结果
小敏和小名的聊天记录,将小敏的字体颜色设置成红色,小名的客户端背景设置成蓝色。
服务器端监听两客户端聊天记录,显示在线用户的昵称及ip地址。
对于用户输入不文明用语,将其踢出聊天室。
在客户端显示相应的踢出信息。
2.异常测试数据及运行结果
由于设置的不文明用语是“你是个笨蛋”没有感叹号,所以不显示任何提示信息。
已经启动了一个服务器界面,在启动就会显示相应的警告信息。
六.调试情况,设计技巧及体会
由于输入的不文明信息和程序设计的不一致,所以不显示提示信息,更改之后,相应的提示信息,显示出来了,并将其踢出客户端。对于启动了两次服务器端的情况,必须先关掉以个服务器端,程序才得以正常运行。
1.改进方案
本次设计对于界面的设计太过于简单,不能更改客户端的字体,以及不能给客户端添加表情等,对于服务器端,以为聊天中的不文明用语不可能仅仅是那一句,所以应该尽可能多的添加不文明用语,或者设置成只要出现某个不文明的字,就认为是不文明用语,就将其屏蔽掉。
2.体会
在设计的过程中了解到自己的知识还是比较有限,使得程序还不够完善。回顾此次Java,至今仍感概很多,的确,从选题到定稿,从理论到实践,整整一星期的日子里,可以说是苦多于甜,但是可以学到很多东西,同时不仅可以巩固以前学的东西,而且学到了很多课本上学不到的东西,通过本次课程设计,使得自己懂得理论和实践相结合起来,从理论中得出结论,才能真正掌握这门技术,也提高了自己独立思考的能力,在设计的过程中,可以自己解决。真正体会到要将一门知识学的更深入,必须加强实践,多练习,才能发现问题所在。
七.参考文献
Java语言最新实用案例教程 杨树林 胡洁萍 清华出版社
Java项目开发实训教程 朱福喜 黄方胜 清华大学出版社