毕业设计linux环境下基于tcpip及socket技术的远程文件传送控制编程报告.docx
《毕业设计linux环境下基于tcpip及socket技术的远程文件传送控制编程报告.docx》由会员分享,可在线阅读,更多相关《毕业设计linux环境下基于tcpip及socket技术的远程文件传送控制编程报告.docx(25页珍藏版)》请在冰豆网上搜索。
毕业设计linux环境下基于tcpip及socket技术的远程文件传送控制编程报告
Linux环境下基于TCP/IP及Socket技术的远程文件传送控制
编程报告
2013年5月
目录
一、编程目的3
二、通信原理3
(一)TCP/IP3
(二)Socket4
三、Linux虚拟环境安装7
(一)VMware简介7
(二)Linux简介8
(三)Linux虚拟环境安装9
四、设计过程11
(一)服务器端创建监听与文件管理11
(二)客户端连接与文件传输12
五、结果演示12
六、代码开发13
(一)服务器端13
(二)客户端18
七、结束语20
一、编程目的
这次程序设计的目标是在以Linux为内核的操作系统下,实现多线程文件传输系统功能模块。
系统模块分为服务器和客户端两部分,客户端实现对文件的上传、下载和查看服务器默认路径下的文件列表;服务器可以对文件进行管理操作,包括创建、删除和重命名等。
多线程文件传输是一种一对多或者多对多的关系,一般是一个服务器对应着多个客户端。
客户端通过socket连接服务器,服务器要为客户端创建一个单独进程(线程)监听每个客户端的请求。
创建好连接之后文件就可以通过流的形式传输。
linux内核中为我们提供了两种不同形式的读写流,包括read()、write()和send()、recv()。
客户机对文件的查看指令也是通过流传递给服务器,服务器根据请求类型返回不同相应流。
根据socket原理和特点绘画出链接流程图,将客户机与服务器的相互通信划分为不同的模块,每个模块负责独立的功能项。
服务器输入指令管理目录下的文件,createfilename是创建文件命令,renameoldnamenewname是删除文命令,deletefilename是删除文件命令,同时监听着客户端的请求;客户端向服务器发送上传、下载和查看请求,从而得到不同的相应,包括将文件下载到当前路径下,从当前路径下上传文件给服务器,列出服务器的文件列表。
二、通信原理
Linux是计算机使用的主流操作系统之一,TCP/IP是广为应用的互联网协议,Linux为TCP/IP网络编程提供了一种网络进程通信机制:
套接字接口(Sockets Interface)。
以下将介绍Linux环境下套接字的基本概念及编程技术,并结合实例说明在Linux下如何用套接字实现客户机/服务器方式的进程通信。
(一)TCP/IP
TCP/IP即传输控制协议/网际协议(TransmissionControlProtocol/InternetProtocol),是一个由多种协议组成的协议族,他定义了计算机通过网络互相通信及协议族各层次之间通信的规范。
TCP是一种面向连接的、可靠的、双向的通信数据流,说他可靠,是因为他使用3段握手协议传输数据,并且在传输时采用“重传肯定确认”机制保证数据的正确发送:
接收端收到数据后要发出一个肯定确认,而发送端必须要能接收到这个肯定信号,否则就要将数据重发。
在此原理基础之上,设计了基于Linux操作系统下TCP/IP编程实现文件传输的实例。
我们采用客户机/务器模式通信时,通信双方发送/接收数据的工作
Linux支持BSD的套接字和全部的TCP/IP协议,是通过网络协议将其视为一组相连的软件层来实现的。
BSD套接字(BSDSocket)由通用的套接字管理软件支持,该软件是INET套接字层,用来管理基于IP的TCP与UDP端口到端口的互联问题。
从协议分层来看,IP是网络层协议,TCP是一个可靠的端口到端口的传输层协议,他是利用IP层进行传接报文的;同时也是面向连接的,通过建立一条虚拟电路在不同的网络问传输报文,保证所传输报文的无丢失性和无重复性。
用户数据报文协议(UserDatagramProtocol,UDP)也是利用IP层传输报文,但他是一个非面向连接的传输层协议。
利用IP层传输报文时,当目的方网际协议层收到IP报文后,必须能够识别出该报文所使用的上层协议(即传输层协议)。
因此,在IP报头中,设有一个“协议”域(Protoco1)。
通过该域的值,即可判明其上层协议类型。
传输层与网络层在功能上的最大区别是前者提供进程通信能力,而后者则不能。
在进程通信的意义上,网络通信的最终地址不仅仅是主机地址,还包括可以描述进程的某种标识符。
为此,TCP/UDP提出了协议端口(ProtocolPort)的概念,用于标识通信的进程。
例如,Web服务器进程通常使用端口80,在/etc/serv~ices文件中有这些注册了的端口地址。
对于TCP传输,传输节点问先要建立连接,然后通过该连接传输已排好序的报文,以保证传输的正确性。
IP层中的代码用于实现网际协议,这些代码将IP头增加到传输数据中,同时也把收到的IP报文正确地转送到TCP层或UDP层。
TCP是一个面向连接协议,而UDP则是一个非面向连接协议,当一个UDP报文发送出去后,Linux并不知道也不去关心他是否成功地到达了目的主机。
IP层之下,是支持所有Linux网络应用的网络设备层,例如:
点到点协议(PointtoPointProtocol,PPP)和以太网层。
网络设备并非总代表物理设备,其中有一些(例如回送设备)则是纯粹的软件设备,网络设备与标准的Linux设备不同,他们不是通过Mknod命令创建的,必须是底层软件找到并进行了初始化之后,这些设备才被创建并可用。
因此只有当启动了正确设置的以太网设备驱动程序的内核后,才会有/dev/eth0文件。
ARP协议位于IP层和支持地址解析的协议层之间。
(二)Socket
1、套接字简介
套接字(Socket)是网络通信的基本操作单元,它提供了不同主机间进程双向通信的端点,这些进程在通信前各自建立一个Socket,并通过对Socket的读/写操作实现网络通信功能。
套接字分为以下3种类型。
1)字节流套接字
这是最常用的套接字类型,TCP/IP协议簇中的TCP(TransportControlProtocol)协议使用此类接口,它提供面向连接的(建立虛电路)、无差错的、发送先后顺序一致的、包长度不限和非重复的网络信包传输。
2)数据报套接字
TCP/IP协议族中的UDP(User Datagram Protocol)协议使用此类接口,它是无连接的服务,以独立的信包进行网络传输,信包最大长度为32KB,传输不保证顺序性、可靠性和无重复性,它通常用于单个报文传输或可靠性不重要的场合。
3)原始数据报套接字
提供对网络下层通讯协议(如IP协议)的直接访问,它一般不是提供给普通用户的,主要用于开发新的协议或用于提取协议较隐蔽的功能。
2、套接字编程方法
这里将分别介绍面向连接协议的字节流套接字与非连接协议的数据报套接字的编程方法,因原始数据报套接字在实际工作中较少,在此不作讨论。
不论何种套接字编程均采用客户机/服务器的协作模式,即由客户进程向服务器进程发出请求,服务器进程执行被请求的任务并将结果返回给客户进程。
字节流套接字的服务进程和客户进程在通信前必须建立连接。
建立连接及通信的步骤见图1。
1)服务进程首先调用Socket()创建一个字节流套接字,并调用bind()将服务器地址捆扎在该套接字上,接着调用listen()监听连接请求 ,随后调用accept()做好与客户进程建立连接的准备,无连接请求时,服务进程被阻塞;
2)客户进程调用Socket()创建字节流套接字,然后调用connect()向服务进程发出连接请求;
3)当连接请求到来后,服务进程被唤醒,生成一个新的字节流套接字,并用新套接字同客户进程的套接字建立连接,而服务进程最早生成的套接字则继续用于监听网络上的服务请求;
4)服务进程和客户进程通过调用read()和write()交换数据;
5)服务进程和客户进程通过调用close()撤消套接字并中断连接。
数据套接字的服务进程客户进程通信前不必建立连接, 通信的步骤见图2。
1)服务进程首先调用Socket()创建一个数据套接字,并调用Bind()将服务器地址捆扎在该套接字上,然后调用Recvfrom()等待客户进程发来的请求;
2)客户进程在调用Socket()创建一个数据报套接字后,调用Bind()将客户机地址捆扎在此套接字上,接着调用Sendto()向服务进程发送请求,然后调用Recvfrom()等待服务进程返回该请求的处理结果;
3)服务进程在执行客户进程所请求的任务后,调用Sendto()将处理结果返回给客户进程;
4)服务进程和客户进程通过调用Close()撤消套接字。
3、套接字编程示例
下面给出一个运用字节流套接字在TCP/IP网络上实现客户机/服务器方式进程通信的实例。
在此例中,服务进程先于客户进程运行,当双方建立连接后,服务进程通过该连接向客户进程不断发送一个连续增长的序列数,客户进程每接收到50个序列数就在屏幕上显示一个‘.’,显示至20个点后换行,直至任意一方进程被中断为止。
# include
# include
# include
# include
# include
main()
{
int sock,namelen,seq,netint;
struct sockaddr-in server;
//存服务器的internet地址
char msgsock;
char buf[1024];
//创建internet域的TCP协议的字节流套接字
sock=socket(AF-INET,SOCK-STREAM,IPPROTO-TCP);
if(sock<0){
perror("socket");
exit
(1);
}
//将本地主机(服务器)的地址捆扎到创建的套接字上
server.sinfamily=AF-INET;
//internet域
sevrer.sinaddr.s-addr=INADDR-ANY; //使用任意合法地址
sevrer.sinport=htons(1032);
//公认的服务端口号
if(bind(sock,&server,sizeof(server))<0){
perror("bind");
exit
(2);
}
//阻塞至客户方有连接请求到来,建立一新套接字用于通信
namelen=sizeof(server);
if((msgsock=accept(sock,&server,&namelen))<0){
perror("accept");
exit(4);
}
//此时连接已建立,可以进行通信
seq=0;
for(;;){
netint=htonl(seq);
//主机字节顺序转为网络字节顺序
write(msgsock,&netint,4);
//向客户方写序列数
seq++;
}
}
三、Linux虚拟环境安装
为了便于程序的开发和测试,本次利用虚拟机软件VMware在同一台计算机上安装Linux操作系统,实现程序开发环境的搭建。
(一)VMware简介
美国VMware公司推出的虚拟计算平台——VMware提供了一种在一台计算机上同时运行各种Linux和Windows操作系统的具有创新意义的解决方案。
VMware支持的系统包括:
各种Linux发行版、DOS、Windows3.1、Windows95、Windows98、WindowsNT,Windows2000、WindowsMe以及FreeBSD等。
1、VMware的特点
在本书后续章节中,使用Guest来表示VMware虚拟的客户机,GuestOS指运行在虚拟机上的操作系统;Host指物理存在的计算机,HostOS指在Host上运行的操作系统。
例如,在操作系统为Windows的计算机上安装了VMware,并虚拟了Linux。
那么,Host是安装Windows的这台计算机,HostOS是Windows,虚拟机上运行的Linux就是GuestOS。
VMware具有以下特点:
VMware为虚拟计算机提供了BIOS,你可以像对独立的计算机那样更改虚拟机的BIOS参数设置。
可以在窗口模式下运行Guest,也可以在全屏模式下运行。
当你从GuestOS切换到HostOS屏幕之后,系统将自动保存GuestOS上运行的所有任务,以避免由于HostOS的崩溃而损失GuestOS应用程序中的数据。
每一个虚拟机操作系统都是相对独立的,并拥有独立的网络地址。
Guest和Host之间支持TCP/IP、NovellNetware、Microsoft虚拟网络以及Samba文件共享等协议。
4.2节将详细介绍WindowsHost与LinuxGuest之间的文件共享方式。
VMware支持GuestOS和HostOS之间以及不同GuestOS操作环境下的剪切、复制和粘贴操作。
在硬件方面,VMware支持CD-ROM、软驱以及音频的输入输出,从VMware2.03开始增加了对SCSI设备、SVGA图形加速卡以及ZIP驱动器的支持。
VMware窗口提供了打开虚拟机电源、关闭虚拟机电源以及复位键等按钮,这些按钮就如同虚拟机机箱上的按钮一样。
例如,对WindowsGuest来说,如果在运行过程中非正常关机或者VMware崩溃,下次启动Guest的时候,它会自动进行文件系统的检查和修复。
2、VMware的运行原理
借助VMware提供的虚拟功能,可以在同一个窗口运行多个全功能的操作系统。
VMware中的GuestOS直接在X86保护模式下运行,使所有的虚拟机操作系统就像运行在单独的计算机上一样。
VMware运行在主机的操作系统之上,它在GuestOS与HostOS之间加了一层虚拟操作平台(VMwareVirtualPlatform),对下层的硬件和相关软件进行了虚拟,形成独立的计算机系统,所有的GuestOS都运行在一个独立的虚拟计算机之上。
(二)Linux简介
Linux是一种自由和开放源代码的类UNIX操作系统。
定义Linux的组件是Linux内核,该操作系统内核由林纳斯·托瓦兹在1991年10月5日首次发布。
严格来讲,术语Linux只表示操作系统内核本身,但通常采用Linux内核来表达该意思。
Linux则常用来指基于Linux内核的完整操作系统,包括GUI组件和许多其他实用工具。
由于这些支持用户空间的系统工具和库主要由理查德·斯托曼于1983年发起的GNU计划提供,自由软件基金会提议将该组合系统命名为GNU/Linux。
Linux最初是作为支持英特尔x86架构的个人电脑的一个自由操作系统。
目前Linux已经被移植到更多的计算机硬件平台,远远超出其他任何操作系统。
Linux是一个领先的操作系统,可以运行在服务器和其他大型平台之上,如大型主机和超级计算机。
世界上500个最快的超级计算机90%以上运行Linux发行版或变种,包括最快的前10名超级电脑运行的都是基于Linux内核的操作系统[10]。
Linux也广泛应用在嵌入式系统上,如手机,平板电脑,路由器,电视和电子游戏机等。
在移动设备上广泛使用的Android操作系统就是创建在Linux内核之上。
Linux也是自由软件和开放源代码软件发展中最著名的例子。
只要遵循GNU通用公共许可证,任何个人和机构都可以自由地使用Linux的所有底层源代码,也可以自由地修改和再发布。
通常情况下,Linux被打包成供个人计算机和服务器使用的Linux发行版,一些流行的主流Linux发布版,包括Debian(及其派生版本Ubuntu,LinuxMint),Fedora(及其相关版本RedHatEnterpriseLinux,CentOS)和openSUSE等。
Linux发行版包含Linux内核和支撑内核的实用程序和库,通常还带有大量可以满足各类需求的应用程序。
个人计算机使用的Linux发行版通常包XWindow和一个相应的桌面环境,如GNOME或KDE。
桌面Linux操作系统常用的应用程序,包括Firefox网页浏览器,LibreOffice办公软件,GIMP图像处理工具等。
由于Linux是自由软件,任何人都可以创建一个符合自己需求的Linux发行版。
(三)Linux虚拟环境安装
1、第一步:
安装VMware虚拟机,安装过程不再赘述,安装后如图3所示。
图3VMware安装截图
2、第二步:
安装RedHatLinux,安装过程不再赘述,安装后如图4所示。
图4Linux安装截图
3、第三步:
点击“打开虚拟机电源”,运行Linux,用root登录系统(如图5所示),进行XWindows。
图5Linux登录截图
4、第四步:
在XWindows下点击左下角的小红帽图标,然后选择“系统工具”-“终端”,打开终端,开始程序编制,如图6所示。
图6Linux终端窗口截图
至此Linux环境已安装完成,具备程序开发条件。
四、设计过程
(一)服务器端创建监听与文件管理
服务器负责的功能模块主要有两部分,一是对连接进来客户端所有线程的管理和服务器目录下的文件管理;二是创建线程来单独监听客户端的动作。
为了便于管理,我们创建两个user.txt和client.txt两个文档来分别负责服务器的连接和客户端的连接。
user.txt中存放了服务器名和密码。
client.txt存放了连接客户端名字和密码。
我们首先对服务器的创建有个监测,即在启动时先核实服务器的所有者username和密码password,将输入的用户、密码与user.txt中的用户密码比较,匹配成功则同意启动,否则return-1表失败。
接着创建一个socket套接口,绑定Ip设置客户端的最大连接数为10,然后创建一个sever线程来实现对服务器本身监听动作。
主体代码见最后
接下来创建线程完成对客户端的监听
监听等待连接:
while
(1)
{
sockdata=accept(sockfd,(structsockaddr*)0,(int*)0);
…………….
我们定义结构体:
structclient_t
{
pthread_ttid;
intconn_fd;
intused;
charname[20];
}p_client[10];
来存放每个客户端的socket信息、线程标识、使用号、连接号和客户名。
创建线程实现单独监听:
p_client[i].conn_fd=sockdata;
p_client[i].used=i;
strcpy(p_client[i].name,client_name);pthread_create(&p_client[i].tid,NULL,&client_conn,&p_client[i])
接下来是线程client_conn()的功能,监听客户端的功能完成。
(二)客户端连接与文件传输
在客户端这边我们同样适用了检测机制,运行客户机时要将用户名、密码以及ip地址和端口号作为参数输进来,先建立与服务器的连接,然后将用户名和密码发送到服务端检测,如果检测失败则接收到一条拒绝信息,连接断开,如果检测成功则接收到一条确认信息,双方通信开始。
主体代码见最后:
到此为止我们已经实现了服务器和客户端的主体功能,具体代码查看附录文件夹。
五、结果演示
六、代码开发
(一)服务器端
#include
#include//包含套接字函数库
#include
#include//包含AF_INET相关结构
#include//包含AF_INET相关操作的函数
#include
#include
#include
#include
#include
#include
#definePORT9999
#defineBUFFSIZE1000
#defineMAXPATH32
#defineLENsizeof(structlist)
intcount=0;//计量歌曲数
doublefilesize;//文件总大小
structlist
{
charpathname[1024];
charfilename[512];
structlist*next;
};
structlist*head,*p1,*p2;
voidscan_dir(char*dir,intdepth)//定义目录扫描函数
{
DIR*dp;
structdirent*entry;
structstatstatbuff;
intl;
if(!
(dp=opendir(dir)))
{
//puts("can'topen");
return;
}
chdir(dir);//切换到当前目录中去
while((entry=readdir(dp))!
=NULL)
{
lstat(entry->d_name,&statbuff);//获取下一级成员属性
if(S_IFDIR&statbuff.st_mode)//判断下一级成员是否是目录
{
if(strcmp(".",entry->d_name)==0||
strcmp("..",entry->d_name)==0)
continue;
//printf("%*s%s/\n",depth,"",entry->d_name);
scan_dir(entry->d_name,depth+4);//调用自身,扫描下一级
}
else
{
l=strlen(entry->d_name);
l-=4;
if(strcmp(entry->d_name+l,".mp3")==0||
strcmp(entry->d_name+l,".MP3")==0)
{charpath_buff[MAXPATH];
getcwd(path_buff,MAXPATH);
p1=malloc(LEN);
strcpy(p1->pathname,path_buff);
strcpy(p1->filename,entry->d_name);
//printf("%shello%s",p1->pathname,p1->filename);
p1->next=0;
count++;
if(count==1)
head=p2=p1;
else
{p2->next=p1;
//p2=p2->next;
p2=p1;
//printf("hello");
}
inttem;
tem=statbuff.st_size;
filesize+=tem;
}
}
}
chdir("..");//回到上一级目录
closedir(dp);
}
intprint()
{