本部 一班二组 终极报告Word格式文档下载.docx
《本部 一班二组 终极报告Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《本部 一班二组 终极报告Word格式文档下载.docx(18页珍藏版)》请在冰豆网上搜索。
2课程设计正文2
2.1设计方案3
2.2理论分析3
2.2.1TCP协议3
2.2.2Socket编程3
2.3流程图3
2.4软件界面设计4
3源程序代码5
4软件测试17
5设计总结或结论18
6参考文献18
1课程设计目的
本次课程设计,要求我们设计一款文件传输的软件,能够方便地实现局域网中主机之间的文件传输。
项目设计为客户机/服务器即当主机主动发起连接或者主动传输文件时,该主机为客户机,当处于监听状态,被连接时或者被动接收文件时,该主机为服务器。
执行一个线程的时候,同步调用必须等待这段代码执行完返回结果后,调用方才能继续往下执行,并且同步调用又叫阻塞调用,他阻塞当前线程,然后执行调用,当调用完毕后在继续向下进行。
异步调用,调用方和被调方可以属于两个不同的线程,调用方启动被调方线程后,不等对方返回结果就继续执行后续代码,可以执行多个线程,并且可以避免阻塞,所以将在本程序中采用异步调用的方式启动线程。
通过这次课设,我们要分析并回答以下问题。
(1)深入理解TCP的工作原理和服务的特性,掌握TCP套接字编程机制和方法;
(2)理解C/S架构,理解Pull和Push两种文件传输模式;
(3)深入分析文件发送方和接收方的程序运行流程,绘制程序流程图;
(4)学习使用OpenFileDialog、SaveFileDialog、FileStream;
(5)完成服务器端软件和客户端软件的开发和测试;
2课程设计正文
文件传输是各种计算机网络实现的基本功能,文件传送协议是一种最基本的应用层协议。
它是按照客户/服务器的模式进行工作,提供交式的访问。
是INTERNET使用最广泛的协议之一。
以及深入了解计算机网络是建立在TCP/IP网络体系结构之上。
本课程设计目的是要学会利用已有网络环境设计并实现简单应用层协议,掌握TCP/IP网络应用程序基本设计方法各实现技巧。
并要求使用这些功能实现一个简单的文件传送协议。
2.1设计方案
设计程序使客户端连接的时候,服务器将会向客户端发回一条消息告知它的IP地址,然后关闭连接并继续接收端口的连接。
当然服务器端先进行监听,发送端发送连接请求,等待接收端的服务。
接收端接受该请求(或是拒绝),创建socket,然后进行发送端与接收端的文件传输,当然也可以进行聊天的功能。
文件传输过程中,发送放与接收方都可以对其进行控制,可随时中断,当文件传输完毕时,线程自动停止。
我们为了可靠传送文件,我们使用了TCP协议,用了C/S架构和P2P架构混合模式。
开始侦听建立连接的时候采用的是C/S架构,后来进行文件传输和文本发送时,应用的是P2P架构。
在C/S架构中,我们采用Pull方式(客户端主动获取文件,服务器端被动地提供文件)。
在P2P架构中,在使用TCP方式在对等方之间传递文本消息以及用于发起文件传输的控制信息,实际进行文件传输时仍通过TCP连接进行。
使用Socket进行Client/Server程序设计的一般连接过程是这样的:
Server端Listen(监听)某个端口是否有连接请求,Client端向Server端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。
一个连接就建立起来了。
Server端和Client端都可以通过Send,Write等方法与对方通信。
对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含创建Socket;
打开连接到Socket的输入/出流、按照一定的协议对Socket进行读/写操作、关闭Socket四个基本的步骤。
基本设计思路是先建立TCP连接,连接成功后,发送文件的一方从文件流读取数据,写入网络流,接收方读取网络流,写入文件流,完成后断开TCP连接,释放相关的资源。
2.2理论分析
2.2.1TCP协议
TCP,传输控制协议(TransportControlProtocol),是一种面向连接的、可靠的传输层协议。
面向连接指其一次正常的TCP传输需要通过在TCP客户端和TCP服务端建立特定的虚电路连接来完成,该过程通常被称为“三次握手”。
可靠性可以通过很多种方法来提供保证,在这里我们关心的是数据序列和确认。
TCP通过数据分段(Segment)中的序列号保证所有传输的数据可以在远端按照正常的次序进行重组,而且通过确认保证数据传输的完整性,并提供拥塞控制机制,当网络拥塞时,源抑制其传输速率。
要通过TCP传输数据,必须在两端主机之间建立连接。
2.2.2Socket编程
Socket编程是建立在应用层TCP协议之上的。
目前最流行的是客户机/服务器模式,在面向连接的Client/Server模型中,Server端的socket总是等待一个Client端的请求。
本次课程设计采用了C/S架构和P2P架构混合模式,开始侦听建立连接的时候采用的是客户机/服务器模型,其工作流程图如图1。
2.3流程图
其发送方与接收方的流程如图2。
2.4软件界面设计
本程序的文件传输系统的实现应包含服务端模块、客户端模块、界面显示模块等几个部分,整个程序采用C#完成。
程序使用流式套接字,基于C/S模型。
在设计GUI界面时把客户端和服务端放在一个界面中,根据用户需要将程序设置成客户端和服务端。
在通信的时候主要可以分为两个部分,一个部分是控制信息的传输部分,而另一个部分就是文件的传输部分。
传送的控制信息可以包含发送文件的请求,文件的名称、大小等。
对于文件的传输,由于数据量相对来说比较大,所以用两个线程来实现文件的传输,一个线程用于发送,一个线程用于接收。
此次文件传输软件界面设计如图2-5所示。
软件设计大致有三模块组成:
首先,两台主机IP模块,再者则是文件传输区,包括文件的选择、接收与发送;
最后,作为完善,增加了聊天区域,完善了单纯文件传输功能这一弊端,使软件更实用化。
而客户端图形界面(GUI)设计的中心问题是用户控制式。
现代面向对象程序是事件驱动的,对象响应事件(消息)。
友好的界面设计包括以下六点方针,包括用户控制式、一致性、个性化和客户化、宽容、反馈。
由于客户端程序与服务器程序很类似,所以客户机与服务器端基本一致,先发起连接的,则为客户机,而且文件的互传也很方便,只要安装了此软件,你既可以把本地主机当做客户机向远地主机发送文件,又可以把本地主机当做服务器接受从远地主机发来的文件。
此界面中用到的控件分别有:
label(对方IP、文件正在传输);
Button(连接、断开连接、监听、选择文件、发送、接收、清空、发送等);
textbox(输入对方IP的位置、显示状态的位置、聊天对话框)。
3程序源代码
publicForm1()
{
InitializeComponent();
try
stringstr=Dns.GetHostName();
ip=Dns.GetHostAddresses(str);
localendpoint=newIPEndPoint(ip[0],2000);
//用来传输文本的本地网络端点
filelocalendpoint=newIPEndPoint(ip[0],4000);
//传输文件的本地网络端点
//实例化2个异步调用
callback=newAsyncCallback(startreceivesocket);
filecallback=newAsyncCallback(startfilereceivesocket);
/
//实例化文本和文件监听
tcplistener=newTcpListener(localendpoint);
filetcplistener=newTcpListener(filelocalendpoint);
this.lsb_Information.Items.Add("
请选择监听或主动连接!
\r\n请确保对方处于监听状态才可发出连接申请!
\r\n否则需要重启这个这个程序!
\n"
);
}
catch(Exceptionex)
MessageBox.Show(ex.Message);
}
(1)启动线程,并在后台运行,在连接的时候打开
privatevoidopenth0()
th[0]=newThread(newThreadStart(th0process));
th[0].IsBackground=true;
if(th[0].IsAlive==false)
th[0].Start();
privatevoidopenth1()
th[1]=newThread(newThreadStart(th1process));
th[1].IsBackground=true;
if(th[1].IsAlive==false)
th[1].Start();
(2)听方,等待被连接那一方的监听程序
privatevoidth0process()
while(true)
if(receivesocket!
=null)
receivesocket.Blocking=true;
length=receivesocket.Receive(bytesreceived,bytesreceived.Length,0);
receivestring=System.Text.Encoding.GetEncoding("
GB2312"
).GetString(bytesreceived,0,length);
this.Invoke(newshowmessage(showmessagetotextbox1),newobject[]{receivestring});
//这里是另外开的线程,直接用赋值语句是不行的
//不是主线程,所以不能对form上的控件直接进行访问
if(receivestring.Length>
9)
if(receivestring.Substring(0,9)=="
对方断开了当前连接!
"
)
this.Invoke(newresume(resumefunction));
closefunction();
12)
if(receivestring.Substring(0,12)=="
对方取消了发送文件的请求!
this.Invoke(newresume(resumefilefunction));
closefilefunction();
if(receivestring=="
对方拒绝了您发送文件的请求!
(3)发送方的监听程序
privatevoidth1process()
if(sendsocket!
sendsocket.Blocking=true;
length=sendsocket.Receive(bytesreceived,bytesreceived.Length,0);
).GetString(bytesreceived,0,length);
对方拒绝了您的连接请求!
对方拒绝了您传送文件的请求!
(4)传文件时,主动发送方的传文件的程序
privatevoidtransferfile()
//这里每次发送之前都要受到对方发过来的信号是为了保证收发同步
if(filereceivesocket!
if(filereceivesocket.Connected)
//先发送文件名称,以便在保存文件时,有文件原始名称
filebytestosend=newbyte[filename.Length];
filebytestosend=Encoding.Unicode.GetBytes(filename);
filereceivesocket.Send(filebytestosend,filebytestosend.Length,0);
//随后发送文件长度,以便后面确定发送的次数filereceivesocket.Blocking=true;
length=filereceivesocket.Receive(filebytesreceived,filebytesreceived.Length,0);
startstring=Encoding.Unicode.GetString(filebytesreceived,0,length);
if(startstring=="
下面是长度"
FileInfofiletosend=newFileInfo(filepath);
sendfilelength=filetosend.Length;
filebytestosend=newbyte[sendfilelength.ToString().Length];
filebytestosend=Encoding.Unicode.GetBytes(sendfilelength.ToString());
//最后发送文件本身的内容
filereceivesocket.Blocking=true;
开始"
FileStreamfs=newFileStream(filename,FileMode.Open,FileAccess.Read);
filebytestosend=newbyte[32768];
temp=0;
//这里每次都要清零否则会留下上次传送文件长度的痕迹
this.Invoke(newMethodInvoker(this.timer1.Start));
while(temp<
sendfilelength)
sendlengthtemp=fs.Read(filebytestosend,0,filebytestosend.Length);
filereceivesocket.Send(filebytestosend,sendlengthtemp,0);
temp+=sendlengthtemp;
System.Threading.Thread.Sleep(200);
fs.Close();
else
temp=sendfilelength+1;
this.Invoke(newshowmessage(showmessagetotextbox1),newobject[]{"
文件已传输完毕"
});
MessageBox.Show("
(5)接收文件时,接收方的程序
privatevoidreceivefile()
if(filesendsocket!
if(filesendsocket.Connected)
filesendsocket.Blocking=true;
length=filesendsocket.Receive(filebytesreceived,filebytesreceived.Length,0);
filereceivestring=Encoding.Unicode.GetString(filebytesreceived,0,length);
//接受从对方传过来的文件的原有文件名
saveFileDialog1.Title="
另存为"
;
saveFileDialog1.FileName=filereceivestring;
//显示另存为对话框
this.Invoke(newshow(showdialog));
filestringtosend="
//随意发送一个信号,为了确保收发同步,以免这边还没接收完,那边已全发送完了sendfilefunction(filestringtosend);
//获取文件的长度
receivedfilelength=long.Parse(filereceivestring);
//将数字的字符串表示形式转换为它的等效64位有符号整数filestringtosend="
//这也是随意发送的一个记号,为收发同步
sendfilefunction(filestringtosend);
FileStreamfs=newFileStream(savedfilepath,FileMode.Create,FileAccess.Write);
temp2=0;
//这里每次temp2都要清零,否则会留下上次收到数据的长度的痕迹
this.Invoke(newMethodInvoker(this.timer2.Start));
//下面是接收文件的具体内容
while(temp2<
receivedfilelength)
receivedlengthtemp=filesendsocket.Receive(filebytesreceived,filebytesreceived.Length,0);
temp2+=receivedlengthtemp;
fs.Write(filebytesreceived,0,receivedlengthtemp);
//接收文件方向发方发送消息,以便同步
privatevoidsendfilefunction(stringstr)
filebytestosend=newbyte[str.Length];
filebytestosend=Encoding.Unicode.GetBytes(str);
filesendsocket.Send(filebytestosend,filebytestosend.Length,0);
(6)监听线程和关闭线程
这要运行到beginacceptsocket函数时,就会开辟这个线程,一直等到有对方发出连接请求来,否则这个线程一直在后台运行,所以有时监听然后复位后,这个监听线程还是在运行,以至于复位后选择主动连接,这个线程仍然在运行,以至于后