090110汪锋.docx
《090110汪锋.docx》由会员分享,可在线阅读,更多相关《090110汪锋.docx(27页珍藏版)》请在冰点文库上搜索。
090110汪锋
中南大学
《计算机网络》课程设计报告
设计题目:
网络聊天室
专业班级:
计科0802
姓名:
汪锋
学号:
0902061110
指导老师:
郭克华
完成日期:
2011.1.14
一.题目内容描述
本项目主要是利用JAVA语言制作一个聊天室,实现群体聊天。
采用的是客户/服务器结构,二层的C/S结构是指以单一的服务器和局域网为核心,能通过客户端与用户进行直接对话。
典型的C/S结构有以下特点:
(1)服务器负责数据管理及程序处理;
(2)客户机负责界面描述和界面显示;
(3)客户机向服务器提出处理要求;
(4)服务器响应将处理结果返回客户机;
二.需求分析
1.开发背景:
随着计算机网络的发展,人们对生活的要求不断提高,计算机网络通信已成为了人们生活中的亮点,不可缺少的一部分。
由此而产生的聊天工具越来越多,网络聊天一类的聊天系统发展日新月异,因此本次课程设计制作一个简陋的聊天室程序,巩固自己的计算机网络及JAVA方面的知识。
2.功能要求
(1)登陆功能;
(2)客户可以通过服务器发送消息,实现多人聊天;
(3)客户端程序应该可以显示登陆用户列表;
(4)退出功能。
3.开发环境
WindowsXP+JDK1.6+JCreator
三.概要设计
为实现网络聊天的功能,采用WindowsSocket编程,服务器与客户端采用了TCP/IP连接方式,在设计聊天方案时,实行将所有信息发往服务器端,再由服务器进行分别处理的思路,服务器端是所有信息的中心。
由于服务器端要保存用户信息,我们利用哈希表来实现这一功能,因此在服务器开启时需要建立用户信息对应的哈希表。
四.详细设计
客端和户服务器端的功能可划分为以下模块:
1.客户端:
(1)登陆功能:
建立与服务器的连接并登陆,能显示登陆错误信息。
(2)聊天功能:
发送消息到聊天室。
(3)退出功能:
退出与服务器的连接。
2.服务器端
(1)接受来自客户端的各种信息并分别处理。
(3)登陆信息:
检查登陆信息是否正确,并向客户端返回登陆信息,如信息正确。
就将在线用户发给该用户,并将该用户的状态发给各在线用户。
同时在服务器端显示出来。
(4)聊天信息:
转发给消息到每个用户。
(5)用户下线:
将此用户下线消息发给各客户端,并改写用户在服务器端的状态。
网络通信流程图
程序主要流程
五.源代码
服务端包含三类线程,主线程负责生成服务端窗口,并监听窗口及窗口中的按钮,ServerThread线程负责将客户机连入,ServerThreadSingle线程主要负责与客户端的通信。
代码如下:
importjava.awt.*;
importjava.awt.event.*;
import.*;
importjava.io.*;
importjava.util.Enumeration;
importjava.util.Hashtable;
importjava.util.Calendar;
publicclassServerextendsFrameimplementsActionListener{
privatebooleanserving=false;
privateColorbg=newColor(176,196,227);
privateStringrightNow;
privateInetAddresssvrAddress;
privateServerSocketserver;
privateServerThreadst;
privateHashtableht;//记录StringuserName->DataOutPutStreamout
//privateHashtableht_s;//记录StringuserName->Socketconnection
privateLabelsvrIP=newLabel("ServerIP:
");
privateTextFieldshowSvrIP=newTextField();
privateLabelsvrPort=newLabel("Serverport:
");
privateTextFieldgetSvrPort=newTextField("8888");
privateButtonenter=newButton("Start");
privateTextAreaoutput=newTextArea();
privateLabeluser=newLabel("User(0)");
privateListlist=newList();
//响应关闭按钮的内部类
privateclassWindowCloserextendsWindowAdapter
{
publicvoidwindowClosing(WindowEventwe)
{
System.exit(0);
}
}
//布局设置方法,在构造方法里面调用
privatevoidsetup()
{
Paneltop=newPanel();
top.setLayout(newFlowLayout());
top.add(svrIP);
svrIP.setBackground(bg);
top.add(showSvrIP);
showSvrIP.setBackground(bg);
top.add(svrPort);
svrPort.setBackground(bg);
top.add(getSvrPort);
getSvrPort.setBackground(bg);
top.add(enter);
enter.setBackground(bg);
Paneleast=newPanel();
east.setLayout(newBorderLayout());
east.add("North",user);
user.setBackground(bg);
east.add("Center",list);
list.setBackground(bg);
setLayout(newBorderLayout());
add("North",top);
top.setBackground(bg);
add("East",east);
east.setBackground(bg);
add("Center",output);
output.setBackground(bg);
setBackground(bg);
}
//构造方法
publicServer()throwsUnknownHostException,IOException
{
super("ChatRoomServer");
serving=false;
svrAddress=InetAddress.getLocalHost();
byte[]ip=svrAddress.getAddress();
showSvrIP.setText((ip[0]&0xFF)+"."+(ip[1]&0xFF)+"."
+(ip[2]&0xFF)+"."+(ip[3]&0xFF));
showSvrIP.setEditable(false);
output.setEditable(false);
enter.addActionListener(this);
addWindowListener(newWindowCloser());
setup();
pack();
setSize(500,400);
show();
}
//用于接收客户消息的线程类
privateclassServerThreadSingleextendsThread{
publicSocketconnection;
privateDataInputStreamin;
privateDataOutputStreamout;
privatebooleanlogin;
privateStringuserName;
publicServerThreadSingle(Socket_connection){
try{
connection=_connection;
in=newDataInputStream(connection.getInputStream());
out=newDataOutputStream(connection.getOutputStream());
login=false;
userName=newString("");
start();
}catch(IOExceptionioe){
output.append("Error:
"+ioe);
}
}
publicvoidrun(){
try{
Stringline=newString("Q");
while(!
login){
if(!
serving)break;
line=in.readUTF();
if(line.charAt(0)=='L'){
userName=line.substring
(2);
if(userName.length()==0){
out.writeUTF("R");//refused
break;
}
//判断是否已存在该用户名
for(Enumeratione=ht.keys();e.hasMoreElements();){
Stringstr=(String)(e.nextElement());
if(str.equalsIgnoreCase(userName)){
out.writeUTF("R");//refused
return;
}
}
//向登陆的用户发送当前在线用户列表
login=true;
list.add(userName);
user.setText("User("+list.getItemCount()+")");
String[]str=list.getItems();
line="A";//accepted
for(inti=0;iline+=(str[i]+"");
}
out.writeUTF(line);
line="L"+userName;
rightNow=Calendar.getInstance().getTime().toLocaleString();
output.append(rightNow+"\n"+userName+"login.\n\n");
//向在线用户(不包括当前客户)发送消息
for(Enumeratione=ht.elements();e.hasMoreElements();){
DataOutputStreamout=(DataOutputStream)(e.nextElement());
out.writeUTF(line);
}
ht.put(userName,out);//将登录的客户加入名单
//ht_s.put(userName,connection);
}
}
while(login){
line=in.readUTF();
if(!
serving)break;
if(line.charAt(0)=='Q'){
out.writeUTF("Q");
login=false;
ht.remove(userName);//将退出登录的客户从名单中剔除
ht_s.remove(userName);
list.remove(userName);
user.setText("User("+list.getItemCount()+")");
line="Q"+userName;
}
else{
line=userName+"said:
\n"+line.substring
(2);
}
rightNow=Calendar.getInstance().getTime().toLocaleString();
output.append(rightNow+"\n"+(line.charAt(0)=='Q'?
userName+"logout.":
line)+"\n\n");
//向所有客户发送消息
for(Enumeratione=ht.elements();e.hasMoreElements();){
DataOutputStreamout=(DataOutputStream)(e.nextElement());
out.writeUTF(line);
}
}
in.close();
out.close();
connection.close();
}catch(SocketExceptionse){
ht.remove(userName);
ht_s.remove(connection);
list.remove(userName);
output.append("Connectionto"+userName+"closed\n");
}catch(IOExceptionioe){
if(!
(ioeinstanceofEOFException)){
output.append("Error:
"+ioe+"\n\n");
}
}
}
}
//用于接受客户连接请求的线程类
privateclassServerThreadextendsThread{
privatebooleanrunning;
publicServerThread()
{
start();
}
publicvoidrun()
{
try{
while(serving){
Socketconnection=server.accept();
ServerThreadSinglehandler
=newServerThreadSingle(connection);
}
}catch(SocketExceptionse){
rightNow=Calendar.getInstance().getTime().toLocaleString();
output.append(rightNow+"\nServingstoped.\n\n");
}catch(IOExceptionioe){
output.append("Error:
"+ioe+"\n\n");
}
}
}
//消息处理方法
publicvoidactionPerformed(ActionEventevent)
{
if(event.getSource()==enter)
{
if(serving){
try{
for(Enumeratione=ht.elements();e.hasMoreElements();){
DataOutputStreamout=(DataOutputStream)(e.nextElement());
out.writeUTF("Q");
}
serving=false;
ht.clear();
ht_s.clear();
list.clear();
user.setText("User(0)");
server.close();
enter.setLabel("Start");
}catch(SocketExceptionse){
rightNow=Calendar.getInstance().getTime().toLocaleString();
output.append(rightNow+"\nServingstoped.\n\n");
}catch(IOExceptionioe){
output.append("Error:
"+ioe+"\n\n");
}
}
else{
try{
server=newServerSocket(Integer.parseInt(getSvrPort.getText()));
rightNow=Calendar.getInstance().getTime().toLocaleString();
output.append(rightNow+"\nServingstarted.\n\n");
enter.setLabel("Stop");
ht=newHashtable();
//ht_s=newHashtable();
st=newServerThread();
serving=true;
}catch(IOExceptionioe){
output.append("Error:
"+ioe+"\n\n");
}
}
}
}
//主方法
publicstaticvoidmain(Stringargs[])throwsUnknownHostException,IOException
{
Servers=newServer();
}
}
客户端主要有两个线程,主线程负责窗口布局与显示,recThread线程主要负责与服务器的通信,代码如下:
importjava.awt.*;
importjava.awt.event.*;
import.*;
importjava.io.*;
importjava.util.Calendar;
publicclassClientextendsFrameimplementsActionListener{
privatebooleanlogin=false;
privateColorbg=newColor(255,255,255);
privateStringrightNow;
privateInetAddressclientAddress;
privateSocketconnection;
privateDataInputStreamin;
privateDataOutputStreamout;
privaterecThreadreceiver;
privateButtonB_login=newButton("Login");
privateButtonB_logout=newButton("Logout");
privateButtonB_send=newButton("Send");
privateLabeluserName=newLabel("UserName:
");
privateTextFieldgetUserName=newTextField();//获取用户名
privateLabelsvrIP=newLabel("ServerIP:
");
privateTextFieldgetSvrIP=newTextField();//获取服务器IP地址
privateLabelsvrPort=newLabel("ServerPort:
");
privateTextFieldgetSvrPort=newTextField();//获取服务器端口
privateTextAreainput=newTextArea();//输入文本框
privateTextAreaoutput=newTextArea();//输出文本框
privateLabeluser=newLabel("User(0)");
privateListlist=newList();
//用于接收服务器消息的线程类
privateclassrecThreadextendsThread
{
publicrecThread()
{
start();
}
publicvoidrun()
{
try{
while(login){
Stringline=in.readUTF();
rightNow=Calendar.getInstance().getTime().toLocaleString();
if(line.charAt(0)=='Q'){