课程设计课程名称计算机网络课程设计题目名称文件传输协议的简单设计与实现学生学院专业班级___ _学号学生姓名______ _________指导教师______ _____2010 年 1 月 5 日设计摘要关键词:SOCKET编程,FTPclient/server程序摘要:本课程设计包含了文件传输协议的简单设计与实现。
文件传送是各种计算机网络实现的基本功能,文件传送协议是一种最基本的应用层协议按照客户/服务器的模式进行工作,提供交互式的访问,是INTERNET使用最广泛的协议之一。
文件传输协议的简单设计与实现建立在计算机网络实验环境TCP/IP 网络体系结构之上,使用socket 编程接口编写两个程序,分别为客户程序(client.c)和服务器程序(server.c),实现下述命令功能:get , put, pwd, dir, cd, ?, quit 等,利用了已有网络环境设计并实现简单应用层协议。
本设计包括了具体设计任务,基本思路及所涉及的相关理论,设计流程图,调试过程中出现的问题及相应解决办法,实验运行结果,核心程序,个人体会及建议等。
目录1、文件传输协议的简单设计与实现------------------------------181. 1 具体设计任务----------------------------------------------18 1.2 基本思路及所涉及的相关理论--------------------------------181.2.1基本思路-------------------------------------------------182.2.2 相关理论--------------------------------------------18 1.3设计流程图------------------------------------------------191.4实验运行情况----------------------------------------------191.5 核心程序--------------------------------------------------222.5.1 服务器(sever)程序---------------------------------222.5.2 客户(client)程序----------------------------------291.6心得体会-----------------------------------------------------------------------------37参考文献--------------------------------------------------------382、文件传输协议的简单设计与实现2. 1 具体设计任务计算机网络实验环境建立在TCP/IP 网络体系结构之上。
各计算机除了安装TCP/IP 软件外,还安装了TCP/IP 开发系统。
实验室各计算机具备Windows环境中套接字socket 的编程接口功能,可为用户提供全网范围的进程通信功能。
本实验要求利用这些功能,设计和实现一个简单的文件传送协议。
用socket 编程接口编写两个程序,分别为客户程序(client.c)和服务器程序(server.c),该程序应能实现下述命令功能:get:取远方的一个文件put:传给远方一个文件pwd:显示远主当前目录dir:列出远方当前目录cd :改变远方当前目录?:显示你提供的命令quit :退出返回2.2基本思路及所涉及的相关理论2.2.1基本思路设计程序使客户端连接的时候,服务器将会向客户端发回一条消息告知它的IP地址,然后关闭连接并继续接收端口的连接。
建立各个命令功能对应的函数,发送请求,等待服务器端的服务。
服务器端初始化WinSock,创建SOCKET,获取主机信息,并对客户端进行会话,发送回复讯息给客户端,响应完毕后关闭连接,释放WinSock。
2.2.2 相关理论文件传送是各种计算机网络都实现的基本功能,文件传送协议是一种最基本的应用层协议按照客户/服务器的模式进行工作,提供交互式的访问,是INTERNET使用最广泛的协议之一。
计算机网络实验环境建立在TCP/IP 网络体系结构之上。
各计算机除了安装TCP/IP 软件外,还安装了TCP/IP 开发系统。
实验室各计算机具备Windows 环境中套接字socket 的编程接口功能,可为用户提供全网范围的进程通信功能。
本设计利用这些功能,设计和实现一个简单的文件传送协议。
用socket 编程接口编写两个程序,分别为客户程序(client.c)和服务器程序(server.c)。
2.3 设计流程图2.4实验运行情况服务器端运行,默认自动启动监听,情况如下图:服务如果关闭监听,效果如下图:客服端运行,如下图:输入服务器端的IP和端口号进行连接,效果如图:下面设置文件保存位置,如图:下面演示上传一个视频文件,然后再下载下来,其中上传过程的进度信息:上传完成后的效果图:下载文件的进度提示信息:下载完成后,指定目录下就有了该文件了:2.5 核心程序2.5.1 服务器(server)程序如下using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;using ;using .Sockets;using System.IO;using System.Windows.Forms;namespace TCP文件传输_服务器{class WorkThread{byte[] buffer;int bufferSize= 4096;Thread mythread;Socket handler;public WorkThread(Socket workSocket){handler = workSocket;buffer = new byte[bufferSize];mythread = new Thread(BeginCommunication);}public void Start(){mythread.Start();}public void Abort(){mythread.Abort();}private void BeginCommunication(){while (true)//从客户端接收数据,直到连接断开{try{int reCount = handler.Receive(buffer);string insStr =Encoding.BigEndianUnicode.GetString(buffer, 0, reCount);if (insStr.Substring(0, 4) == "name")//收到下文件请求{string fileName = insStr.Substring(5, insStr.Length - 5);SendFile(fileName, handler);}else if (insStr.Substring(0, 4) == "boot")//收到获取根目录请求 {string listStr = GetFileNames("F:", false);SendString(handler, listStr);}else if (insStr.Substring(0, 4) == "path")//收到获取指定目录内容请求{string path = insStr.Substring(5, insStr.Length - 5);string listStr = GetFileNames(path, false);SendString(handler, listStr);}else if (insStr.Substring(0, 4) == "Queu")//获取下载文件夹请求,回复完整文件名列表{string path = insStr.Substring(5, insStr.Length - 5);string listStr = GetFileNames(path, true);SendString(handler, listStr);}else if (insStr.Substring(0, 4) == "upld")//收到上传文件请求{string reFileName = insStr.Substring(5, insStr.Length - 5);LoadFile(reFileName, handler);}else if (insStr.Substring(0, 4) == "quit")//收到断开连接命令{HostQuit();break;}}catch { HostQuit();break; }}}private void LoadFile(string reFileName, Socket handler){try{FileStream fs = new FileStream(reFileName, FileMode.Create, FileAccess.Write);handler.Receive(buffer, 8, SocketFlags.None);//接收文件大小long filesize = BitConverter.ToInt64(buffer, 0);long receiveCount = 0;while (receiveCount < filesize)//接收文件{int readcount = handler.Receive(buffer, bufferSize, SocketFlags.None);fs.Write(buffer, 0, readcount);receiveCount += readcount;}fs.Close();}catch{HostQuit();}}private void SendFile(string fileNmae, Socket handler){try{FileStream fs = new FileStream(fileNmae, FileMode.Open, FileAccess.Read);long size = fs.Length;byte[] bysize = BitConverter.GetBytes(size);handler.Send(bysize, 8, SocketFlags.None);//发送文件大小BinaryReader br = new BinaryReader(fs);int sendcount = 0;while (sendcount < size)//发送文件{int readcount = br.Read(buffer, 0, bufferSize);sendcount += readcount;handler.Send(buffer, readcount, SocketFlags.None); }}catch{HostQuit();}}private void HostQuit(){GlobalValues.canControl.WaitOne();GlobalValues.canControl.Release();if (handler.Connected == true){handler.Shutdown(SocketShutdown.Both);handler.Close();}}private void SendString(Socket handler,string listStr){byte[] list = Encoding.BigEndianUnicode.GetBytes(listStr);byte[] listcountbuffer =BitConverter.GetBytes(Convert.ToInt64(list.Length));try{handler.Send(listcountbuffer, 8, SocketFlags.None);//发送列表大小int sendcount = 0;while (sendcount < list.Length)//发送列表{if (sendcount + bufferSize < list.Length)//不是最后一次{int i = handler.Send(list, sendcount, bufferSize, SocketFlags.None);sendcount += i;}else{int i= handler.Send(list, sendcount, list.Length- sendcount, SocketFlags.None);//发送最后一次sendcount += i;}}}catch (Exception e){MessageBox.Show("连接断开.");}}//fileOnly=true则获取指定目录及子目录内所有文件信息,fileOnly=false则获取指定目录文件信息及文件夹信息private string GetFileNames(string path, bool fileOnly){string listStr = string.Empty;DirectoryInfo drct = new DirectoryInfo(path);foreach (FileSystemInfo fsi in drct.GetFileSystemInfos()){if (fsi is FileInfo){//文件FileInfo fi = (FileInfo)fsi;listStr += fi.FullName;listStr += "\n";if (fi.Length < 1024){string sizeStr = fi.Length.ToString() + "字节";listStr += sizeStr;}else if (fi.Length < 1024 * 1024){double temp = Convert.ToDouble(fi.Length) / 1024;string sizeStr = string.Format("{0:F1}", temp) + "KB";listStr += sizeStr;}else if (fi.Length < 1024 * 1024 * 1024){double temp = Convert.ToDouble(fi.Length) / 1024 / 1024;string sizeStr = string.Format("{0:F1}", temp) + "MB";listStr += sizeStr;}else{double temp= Convert.ToDouble(fi.Length) / 1024 / 1024 / 1024;string sizeStr = string.Format("{0:F1}", temp) + "GB";listStr += sizeStr;}listStr += "\n";listStr += stWriteTime.ToString("yyyy-MM-dd hh:mm:ss");listStr += "\n";}else if (fsi is DirectoryInfo){if (fileOnly){listStr += GetFileNames(fsi.FullName, true);}else{//文件夹DirectoryInfo di = (DirectoryInfo)fsi;listStr += di.FullName;listStr += "\n";listStr += "dir";listStr += "\n";listStr+= stWriteTime.ToString("yyyy-MM-dd hh:mm:ss");listStr += "\n";}}}return listStr;}}}public服务器(){InitializeComponent();Control.CheckForIllegalCrossThreadCalls = false;string HostName = Dns.GetHostName(); //得到主机名IPHostEntry IpEntry = Dns.GetHostEntry(HostName); //得到主机IPstring strIPAddr = IpEntry.AddressList[0].ToString();tsTxb_IPAdd.Text = strIPAddr;hostIPAddress = IPAddress.Parse(strIPAddr);Server = new IPEndPoint(hostIPAddress,Convert.ToInt32(tsTxb_Port.Text));sock = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);sock.Bind(Server);sock.Listen(10);string str = "正在监听端口 " + tsTxb_Port.Text + "...";tsLbl_Status.Text = "状态:" + str;AcceptTh = new Thread(ThAccept);AcceptTh.Start();//开始监听端口}//下面是监听和停止监听的按钮事件private void btn_Start_Click(object sender, EventArgs e){tsBtn_Start.Enabled = false;tsBtn_Stop.Enabled = true;Server= new IPEndPoint(hostIPAddress, Convert.ToInt32(tsTxb_Port.Text));sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);sock.Bind(Server);sock.Listen(10);string str = "正在监听端口 " + tsTxb_Port.Text + "...";tsLbl_Status.Text = "状态:" + str;AcceptTh = new Thread(ThAccept);AcceptTh.Start();}private void btn_stop_Click(object sender, EventArgs e){tsBtn_Start.Enabled = true;tsBtn_Stop.Enabled = false;try{sock.Shutdown(SocketShutdown.Both);}catch { }sock.Close();tsLbl_Status.Text = "状态:已停止监听";}2.5.2 客户(client)程序//MyThread.cs中的实现using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;using .Sockets;using System.Threading;using System.Windows.Forms;namespace客户端_TCP文件传输{public class myThread{Thread downloadTh;Thread uploadTh;Socket wordSocket;string fileName;string savePath;byte[] buffer;const int bufferSize = 4096;ToolStripProgressBar tspBar;ToolStripStatusLabel speed;int startTime=0;long receivecount = 0;long sendCount = 0;int upOrDown = 1;//指示上传或下载System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();public myThread(Socket workSock, string FileName,string SavePath, ref ToolStripProgressBar TspBar, ref ToolStripStatusLabel Speed){wordSocket = workSock;fileName = FileName;savePath = SavePath;buffer = new byte[bufferSize];downloadTh = new Thread(new ThreadStart(Download));uploadTh = new Thread(new ThreadStart(Upload));tspBar = TspBar;speed = Speed;}public myThread(Socket workSock, string FileName,ref ToolStripProgressBar TspBar, ref ToolStripStatusLabel Speed){wordSocket = workSock;fileName = FileName;buffer = new byte[bufferSize];downloadTh = new Thread(new ThreadStart(Download));uploadTh = new Thread(new ThreadStart(Upload));tspBar = TspBar;speed = Speed;}public void StartDownload(){upOrDown = 1;downloadTh.Start();timer1.Enabled = true;timer1.Interval = 100;timer1.Tick += new EventHandler(timer1_Tick);startTime = System.Environment.TickCount;timer1.Start();}public void StartUpload(){upOrDown = 0;uploadTh.Start();timer1.Enabled = true;timer1.Interval = 50;timer1.Tick += new EventHandler(timer1_Tick);startTime = System.Environment.TickCount;timer1.Start();}void timer1_Tick(object sender, EventArgs e){int spentTime = System.Environment.TickCount - startTime;if (spentTime != 0){if (upOrDown == 1){double sp = Convert.ToDouble(receivecount) /Convert.ToDouble(spentTime) * 1000 / 1024;if (sp < 1024)speed.Text = tspBar.Value.ToString() + "% 下载速度:" + string.Format("{0:F1}", sp) + "KB";else speed.Text = tspBar.Value.ToString() + "% 下载速度:" + string.Format("{0:F1}", sp / 1024) + "MB/S";}else{double sp = Convert.ToDouble(sendCount) /Convert.ToDouble(spentTime) * 1000 / 1024;if (sp < 1024)speed.Text = tspBar.Value.ToString() + "% 上传速度:" + string.Format("{0:F1}", sp) + "KB";else speed.Text = tspBar.Value.ToString() + "% 上传速度:" + string.Format("{0:F1}", sp / 1024) + "MB/S";}}}private void Download(){try{string ins = "name=" + fileName;byte[] data = Encoding.BigEndianUnicode.GetBytes(ins);wordSocket.Send(data, data.Length, SocketFlags.None);//发送下载请求string reFileName = savePath + "\\" + GetFileName(fileName);Directory.CreateDirectory(savePath);FileStream fs = new FileStream(reFileName, FileMode.Create, FileAccess.Write);wordSocket.Receive(buffer, 8, SocketFlags.None);//接收文件大小long filesize = BitConverter.ToInt64(buffer, 0);while (receivecount < filesize)//接收文件{int readcount = wordSocket.Receive(buffer, bufferSize, SocketFlags.None);fs.Write(buffer, 0, readcount);receivecount += readcount;tspBar.Value = Convert.ToInt32(Convert.ToDouble(receivecount) / Convert.ToDouble(filesize) * 100);}tspBar.Value = 100;timer1_Tick(new object(), new EventArgs());fs.Close();timer1.Stop();StaticValue.isBusy = false;}catch{MessageBox.Show("连接断开.");timer1.Stop();if (wordSocket.Connected == true){wordSocket.Shutdown(SocketShutdown.Both);wordSocket.Close();timer1.Stop();StaticValue.isBusy = false;}}}private void Upload(){try{string shortFileName= fileName.Substring(stIndexOf('\\') + 1, fileName.Length - stIndexOf('\\') - 1);string serverFileName = StaticValue.curServerPath + "\\" + shortFileName;//指定上传到服务器的哪个路径string ins = "upld=" + serverFileName;byte[] byins = Encoding.BigEndianUnicode.GetBytes(ins);wordSocket.Send(byins, byins.Length, SocketFlags.None);//发送上传请求及完整文件名FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);long size = fs.Length;byte[] bysize = BitConverter.GetBytes(size);wordSocket.Send(bysize, 8, SocketFlags.None);//发送上传文件大小BinaryReader br = new BinaryReader(fs);while (sendCount < size)//发送文件{int readcount = br.Read(buffer, 0, bufferSize);sendCount += readcount;wordSocket.Send(buffer, readcount, SocketFlags.None);tspBar.Value = Convert.ToInt32(Convert.ToDouble(sendCount) / Convert.ToDouble(size) * 100);}tspBar.Value = 100;timer1_Tick(new object(), new EventArgs());fs.Close();timer1.Stop();StaticValue.isBusy = false;}catch (Exception e){MessageBox.Show("连接断开.");wordSocket.Shutdown(SocketShutdown.Both);wordSocket.Close();timer1.Stop();StaticValue.isBusy = false;}}public string GetFileName(string fileName){return fileName.Substring(stIndexOf('\\')+1, fileName.Length - stIndexOf('\\')-1);}public string GetFileType(string fileName){return fileName.Substring(stIndexOf('.'), fileName.Length - stIndexOf('.'));}}}//下面给出各按钮点击事件///<summary>///连接服务器按钮点击事件///</summary>///<param name="sender"></param>///<param name="e"></param>private void btn_Connect_Click(object sender, EventArgs e){hostIPAddress = IPAddress.Parse(ttxb_IPAdd.Text);int port =Convert.ToInt32( ttxb_port.Text);Server = new IPEndPoint(hostIPAddress, port);sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);try{sock.Connect(Server);//连接服务器toolStripStatusLabel1.Text = "与远程主机" + ttxb_IPAdd.Text + " " + ttxb_port.ToString() + "连接成功";RefreshListView(GetDtListArray("boot", false));//获取服务器根目录connectDone = true;tsBtn_Connect.Enabled = false;tsBtn_DisConnect.Enabled = true;}catch{MessageBox.Show("连接失败.");if (sock.Connected == true){sock.Shutdown(SocketShutdown.Both);sock.Close();}}}///<summary>///下载文件点击事件///</summary>///<param name="sender"></param>///<param name="e"></param>private void btn_GetFile_Click(object sender, EventArgs e){DownLoad(listView1);//调用自定义方法下载文件}private void DownLoad(ListView listView1){if(listView1.SelectedIndices.Count> 0 && listView1.SelectedIndices[0] !=0)//有选定项且选定的不是"返回上层"{string size=listView1.SelectedItems[0].SubItems[1].Text;if ( size!= "")//如果选定的是文件{listView2.Items.Add(listView1.SelectedItems[0].SubItems[0].Text);listView2.Items[listView2.Items.Count-1].SubItems.Add(size);//将文件大小加入listViewlistView2.Items[listView2.Items.Count-1].ImageIndex =ICOSearcher.GetIcoIndex(listView1.SelectedItems[0].SubItems[0].Text);//获取该文件的图标int index = listView1.SelectedIndices[0] - 1;string FileName = fullNameList[index];downloadItem newfile = new downloadItem(FileName,StaticValue.curSavePath);downQueue.Enqueue(newfile);}else//如果是文件夹{string downPath=StaticValue.curServerPath +"\\"+listView1.SelectedItems[0].SubItems[0].Text;string[] listArray = GetDtListArray(downPath, true);Queue<downloadItem> tempQueue = GetDownFileQueue(listArray);while(tempQueue.Count>0){downQueue.Enqueue(tempQueue.Dequeue());}}}}///<summary>///设置保存位置的按钮点击事件///</summary>///<param name="sender"></param>///<param name="e"></param>private void tsBtn_savePath_Click(object sender, EventArgs e) { if (folderBrowserDialog1.ShowDialog() == DialogResult.OK){StaticValue.curSavePath = folderBrowserDialog1.SelectedPath;}}///<summary>///点击断开连接的按钮事件,断开连接,并清空listView等///</summary>///<param name="sender"></param>///<param name="e"></param>private void tsBtn_DisConnect_Click(object sender, EventArgs e)//{try{string ins = "quit";byte[] byins = Encoding.BigEndianUnicode.GetBytes(ins);sock.Send(byins, byins.Length, SocketFlags.None);sock.Shutdown(SocketShutdown.Both);sock.Close();tsBtn_Connect.Enabled = true;tsBtn_DisConnect.Enabled = false;DirLayer = 0;}catch { }toolStripStatusLabel1.Text = "连接已断开";connectDone = false;listView1.Items.Clear();fullNameList.Clear();}///<summary>///双击列表项的事件,返回上一级或者进入到下一级目录///</summary>///<param name="sender"></param>///<param name="e"></param>private void listView1_MouseDoubleClick(object sender, MouseEventArgs e) {if (connectDone&&!StaticValue.isBusy)//如果已连接服务器{if (listView1.SelectedIndices[0] == 0){if (DirLayer > 0){StaticValue.curServerPath = StaticValue.curServerPath.Substring(0,StaticValue.stIndexOf("\\"));//获取父目录DirLayer--;RefreshListView(GetDtListArray(StaticValue.curServerPath, false));}}else{string s =listView1.SelectedItems[0].SubItems[1].Text;if (s == ""){StaticValue.curServerPath =StaticValue.curServerPath + "\\" + listView1.SelectedItems[0].SubItems[0].Text;//获取子目录DirLayer++;RefreshListView(GetDtListArray(StaticValue.curServerPath, false));}else DownLoad(listView1);}}}///<summary>///上传文件的点击事件///</summary>///<param name="sender"></param>///<param name="e"></param>private void tsBtn_Upload_Click(object sender, EventArgs e){openFileDialog1.ShowDialog();string fileName = openFileDialog1.FileName;myThread workThread = new myThread(sock, fileName, ref toolStripProgressBar1, ref toolStripStatusLabel2);workThread.StartUpload();}2.6心得体会通过本次实验,我对网络命令有了一定的概念性认识,并熟悉了基本网络命令的使用,通过miniFTP Client/Server实验的实践,了解了SOCKET编程相关的一些知识以及用法,了解了客户端和服务器端进行通信的相关算法机制,在建立通信的基础上,实现了一些简单的功能。