课程设计报告.docx
《课程设计报告.docx》由会员分享,可在线阅读,更多相关《课程设计报告.docx(36页珍藏版)》请在冰豆网上搜索。
课程设计报告
吉首大学
Java课程设计报告
基于客户服务器程序的网络聊天小程序
年级专业班级:
08数计计科2班
学院:
数学与计算机科学技术学院
指导老师:
欧云老师
学生姓名学号:
刘文20084042076
陈明20084042066
廖志威20084042074
设计题目:
Java网络聊天小程序。
系统环境(硬件环境、软件环境)
Intel-x86,WindowsServer2008-JavaSEDevelopmentKit6u22。
设计内容:
基于客户/服务器模式的局域网java聊天小程序。
包括两个小程序:
Server程序和client程序。
总体设计思路:
在目前的网络系统中,运输层数据传送方式常见的有两种TCP和UDP方式,本程序使用的是TCP传送方式,因为实现比较简单。
服务器和客户机程序实现起来类似。
我们用包中提供的ServerSocket类和Socket类分别实现本程序的连接和数据的传送。
在服务器程序中,我们用ServerSocket建立一个对象,然后通过构造函数为其分配端口号,用ServerSocket中的accept函数等待用户程序向这个端口建立连接,然后通信。
在服务器程序中,我们用Socket类创建一个对象,用服务器所在的程序的IP和端口号初始化这个对象(Socket(ClientConnectCtl.SERVER_NAME,SERVER_PORT);)。
客户机建立Socket对象的时候会向服务器对应端口发送连接请求,服务器接收客户端发送的数据,若服务器没有建立连接就响应这个请求,若有连接就向对应的客户端发送忙的提示语句。
服务器程序中设置了两个线程,一个线程用于发送和接收一个端口的数据,另外一个端口用来监听其他的客户机的连接,并且向客户机发送忙的提示,这是因为TCP是面向连接的通信方式,一个端口不能同时和两个地址连接,否则要使用UDP协议了。
在这两个程序中,我们需要使用窗口来显示程序,而不是控制带界面。
在这个程序中我们用到的是java.lang.Object中的BorderLayout来设置布局,使用这个布局管理方式我们只需要设置North,South,Center来设置我们的程序的窗口的布局。
在本程序中,我们还用到了importjava.awt和importjava.awt.event的TextField和Button的GUI的组件。
初始化数据对象和构造用户界面;
用户随时可以输入自己的喜欢的名字;
创建用于监听的ServerSocket;
If(监听线程对象==NULL)
{
启动监听线程;
}
If(监听线程启动)
{
监听连接请求;
If(有连接请求)
{
启动一个新线程,监听其他的连接请求;
接受连接请求;
If(连接并且握手成功)
{
语音提示;
根据用户输入的信息和点击发送按钮事件,发数据;
随时接受已经连接的客户机的数据,并显示;
If(客户机退出)
{
复位系统;
提示用户,并且监听其他的连接请求;
}
If(用户发送bye信息或者直接退出程序)
{
关闭stream和Socket;
退出;
}
}
}elseif(有其他用户发出连接请求)
{
语音提示;
显示发送连接请求的用户的用户信息;
通知呼叫用户服务器正忙,稍后再试;
}
}
java列表说明各类模块的功能及成员变量和成员方法的作用:
服务器程序:
ClassLanServerTalk;这个程序中设置了服务器程序的run方法,其中的init方法使用BorderLayout设置了本程序的界面的布局。
用Thread来设置了连接线程和监听进程。
当程序中收到或者发送“bye”字符时退出程序。
并全还设置了这种端口关闭方法。
ClassListenOther;这个类是设置了专门用于监听其他用户连接时发送提示忙信息的类,并且还设置了退出是这种端口设置的关闭。
ClassServerTalkCanvas;这个类很重要,设置了“显示消息区”的信息的更新方法。
若程序的消息区没有满则讲消息直接写到消息区,若消息区满了,就重写整个消息区。
ClassServerSendCtl;这个类设置了当点击发送按钮的时候所需要完成的事件。
ClassServerNameCtl;这个类用来给程序中的用户设置一个名字。
客户机程序:
ClassLanClientTalk;这个类设置了run方法,和服务器程序中的LanServerTalk对应,而且实现的功能也是类似的。
ClassClientTalkCanvas;设置了“显示消息区”的信息的更新方法。
若程序的消息区没有满则讲消息直接写到消息区,若消息区满了,就重写整个消息区。
ClassClientSendCtl;这个类设置了当点击发送按钮的时候所需要完成的事件。
ClassClientConnectCtl;这个类中设置了点击呼叫主机和连接按钮时需要做的工作。
程序清单:
服务器程序:
/**目的:
用于通信的服务器端
作者:
刘文,廖志威,陈明
时间:
2010年10月~2010年11月
*/
importjava.io.*;
import.*;
importjava.awt.*;
importjava.awt.event.*;
importjava.applet.AudioClip;
importjava.applet.Applet;
publicclassLanServerTalkextendsAppletimplementsRunnable
{
/**
*
*/
privatestaticfinallongserialVersionUID=1L;
ServerTalkCanvasserverTalkCanvas;//显示对话信息的对象
ServerSendCtlserverSendCtl;//发送信息的用户接口的对象
ServerNameCtlserverNameCtl;//更改服务器名称的用户接口的对象
ListenOtherlistenOther;//监听其他用户连接请求的对象
privateServerSocketlistenSocket=null;//监听用的socket
privatePrintWriteranswerSend=null;//发送数据的对象
privateBufferedReaderanswerReceive=null;//接受数据的对象
publicintSERVER_PORT=3210;//监听端口
publicintMAX_CLIENTS=20;//最大的请求数
publicbooleanKeepRunning=true;//监听进程的运行标
privatestaticfinalbooleanAUTOFLUSH=true;//printwriter的autoflush标志
privateAudioClipaudio;//声音提示
@SuppressWarnings("unused")
privatestaticStringdinfo="";//用于显示信息
@SuppressWarnings("unused")
privatestaticStringresponse="";//保存远程机器的信息
ThreadLanServerThread;//服务器进程,用于监听连接以及通信
publicLanServerTalk()//初始化函数
{
try
{
listenSocket=newServerSocket(SERVER_PORT,MAX_CLIENTS);//SERVER_PORT=3210,MAX_CLIENTS=20;
}catch(IOExceptionexcpt)
{
serverTalkCanvas.Display("不能在这个端口成功监听:
"+SERVER_PORT+":
"+excpt);
System.exit
(1);
}//endofcatch
}//endofserver():
构造函数的结尾
publicvoidstart()//创建并启动用于监听的新进程的函数
{
if(LanServerThread==null)
{
LanServerThread=newThread(this);
LanServerThread.start();
}
}
publicvoidstop()//停止监听进程运行的函数
{
if(KeepRunning)
{
KeepRunning=false;
}
}
publicvoidinit()//初始化用户界面
{
setLayout(newBorderLayout());
serverTalkCanvas=newServerTalkCanvas();
add("Center",serverTalkCanvas);
serverSendCtl=newServerSendCtl(serverTalkCanvas);
add("South",serverSendCtl);
serverNameCtl=newServerNameCtl(serverTalkCanvas);
add("North",serverNameCtl);
resize(600,400);
audio=getAudioClip(getDocumentBase(),"hi,au");
}
publicvoidrun()//-------run()函数:
接收请求并且用for实现通信的新进程
{
Stringnextline="";
SocketforkSocket=null;
ThreadlistenOtherThread=null;
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
serverTalkCanvas.Display("提示:
系统启动完成,正在监听连接");
serverTalkCanvas.Display("提示:
如果没有输入用户名,缺省为LOCAL");
serverTalkCanvas.Display("连接后,建议使用bye来借书通信");
try
{
while(KeepRunning)
{
if(!
ServerSendCtl.CONNECT)
{
forkSocket=listenSocket.accept();
answerReceive=newBufferedReader(newInputStreamReader(forkSocket.getInputStream()));
answerSend=newPrintWriter(forkSocket.getOutputStream(),AUTOFLUSH);
if(answerSend!
=null&&answerReceive!
=null)
{
ServerSendCtl.CONNECT=true;
audio.play();
}
if(listenOtherThread==null&&ServerSendCtl.CONNECT)
{
listenOther=newListenOther(listenSocket,audio);
listenOtherThread=newThread(listenOther);
listenOtherThread.start();
}
}
if(!
ServerSendCtl.InfoSend.equals(""))
{
if(ServerSendCtl.CONNECT)
{
if(ServerSendCtl.InfoSend.equals("bye"))
{
answerSend.println("通信结束,再见");
answerSend.println("bye");
ServerSendCtl.CONNECT=false;
break;
}
elseif((forkSocket==null)||(answerSend==null)||(answerReceive==null))
{
serverTalkCanvas.Display("不能发送信息");
}
else
{
answerSend.println(ServerSendCtl.InfoSend);
serverTalkCanvas.Display(ServerNameCtl.LOCAL_NAME+ServerSendCtl.InfoSend);
}
}
ServerSendCtl.InfoSend="";
}
answerSend.println("~!
@#$%^");
if(answerReceive.ready())
{
nextline=answerReceive.readLine();
if(!
nextline.equals("")&&!
nextline.equals("bye"))
{
serverTalkCanvas.Display(nextline);
nextline="";
}
}
if(nextline.equals("bye"))
{
serverTalkCanvas.Display("提示:
用户可能退出了,发送的消息可能无法到达");
serverTalkCanvas.Display("提示:
系统假如监听状态,可以接受用户的连接请求");
if(answerSend!
=null)answerSend.close();
if(answerReceive!
=null)answerReceive.close();
if(forkSocket!
=null)forkSocket.close();
listenSocket.close();
ServerSendCtl.CONNECT=false;
nextline="";
try
{
listenSocket=newServerSocket(SERVER_PORT,MAX_CLIENTS);
}catch(IOExceptionexcpt)
{
serverTalkCanvas.Display("不能在端口监听"+SERVER_PORT+excpt);
System.exit
(1);
}
forkSocket=listenSocket.accept();
answerReceive=newBufferedReader(newInputStreamReader(forkSocket.getInputStream()));
answerSend=newPrintWriter(forkSocket.getOutputStream(),AUTOFLUSH);
if(answerSend!
=null&&answerReceive!
=null)
{
ServerSendCtl.CONNECT=true;
answerSend.println("连接成功");
audio.play();
}
if(ServerSendCtl.CONNECT)
{
listenOther=newListenOther(listenSocket,audio);
listenOtherThread=newThread(listenOther);
listenOtherThread.start();
}
}
if(!
ListenOther.otherreceiveInfo.equals(""))
{
serverTalkCanvas.Display(ListenOther.otherreceiveInfo);
ListenOther.otherreceiveInfo="";
}
}
}catch(IOExceptionexcpt)
{
serverTalkCanvas.Display("IO故障"+excpt);
}
try
{
if(answerSend!
=null)answerSend.close();
if(answerReceive!
=null)answerReceive.close();
if(forkSocket!
=null)forkSocket.close();
listenSocket.close();
System.exit(0);
}catch(IOExceptionexcpt)
{
serverTalkCanvas.Display("结束通信是出现IO故障"+excpt);
}
}
}
classListenOtherimplementsRunnable//当与一个远程机器连接后,监听其他的连接的请求
{
publicbooleanRunning=true;//监听进程的运行标志
privatestaticfinalbooleanAUTOFLUSH=true;
publicPrintWriterotherSend;
publicBufferedReaderotherReceive;
publicstaticStringotherreceiveInfo="";
privateServerSocketlistenSocket=null;
publicSocketotherforkSocket=null;
publicAudioClipaudio;
publicListenOther(ServerSocketlistenSocket,AudioClipaudio)
{
this.listenSocket=listenSocket;
this.audio=audio;
}
publicvoidstop()
{
if(Running)
{
Running=false;
}
}
publicvoidrun()//用于提示客户暂时不能进行通信,之后关闭连接
{
Stringdinfo="";
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
while(Running)
{
try
{
otherforkSocket=listenSocket.accept();
otherSend=newPrintWriter(otherforkSocket.getOutputStream(),AUTOFLUSH);
otherReceive=newBufferedReader(newInputStreamReader(otherforkSocket.getInputStream()));
otherreceiveInfo=otherReceive.readLine();
if(otherreceiveInfo!
=dinfo)
{
otherSend.println("AgNreset");
dinfo=otherreceiveInfo;
otherSend.println("提示:
可能正在与其他用户通信,请重试");
audio.play();
}
}catch(IOExceptionexcpt)
{
break;
}//endofcatch
}
try
{
if(otherSend!
=null)otherSend.close();
if(otherReceive!
=null)otherReceive.close();
if(otherforkSocket!
=null)otherforkSocket.close();
}catch(IOExceptionexcpt)
{
otherreceiveInfo="结束监听其他用户的线程时"+excpt;
}
}
}
classServerTalkCanvasextendsCanvas//用于以滚动的方式显示通信信息
{
/**
*
*/
privatestaticfinallongserialVersionUID=1L;
privatefinalintrowhigh=25;//每行25高
privateintrownum=25;//在显示区同时显示的行数
privateintRectright,Rectbottom;//显示区的右下角的坐标
privateintrowcurren=0;//屏幕显示的当前韩式,当first=false,rowcurren始终显示为最后一行
privateStringdpinfo[];//存放显示信息的字符串数组
privateboolean