Java网络编程实践(基于C/S的控制台聊天室 )
课程设计说明书
课程名称:Java网络课程实践
题目:基于C/S的控制台聊天室
姓名:***
学号:***
教学班号:01
指导教师:***
兰州交通大学软件科学与工程系
二○XX 年 月 日
任务书:
题目 3:基于C/S的控制台聊天室(1人)
实现几个客户端的群聊(在控制台窗口显示聊天信息),信息经服务端转发和群发,多客户端使用多线程。基于Socket。注意:在每一个客户端窗口要显示所有参与聊天者的聊天内容,包括谁发言及发言内容。
文档内容要求:
1、在做上述题目时,正文要求包含4部分内容:1)项目简述和目的 2)解题基本思路;3)分析和设计 4)主要程序源代码;5)运行截图;5)总结和体会
2、文档装订顺序:1)封面;2)任务书;3)团队成员及分工列表;3)正文。
3、打印格式要求:
? 纸型:B5
? 页边距:上2.54cm;下2.54cm;左2.2cm;右1.9cm;装订线为0
? 字体、格式要求:
题目:2号黑体不加粗,居中,段前段后间距1行。子标题字体为4号黑体。所有的子标题均加粗,置于页面最左端,段前段后间距均设为0.5行。文章正文皆为5号宋体,行距为固定值22磅,字距为标准。图、表的标题文字采用小5号黑体,表中的文字采用小5号宋体
基于C/S的控制台聊天室
一、 设计目的
巩固所学的Java语言基础知识,增进Java语言的编辑基本功,拓展常用类库的应用,使我们通过教学环节与手段,把所学的课程及相关知识加以融会贯通,
二、设计内容
运用Java程序编写聊天室,实现简单的聊天功能。它是图形界面,线程,流与文件系统等技术的综合应用。其界面主要采用了Java.awt包,Java.swing包等。程序实现了聊天室的基本功能,其中有:
1. 启动服务器。
2. 启动登录界面。
3. 进入客户端界面。
4. 发送信息。
5. 离开客户端界面。
6. 关闭服务器。
三、 分析
1.首先通过继承JFrame类构造服务器界面。使服务器能够为每一个客户启动一个线程,记录客户的上下线行为、登录的IP,发送消息的时刻,监视客户的聊天记录,对于出现不文明的话语,在客户端进行屏蔽,并统计出现不文明用语的次数,超过三次提示警告信息,并将其踢出聊天室。
2.设计客户端程序使其能够完成正常的通信功能,并且可以更改背景颜色,设置字体颜色。
3.设计登录程序,使其显示登录的IP地址端口号,以及用户昵称,通过登录按钮可以调用客户端程序,实现;聊天功能。
四、详细设计
程序中引入的包:
import java.net.*;
import java.io.*;
import java.util.*;
import java.awt.event.*;
import java.awt.*;
import java.javax.swing.*;
1) 服务器端代码中用户自定义的类:
类名:Server
作用:服务器的启动
继承的类:JFrame,构造服务器界面。
定义的对象:
TextArea 文本域
Java.awt.List 在线客户列表
SeverSocket 服务器
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
实现的接口:ActionListener
构造方法:public Client(PrintStream out,BufferedReader in,String name)
主要的成员方法:public void receive() 接受服务端发来的信息
五.主要程序源代码
//聊天室服务器
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 f1=new FlowLayout(FlowLayout.LEFT);
p8.setLayout(f1);
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.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();
}
});//启动服务按钮监听器
//发送按钮监听器
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);
}
}
});
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("");
}
}
});
}
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();
}
}
}
//处理单个客户的线程
class SgService extends Thread
{
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.SECOND);
String time=hh+":"+mm+":"+"";
broadcast(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+"[系统消息]:"+name+"由于多次使用了不文明用语,经警告无效,现已被踢出聊天室!",client);//告诉他本人
}
removeClient(name,client);//从系统列表中删除这个人的信息
//并向所有人广播这一消息
broadcast(time+"[系统消息]:"+name+"由于多次使用了不文明用语,经警告无效,现已被踢出聊天室!\n");
area.append(time+"[系统消息]:"+name+"由于多次使用了不文明用语,经警告无效,现已被踢出聊天室!");
}
}
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");//同时向每个在线客户广播这则消息
}
}
}
}
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);
}
}
}
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);//就把消息发给这个客户
}
}
//向单个客户发送消息
private void singleCom(String msg,Socket aClient){//singleCommunition---begin
try{
PrintStream pw=new PrintStream(aClient.getOutputStream());
String message=msg;
pw.println(msg);
}catch(Exception ex){
ex.printStackTrace();
}
}
public static void main(String[] args){
new Server().init();//启动服务器窗口
System.out.println("服务器");
}
}
//聊天室客户端
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 broder3=new BorderLayout();
p7.setLayout(broder3);
p7.add(BorderLayout.CENTER,p1);
p7.add(BorderLayout.SOUTH,p5);
FlowLayout f1=new FlowLayout(FlowLayout.RIGHT);
p8.setLayout(f1);
p8.add(sendButton);
p8.add(closeButton);
p8.setBackground(Color.pink);
BorderLayout broder4=new BorderLayout();
p9.setLayout(broder4);
p9.add(BorderLayout.CENTER,p7);
p9.add(BorderLayout.SOUTH,p8);
//窗口关闭
this.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent arg0){
System.exit(0);
}
});
BorderLayout broder5=new BorderLayout();
p10.setLayout(broder5);
p10.add(BorderLayout.CENTER,list);
p10.setBackground(Color.blue);
//添加字体颜色的监听器
jb1.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);
}
});
jb2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
JColorChooser chooser4=new JColorChooser();
Color color=chooser4.showDialog(Client.this,"我的调色盘",Color.yellow);
area.setBackground(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);
}
});
}
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();
}
}
}
}
//登录界面
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
class login extends 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("127.0.0.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 broder1=new BorderLayout();
this.setLayout(broder1);
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);//关闭窗口
}
public void actionPerformed(ActionEvent e){
JButton button=(JButton)e.getSource();
if(button.equals(btnCancel)){
dispose();//如果客户按了登录和取消按钮,那么登录窗口就关闭
}
else{
LoginStart Is=new LoginStart();
Is.start();
this.dispose();
}
}
class LoginStart extends Thread
{
public void run(){
int port=Integer.parseInt(txtPort.getText());//取得端口号
String ip=txtIP.getText();
String nam=txtName.getText();
try{
toServer=new Socket(ip,port);
in=new BufferedReader(new InputStreamReader(toServer.getInputStream()));
out=new PrintStream(toServer.getOutputStream());//输出流
new Client(out,in,nam);//如果登录成功,则弹出客户端聊天窗口
dispose();//同时关闭登录窗口
}
catch(Exception e2){
System.out.println("登录失败!");
e2.printStackTrace();
}
}
}
public static void main(String[] args){
new login();//启动登录窗口
}
}
六、 相关截图
登陆成功后,服务器界面显示:
踢出聊天室:
七、总结及体会
在设计的过程中了解到自己的知识比较有限,使得程序还不够完善,这一次的课程设计,让我懂得理论和实践相结合起来,从理论中得出结论,才能真正掌握这门技术,也提高了自己独立思考的能力,在设计的过程中,可以自己解决问题。真正体会到将一门知识学的更深入,必须加强实践,多练习,才能发现问题所在。