资源描述
评阅教师评语:
课程设计成绩
考勤成绩
实做成绩
报告成绩
总评成绩
指导教师签名:
《综合课程设计》
设 计 报 告
论文题目: 基于Socket的即时通讯系统
学院(系): 电子信息与自动化学院
班 级: 72—1
学生姓名: 学号
指导教师:
时间: 2011年 6 月 7日 到 2011 年 6 月 17日
一、 设计目的
通过综合课程设计,使学生能够运用《数字信号处理》、《信号与系统》、《通信原理》、《面向对象的程序设计》、《计算机通信网》、《通信协议开发及应用》等课程的知识来设计一个基于Socket的即时通讯系统,培养学生的动手能力以及分析问题、解决问题的能力。
二、 设计内容
1.大部分网络协议的实现都由客户端(Client)和服务器端(Server)来协作完成。这种模型本质上涉及两个不同的程序,通常这两个程序在不同机器上运行。这些机器之间都有网络连接。服务器端程序提供服务并对来自客户程序的请求作成响应。而客户端程序则是在使用者和服务器端程序之间建立某种沟通的渠道,或者是作为使用服务器端提供的某种网络服务的工具。
一个典型的服务器与客户机之间的交互可能如下所示:
1) 客户机提出一个请求;
2) 服务器收到客户机的请求,进行分析处理;
3) 服务器将运行处理的结果返回给客户机。
服务器与客户端的Socket 通信类型:Socket 的连接类型可以分为两种,分别是面向连接的字节流类型(Sock_stream)和面向无连接数据报类型(Sock_dgram)。面向无连接数据报类型的Socket 工作流程比较简单,双方不需要进行太多的沟通与交互。客户机直接将用户的请求打包发送到服务器端,省略了建立一个固定信息通道的过程。服务器端也是直接将处理的结果发送给客户端。每个发送的数据报都携带有传递所需要的所有地址信息。其工作流程如图1 所示。面向连接的字节流类型的Socket 工作中有比较严格的操作次序, 工作的原理也比较复杂。在这种类型的Socket 的工作过程中, 必须首先启动服务器端, 通过调用socket()函数建立一个Socket 对象,然后调用Bind()函数将该Socket 对象和本地网络地址绑定到一起。再调用Listen()函数使该Socket 对象处于侦听状态,并规定它的最大请求的数量。
三、 设计要求
1. 熟练掌握面向对象的程序设计方法;
2. 实现点对点通讯,能进行文字对话传输,包括客户端与服务器端;
3. 能对系统参数进行配置;
4. 实现文件传输;
5. 友好的对话界面。
四、 设计原理
Delphi,是Windows平台下著名的快速应用程序开发工具(Rapid Application Development,简称RAD)。Delphi是一个集成开发环境(IDE),使用的核心是由传统Pascal语言发展而来的Object Pascal,以图形用户界面为开发环境,透过IDE、VCL工具与编译器,配合连结数据库的功能,构成一个以面向对象程序设计为中心的应用程序开发工具。
4.1 Delphi Socket
1.Delphi 中Socket 的操作方式
根据连接发起的方式以及本地Socket 要连接的目标,Socket 之间的连接可以分为:客户端连接、监听连接以及服务器端连接。Delphi 分别使用ClientSocket 组件和ServerSocket 组件来操纵客户端Socket 和服务器端Socket 的连接和通信。ClientSocket 组件为客户端组件。它是通信的请求方,也就是说,它是主动地与服务器端建立连接。ServerSocket 组件为服务器端组件。它是通信的响应方,也就是说,它的动作是监听以及被动接受客户端的连接请求,并对请求进行回复。ServerSocket 组件可以同时接受一个或多个ClientSocket 组件的连接请求,并与每个ClientSocket 组件建立单独的连接,进行单独的通信。
2.ServerSocket 的属性设置
·Port 是通信的端口,必须设置。在本文实例中设置为2010;
·ServerType 服务器端读写信息类型,设置为StNonBlocking 表示异步读写信息,本文实例中采用这种方式。
·ThreadCacheSize,客户端的最大连接数,就是服务器端最多允许多少客户端同时连接。本文实例采用默认值10。
3.ClientSocket 的属性
·Port,是通信的端口,必须与服务器端的设置相同。否则可能导致两个组件发送的目标端口和接收的端口不一致,无法建立有效的信息传送连接。
·ClientType,客户端读写信息类型,应该与服务器端的设置相同,为ctNonBlocking 表示异步读写信息.
·Host,客户端要连接的服务器的ip 地址。必须设置,当然也可以在代码中动态设置
4.利用Buffer一次性传输文件数据,采用交互式的传送控制,一次就把文件读入Buffer中再发送.
4.2设计思路
实例包括一个服务器端程序与一个客户端程序。客户端程序可以放到其他计算机上运行,同时与服务器端进行连接通信。
服务器程序实现以下基本功能:1)记录客户端主机名称等;2) 显示当前聊天内容;3)能接收文件并显示文件的相关信息。
客户端程序实现一些基本功能:1) 显示当前程序的工作状况;2) 实现服务器端发送的注销、重启、关机的操作;3) 能实现信息以及文件的发送,以及信息的接收。
4.3 服务器端设计
ServerSocket 组件为服务器端组件,是通信的响应方,它的动作是监听及被动接收客户端的连接请求,并对请求进行回复.服务器端主要包括系统信息. 服务器端可实现服务器的启动、停止等功能.系统信息界面显示服务器是否正常启动;需自定义的数据类型用于设置socket中的Data.添加Label1、Label2、RichEdit1、ToolBar1、StartBtn、StopBtn、SaveBtn、SS等并做相关设置。并在相关控件下添加控制事件,在控制事件中添加相应函数使之完成相应功能。
4.4 客户端设计
ClientSocket 组件为客户端组件,是通信的请求方,能主动与服务器端建立连接.需自定义function GetFileSize(const FileName:string):integer;用获取文件长度。还需要添加一些主要的控件包括Edit1、Label1、RichEdit1、ToolBar1、ConnectBtn、OpenBtn、DisconnectBtn、SendBtn、Cs、OpenDialog1等并做相关设置,添加相应的控制事件,在控制事件中添加相应函数使之能完成相应的功能。
五、 软件设计
1.程序流程图
2.程序清单
1.客户端程序代码如下:
function getfilesize(const filename:string):integer;
var f:Tfilestream;
begin
f:=Tfilestream.Create(filename,fmopenread or
fmsharedenynone);
result :=f.Size;
f.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Cs.socket.sendtext(edit1.text);
end;
procedure TForm1.csRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
memo1.lines.add(socket.receivetext);
richedit1.lines.add(datetostr(date)+' '+
timetostr(time)+'==>>'+socket.receivetext);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if cs.Active //:=true
then
cs.Active:=false;
cs.Close;
end;
procedure TForm1.ConnectBtnClick(Sender: TObject);
var
remotehost:string;
begin
remotehost:=inputbox('建立连接','请输入对方IP地址或名称:','');
if trim(remotehost)<>'' then
begin
if cs.Active then
cs.Active :=true;
cs.Port:=9999;
cs.Host:=remotehost;
try
cs.Active :=true;
except
//show messsage('连接失败!');
end;
end;
end;
procedure TForm1.DisconnectBtnClick(Sender: TObject);
begin
cs.Close;
end;
procedure TForm1.OpenBtnClick(Sender: TObject);
var
tmpstr:string;
begin
with opendialog1 do
begin
execute;
if filename<>'' then
begin
tmpstr:='FILESEND '+
ExtractFilename(Filename)+
' '+inttostr(getfilesize(filename));
edit1.text:=filename;
cs.Socket.SendText(tmpstr);
end;
end;
end;
procedure TForm1.SendBtnClick(Sender: TObject);
var
fs: Tfilestream;
buf: pointer;
begin
fs:=Tfilestream.Create(edit1.Text,
fmopenread or fmsharedenynone);
GetMem(Buf,fs.size);
fs.Seek(0,sofrombeginning);
fs.ReadBuffer(buf^,fs.size);
richedit1.Lines.Add(datetostr(date)+' '+timetostr(time)+'==>>'+
'共发送:'+inttostr(cs.Socket.sendbuf(buf^,fs.size))+'字节.');
end;
procedure TForm1.csConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
richedit1.lines.add(datetostr(date)+' '+
timetostr(time)+'==>>'+'主机连接成功:');
end;
end.
2.服务器端程序代码如下:
type
TDataflag = record
FileName: string;
FileSize: integer;
WorkFlag: integer;
end;
PDataFlag =^TDataflag;
procedure TForm1.ssClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
df : pdataflag;
cmd:string;
buffer : pointer;
nretr : integer;
fs : tfilestream;
const buffersize = 1024;
begin
df:=Socket.Data;
case df.WorkFlag of 0,3 ://修改
begin
memo1.lines.add(socket.receivetext);
cmd :=trim(Socket.receivetext);
if pos('filesend ',uppercase(cmd)) > 0 then
begin
df.FileName := trim(copy(cmd,pos(' ',cmd)+1,length(cmd)));
df.FileSize := strtoint(copy(df.FileName,
pos(' ',df.FileName)+1,length(df.FileName)));
df.FileName := trim(copy(df.FileName,1,
pos(' ',df.FileName)));
df.WorkFlag :=1;
socket.Data := df;
socket.sendtext('文件传送服务器准备完毕,可以传送!'#13#10);
richedit1.Lines.Add(datetostr(date)+' '+
timetostr(time)+'==>>'+'文件名'+df.FileName+'文件大小:'+
inttostr(df.filesize)+#13#10' 文件传送服务准备完毕,可以传送!');
end;
end;
1 : begin
getmem(buffer,buffersize);
nretr := socket.receivebuf(buffer^,buffersize);
if not fileexists(label5.caption+df.FileName)
then
begin
fs :=tfilestream.Create(label5.Caption+df.FileName,
fmcreate or fmsharedenynone);
fs.Seek(0,sofrombeginning);
end//xiugai
else
begin
fs :=tfilestream.Create(label5.Caption+df.FileName,
fmopenwrite or fmsharedenynone);
fs.Seek(0,sofromend);
end;//xiugai
fs.WriteBuffer(buffer^,nretr);
fs.Destroy;
freemem(buffer);
df.WorkFlag:=0;
socket.SendText('文件传送服务准备完毕,可以传送下一个文件!'#13#10);
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ss.open;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ss.socket.connections[0].sendtext(edit1.text);
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ss.Active:=false;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
StartBtnClick(sender);
end;
procedure TForm1.StartBtnClick(Sender: TObject);
begin
ss.Port :=9999;
ss.Active := True;
Richedit1.Lines.Add(datetostr(date)+' '+
timetostr(time)+'==>>'+'文件传送服务启动!');
end;
procedure TForm1.StopBtnClick(Sender: TObject);
begin
ss.Active :=false;
richedit1.Lines.Add(datetostr(date)+' '+
timetostr(time)+'==>>'+'文件传送服务停止!');
end;
procedure TForm1.SaveBtnClick(Sender: TObject);
var
path:string;
begin
path:=Extractfilepath(application.exename);
//if SelectDirectory(path,[sdallowcreate,sdperformcreate,sdprompt],
//1000) then
begin
if path<>'' then begin
if copy(path,length(path),1)<>'\' then
path:=path+'\';//修改
label5.Caption:=path;
richedit1.Lines.Add(datetostr(date)+' '+
timetostr(time)+'==>>'+'文件存储路径设置为:'+path);
end;
end;
end;
procedure TForm1.ssClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
var df : pdataflag;
begin
Df :=new(pdataflag);
Df.FileName := '';
Df.FileSize := 0 ;
df.WorkFlag := 0;
socket.data := df;
socket.SendText('已经连接,请选择要传输的文件,按传送钮开始传送。'#13#10);
richedit1.Lines.Add(datetostr(date)+' '+
timetostr(time)+'==>>'+'文件传送服务器连接用户:'+
socket.remotehost+' '+socket.remoteaddress);
end;
end.
六、 调试过程
程序调试过程中,出现了一些基础的问题,比如一些标点符号的错误,字母错误,还有一些变量未被未初始化的情况,这种问题一般发生在数据库定义的字段属性和赋的属性不一致,或者数据库字段长度不够,假如var(5)你给个长度10的字符串,就会报这种错.。
出现这几个变量未定义
我去网上找了很多解决的办法,尝试很多次,最后由于时间资源有限,最终还是没解决,最后只好把它屏蔽掉啦,因此没了修改存储路径的功能,还好还是能传输,影响还是不是很大吧。以后有机会再继续改进吧。解决方法:
编译程序可以对的程序进行初步的调试,排除一些故障。但是仅仅这样是完全不够的,需要更加强大的调试能力才能胜任更加复杂的程序调试任务。在遇到一些大的问题时,使用一些基本、最重要的调试手段包括:单步跟踪、断点、变量观察、堆栈检查等。所有这些功能在Delphi的集成调试程序中都能提供。按下F9(Compile and Run,编译并运行)一个程序时,就已经启动了Delphi的集成调试程序,而按下Ctrl+Break(Program Pause,程序暂停)时则会暂停被调试程序返回到集成调试程序中去,再次按下F9会从暂停地地方继续执行,而Alt+F2(Program Reset,程序复位)则会完全中止被调试程序的执行,返回集成调试程序中去。对于某些递归调用和复杂的嵌套调用来说,使用Call Stack功能能够方便的检查函数的调用情况。按Ctrl+Alt+S可以显示这个窗体,在最上面的是当前过程或函数,在最下面的往往是你的主程序。
例如:TForm1.Button1Click(???);
Project1
这表示Project1调用了方法TForm1.Button1Click,由于其参数是一个对象(Sender:TObject),不能求值,所 以用???表示。双击Project1可以看出在什么地方调用了TForm1.Button1Click(如果调用点没有源代码,则显示有源代码的第一 行)。
七、 实验结果分析
在这个即时通信程序中,首先打开服务器端程序再打开客户端程序,服务器端与客户端界面如下:
此通信系统的设计可以满足一对一通信,单方面传送文件的要求。如下图所示:
八、 体会和建议
这次课程设计算得上是我第一次软件设计一个小项目,以前学过C语言,也限于仅仅是学过,没有实际编程开发经验,刚开始我看到这个设计的题目——基于Socket的即时通讯系统,我首先想到使用那种开发工具,通过老师的建议、在网上和图书馆的查阅了解以及反复的思考,我决定使用对我来说是全新的开发工具——delphi,从零开始,去图书馆借了一些相关的书籍,另外在网上也下了相关教程,通过做一些简单的例程,慢慢地熟悉了Delphi这个软件的一些操作界面。然后去找即时通信的相关例程,自己先根据例程做了个只能互发信息的系统,然后不断去完善他的界面,为了提要自己的要求,于是我开始编程使之能够单方向发送文件,通过不断的努力,不断的调试,最终由于时间有限,基本还是能实现我所想要的功能。通过这次课程设计,让我加深了对面向对象编程的了解,通过学习使用delphi编程,掌握了它的的基本应用,也懂得了要想熟练编程不是一朝一夕的事情,一份付出一份收获,只有加强以后的上机编程时间,才能让自己的编程能力的到进一步的提高。此外,利用Delphi 下的Socket 控件,可以很好地解决局域网内的实时通信问题,目前在各中小企事业单位中已经得到广泛应用. 系统具有良好的扩充性,例如可实现对信息及数据的加密,充分保障系统的安全性;可进行多媒体等文件的实时在线传输等,具有较好的应用前景.
九、 参考文献
[ 1] 沈金龙. 计算机通信网 .西安:西安电子科技大学出版社,2003.8.
[ 2] 陈志华. Delphi案例教程.北京: 中科多媒体电子出版社,2002.
[ 3] 网冠科技. Delphi 程序开发 触类旁通百例 . 北京:机械工业出版社,2005.4.
[ 4] 吕伟臣.Delphi 6网络编程.北京:科学出版社,2002.6.
[ 5] 任泰明.TCP/IP协议与网络编程.西安:西安电子科技大学出版社,2004.3.
14
展开阅读全文