vc+++网络文件传输
- 格式:doc
- 大小:443.50 KB
- 文档页数:23
C语⾔基于socket的⽂件传输(可循环发送多个⽂件)基本简介:本次⽂件传输的实现主要是通过客户端向服务器发送下载请求,然后在服务器中找到对应的⽂件并打开⽂件,再继续向客户端传送⽂件,⽽客户端就在不停的接收。
这是因为⽂件可能⽐较⼤,⼀个缓冲数组只能保存⼀部分⽂件内容,因此服务器得不断从⽂件中读取内容并发给客户端,⽽客户端得不停的循环接收。
但是在事先,得将相应要发送的⽂件(照⽚,⾳频,视频等)保存在服务器相应的⽬录下。
⽽这个是不符合实际要求的,通常来讲,是应该将客户端1的⽂件发送给客户端2,⽽服务器仅仅只是起到⼀个中转站的作⽤,即⽂件应该事先保存在客户端1下。
这⾥我们只是完成⽂件传输的相应功能就⾏了,就不在计较这些啦。
因为只要你理解了这⼀块,可以根据⾃⼰的实际需要,在进⾏修改。
具体编译:gcc server.c -o server -lpthread //这是因为在服务器中加⼊了线程函数,所以编译的时候需要加上 -lpthread 。
gcc client.c -o client记住⼀定要先运⾏服务器,在运⾏客户端。
在客户端运⾏的时候回提醒你输⼊服务器对应的pc ip,如实输⼊就⾏啦。
如果是在本机pc上进⾏测试的话,也可以输⼊0.0.0.0 。
server.c:#include <stdio.h>#include <netdb.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/types.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <netinet/in.h>#include <pthread.h>#define portnum 12345#define FILE_SIZE 500#define BUFFER_SIZE 1024void *net_thread(void * fd);int main(){//初始化套接字int server_fd=socket(AF_INET,SOCK_STREAM,0);if(-1==server_fd){perror("socket");exit(1);}//绑定端⼝和ip;struct sockaddr_in server_addr; //struct sockaddr_in为结构体类型,server_addr为定义的结构体server_addr.sin_family=AF_INET; //Internet地址族=AF_INET(IPv4协议)server_addr.sin_port=htons(portnum); //将主机字节序转化为⽹络字节序 ,portnum是端⼝号(server_addr.sin_addr).s_addr=htonl(INADDR_ANY);//IP地址if(-1==bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr))) //套接字与端⼝绑定{perror("bind");exit(6);}//开启监听if(-1==listen(server_fd,5)) //5是最⼤连接数,指服务器最多连接5个⽤户if(-1==listen(server_fd,5)) //5是最⼤连接数,指服务器最多连接5个⽤户{perror("listen");exit(7);}while(1){struct sockaddr_in client_addr;int size=sizeof(client_addr);int new_fd=accept(server_fd,(struct sockaddr *)&client_addr,&size); //server_fd服务器的socket描述字,&client_addr指向struct sockaddr *的指针,&size指向协议地址if(-1==new_fd){perror("accept");continue; //进⾏下⼀次循环}printf("accept client ip:%s:%d\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);//inet_ntoa将⼀个⼗进制⽹络字节序转换为点分⼗进制IP格式的字符串。
VC实现文件上传下载(FTP)//连接ftp服务器void CMyFtpDlg::OnConnect(){UpdateData(TRUE);//新建对话m_pInetSession=new CInternetSession(AfxGetAppName(),1,PRE_CONFIG_INTERNET_AC CESS);try{//新建连接对象m_pFtpConnection=m_pInetSession-> GetFtpConnection(m_strServer,m_strUserName, m_strPassword);}catch(CInternetException *pEx){//获取错误TCHAR szError[1024];if(pEx-> GetErrorMessage(szError,1024))AfxMessageBox(szError);elseAfxMessageBox( "There was an exception ");pEx-> Delete();m_pFtpConnection=NULL;return;}m_pRemoteFinder = new CFtpFileFind(m_pFtpConnection);//获得服务器根目录的所有文件并在列表框中显示BrowseDir( " ",&m_ctrlRemoteFiles,m_pRemoteFinder,&m_arrRemoteFiles);}//下载单个文件void CMyFtpDlg::DownFile(FILEITEM fileItem){if(fileItem.bDir == TRUE){AfxMessageBox( "本程序暂时不支持下载整个文件夹,请选择文件下载 ");}else{//格式化文件名CString strLocalFile,strRemoteFile;strRemoteFile.Format( "%s\\%s ",m_pRemoteFinder-> GetRoot(),fileItem.strFileName);strLocalFile.Format( "%s\\%s ",m_LocalFinder.GetRoot(),fileItem.strFileName);//下载if(m_pFtpConnection-> GetFile(strLocalFile,strLocalFile)){CString strMsg;strMsg.Format( "下载文件%s成功! ",fileItem.strFileName);AfxMessageBox(strMsg);}}}//上传单个文件void CMyFtpDlg::UpFile(FILEITEM fileItem){if(fileItem.bDir == TRUE){AfxMessageBox( "本程序暂时不支持上载整个文件夹,请选择文件上载 ");}else{//格式化文件名CString strLocalFile,strRemoteFile;strRemoteFile.Format( "%s\\%s ",m_pRemoteFinder-> GetRoot(),fileItem.strFileName);strLocalFile.Format( "%s\\%s ",m_LocalFinder.GetRoot(),fileItem.strFileName);//上传if(m_pFtpConnection-> PutFile(strLocalFile,strLocalFile)){CString strMsg;strMsg.Format( "上载文件%s成功! ",fileItem.strFileName);AfxMessageBox(strMsg);}}}欢迎您的下载,资料仅供参考!致力为企业和个人提供合同协议,策划案计划书,学习资料等等打造全网一站式需求。
Virtual Host Cloud Storage System VCloudStorage and File Transfer Protocol VCFTP 作者: 陈晓华 李春芝 张鹏宇
作者机构: 湖州师范学院信息与工程学院,湖州313000
出版物刊名: 电信科学
页码: 59-66页
年卷期: 2011年 第7期
主题词: VCloudStorage VCFTP 文件传输协议 云存储 虚拟主机集群 整数规划模型
摘要:对于广域网下的文件传输和管理,云存储系统提供Web Service API、基于文件的API、基于Block的API和其他的API,通常需要在客户端安装特定程序调用这些API实现云存储功能,测试发现如果多人同时访问,传输文件失败率较高。
设计了基于Web Service、HTTP和Flash技术的文件传输协议VCFTP,开发了基于虚拟主机集群的云存储系统VCloudStorage。
首先建立SaaS服务模型,利用HTTP数据流存储技术,建立虚拟主机存储接口;接着建立虚拟主机传输能力、存储能力和价格能力数学模型,结合用户的传输请求建立文件传输整数规划数学模型及最优化算法,最终以此为基础设计了文件传输控制协议VCFTP。
VCFTP利用Flash跨平台和富客户端技术特点,无需在客户端部署其他程序;授权的用户根据传输请求、存储要求、服务水平和当前虚拟主机状态等条件,以传输能力最优化的方式进行文件传输。
实验结果表明VCFTP具有较高的性能和稳定性,VCloudStorage总吞吐量、平均传输率和文件传输成功率均优于微软SkyDrive存储、腾讯QQ邮箱存储和单虚拟主机存储。
本文提出的VCFTP增强了文件传输性能和稳定性,是提高广域网网络存储系统性能的一条有效途径。
////////////////////////////////////#include <netinet/in.h> // for sockaddr_in#include <sys/types.h> // for socket#include <sys/socket.h> // for socket#include <stdio.h> // for printf#include <stdlib.h> // for exit#include <string.h> // for bzero#include <time.h> //for time_t and time#define HELLO_WORLD_SERVER_PORT 7754#define LENGTH_OF_LISTEN_QUEUE 20#define BUFFER_SIZE 1024int main(int argc, char **argv){struct sockaddr_in server_addr;bzero(&server_addr,sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htons(INADDR_ANY);server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);// time_t now;FILE *stream;int server_socket = socket(AF_INET,SOCK_STREAM,0);if( server_socket < 0){printf("Create Socket Failed!");exit(1);}if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))) {printf("Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT); exit(1);}if ( listen(server_socket, LENGTH_OF_LISTEN_QUEUE) ){printf("Server Listen Failed!");}while (1)struct sockaddr_in client_addr;socklen_t length = sizeof(client_addr);int new_server_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length); if ( new_server_socket < 0){printf("Server Accept Failed!\n");break;}char buffer[BUFFER_SIZE];bzero(buffer, BUFFER_SIZE);strcpy(buffer,"Hello,World! ");strcat(buffer,"\n"); //send(new_server_socket,buffer,BUFFER_SIZE,0);bzero(buffer,BUFFER_SIZE);= recv(new_server_socket,buffer,BUFFER_SIZE,0);if (length < 0){printf("Server Recieve Data Failed!\n");exit(1);}printf("\n%s",buffer);if((stream = fopen("z.mp3","r"))==NULL){printf("The file 'data1' was not opened! \n");exit(1);}elseprintf("The file 'filename' was opened! \n");bzero(buffer,BUFFER_SIZE);int lengsize = 0;while((lengsize = fread(buffer,1,1024,stream)) > 0){printf("lengsize = %d\n",lengsize);if(send(new_server_socket,buffer,lengsize,0)<0){printf("Send File is Failed\n");break;}bzero(buffer, BUFFER_SIZE);}if(fclose(stream))printf("The file 'data' was not closed! \n");exit(1);close(new_server_socket);}close(server_socket);return 0;}//client.c////////////////////////////////////#include <netinet/in.h> // for sockaddr_in#include <sys/types.h> // for socket#include <sys/socket.h> // for socket#include <stdio.h> // for printf#include <stdlib.h> // for exit#include <string.h> // for bzero#include <time.h> //for time_t and time #include <arpa/inet.h>#define HELLO_WORLD_SERVER_PORT 7754#define BUFFER_SIZE 1024int main(int argc, char **argv){if (argc != 2){printf("Usage: ./%s ServerIPAddress\n",argv[0]);exit(1);}//time_t now;FILE *stream;struct sockaddr_in client_addr;bzero(&client_addr,sizeof(client_addr));client_addr.sin_family = AF_INET;client_addr.sin_addr.s_addr = htons(INADDR_ANY);client_addr.sin_port = htons(0);int client_socket = socket(AF_INET,SOCK_STREAM,0);if( client_socket < 0){printf("Create Socket Failed!\n");exit(1);}if( bind(client_socket,(struct sockaddr*)&client_addr,sizeof(client_addr))){printf("Client Bind Port Failed!\n");exit(1);}struct sockaddr_in server_addr;bzero(&server_addr,sizeof(server_addr));server_addr.sin_family = AF_INET;if(inet_aton(argv[1],&server_addr.sin_addr) == 0){printf("Server IP Address Error!\n");exit(1);}server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);socklen_t server_addr_length = sizeof(server_addr);if(connect(client_socket,(struct sockaddr*)&server_addr, server_addr_length) < 0) {printf("Can Not Connect To %s!\n",argv[1]);exit(1);}char buffer[BUFFER_SIZE];bzero(buffer,BUFFER_SIZE);int length = recv(client_socket,buffer,BUFFER_SIZE,0);if(length < 0){printf("Recieve Data From Server %s Failed!\n", argv[1]); exit(1);}printf("\n%s\n",buffer);bzero(buffer,BUFFER_SIZE);bzero(buffer,BUFFER_SIZE);strcpy(buffer,"Hello, World! From Client\n");send(client_socket,buffer,BUFFER_SIZE,0);if((stream = fopen("data","w+t"))==NULL){printf("The file 'data' was not opened! \n");}elsebzero(buffer,BUFFER_SIZE);length = 0;while( length = recv(client_socket,buffer,BUFFER_SIZE,0)) {if(length < 0){printf("Recieve Data From Server %s Failed!\n", argv[1]); break;}int write_length = fwrite(buffer,sizeof(char),length,stream); if (write_length<length){printf("File is Write Failed\n");break;}bzero(buffer,BUFFER_SIZE);}printf("Recieve File From Server[%s] Finished\n", argv[1]); fclose(stream);close(client_socket);return 0;}。
C语言实现UDP网络传输UDP(User Datagram Protocol,用户数据报协议)是一种面向无连接的传输协议,它在网络编程中具有重要的作用。
本文将介绍C语言如何实现UDP网络传输的基本原理和步骤。
一、UDP网络传输简介UDP是一种简单的传输层协议,相对于TCP(Transmission Control Protocol,传输控制协议)来说,UDP更加轻量级。
它不提供可靠性和流量控制,但是具有实时性较高的特点,适用于需要快速传输数据的场景,如音频、视频等实时应用。
UDP协议的数据包格式主要包括源端口号、目标端口号、长度、校验和以及数据。
由于UDP是无连接的,所以每个数据包都是独立发送的,不需要建立和维护连接,这使得UDP的实现相对简单。
二、C语言实现UDP网络传输步骤要使用C语言实现UDP网络传输,我们需要按照以下步骤进行操作:1. 创建套接字(Socket)在C语言中,使用socket()函数创建一个套接字,该套接字用于后续的数据传输。
在创建套接字时,需要指定协议簇(AF_INET代表IPv4)和套接字类型(SOCK_DGRAM代表使用UDP协议)。
2. 绑定本地地址和端口号使用bind()函数将套接字与本地地址和端口号绑定,以便接收数据和发送数据。
通常将本地地址设置为INADDR_ANY,端口号可以自定义。
3. 接收数据使用recvfrom()函数接收远程主机发送的数据,该函数会将接收到的数据存储到指定的缓冲区中,并返回接收到的字节数。
可以通过指定发送方的地址和端口号来实现数据的精确接收。
4. 发送数据使用sendto()函数将数据发送给目标主机,该函数需要指定目标主机的地址和端口号,并将待发送的数据和数据长度作为参数传入。
5. 关闭套接字使用close()函数关闭套接字,释放资源。
三、C语言实现UDP网络传输示例代码```c#include <stdio.h>#include <stdlib.h>#include <string.h>#include <arpa/inet.h>#define MAX_BUFFER_SIZE 1024#define SERVER_PORT 8888#define SERVER_IP "127.0.0.1"int main() {int sockfd;char buffer[MAX_BUFFER_SIZE];struct sockaddr_in server_addr, client_addr;socklen_t client_len = sizeof(client_addr);// 创建套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("Error in creating socket");exit(1);}memset(&server_addr, 0, sizeof(server_addr));memset(&client_addr, 0, sizeof(client_addr));// 设置服务器地址和端口号server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);// 绑定本地地址和端口号if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("Error in binding");exit(1);}printf("Server is listening for incoming connections...\n");while (1) {// 接收数据memset(buffer, 0, sizeof(buffer));ssize_t recv_len = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &client_len);if (recv_len < 0) {perror("Error in receiving data");exit(1);}printf("Received data from client: %s\n", buffer);// 发送数据const char* msg = "Hello, client!";ssize_t send_len = sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)&client_addr, client_len);if (send_len != strlen(msg)) {perror("Error in sending data");exit(1);}printf("Sent response to client: %s\n", msg);}// 关闭套接字close(sockfd);return 0;}```以上是一个简单的UDP服务器示例代码,它通过创建套接字、绑定地址和端口、接收数据并发送响应的方式来实现UDP网络传输。
掌握实现网络文件传输的方法,并了解TCP连接是字符流的1.使用CSocket实现单线程文件传输2.应用多线程技术结合CAsyncSocket实现文件传输3.加入异常控制语句,增强程序的鲁棒性(Robust)4.了解如何提高套接字传输的速率及如何加强传输的稳定性·单线程文件传输:在第一章中曾经讲过用windows socket传输数据的方法,本章的单线程文件传输实验要在这个基础上融入文件操作,以实现基本的文件传输。
·多线程文件传输:在文件传输程序中,一个线程是一个搬运工,负责把文件数据从网络(看作是一条走廊)的一个端点搬到另一个端点;使用多线程,就相当于请了多个搬运工来做这项工作,效率自然会提高。
但是有一个疑问:“既然多个工人一起搬会提高工作进度,那是不是人越多越好呢?”——当然不是。
因为既然把网络看成一条走廊,那么当走廊中拥满了人的时候,这些人就成了搬运工作的瓶颈。
所以选择一个适当的线程数,对多线程传输的性能(传输速率)会有重要的影响(这一问题还会在后面探讨)。
由于多线程文件传输的实现涉及线程的同步的问题,所以实现和调试起来都有一定的困难。
但应当相信这是一项有趣的工作,因为多线程技术能把局域网传输的潜力充分的挖掘出来!建议读者再看实验步骤之前,先阅读一下文章末尾的【注意事项】一节。
这将有助于更好的理解本章的实现。
一.单线程文件传输(I):·服务器端(负责发送数据)的实现1.建立一个基于对话框的工程Server,并在建立的过程中选择支持windows socket。
2.在对话框上添加“发送”按钮。
3.为“发送”按钮添加事件BN_CLICKED的响应函数OnSend()。
void CServerDlg::OnSend(){// TODO: Add your control notification handler code hereCFileDialog fd(TRUE); // CFileDialog是MFC提供的一个用于选择文件的对话框类CString filename;char fn[40];CSocket listenSocket, socketSend;CFile file;long FileLength;char* data;if(IDOK==fd.DoModal()) // 启动用于选择文件的对话框{//选择了文件filename=fd.GetFileName(); // 获取用户选择的文件的文件名if(!file.Open(filename.GetBuffer(0),CFile::modeRead| File::typeBinary)){AfxMessageBox(" 打开文件错误,取消发送!");return;}strcpy(fn,filename.GetBuffer(0));}else return; //按了取消按钮listenSocket.Create(7000,SOCK_STREAM);listenSocket.Listen(5);listenSocket.Accept(socketSend);FileLength = file.GetLength(); // 获取文件的长度socketSend.Send(&FileLength, 4); // 把要发送的文件的长度传送给对方socketSend.Send(fn,40); // 发送要传送的文件的文件名data = new char[FileLength]; //分配一块和要传输的文件一样大小的内存空间file.ReadHuge(data, FileLength); //把文件中所有的数据一次性读入datasocketSend.Send(data, FileLength); //把data中的数据都发送出去file.Close();delete data;socketSend.Close();}·客户端(负责接收数据)的实现1.建立一个基于对话框的工程,并在建立的过程中选择支持windows socket。
基于VC 的Winsock 的文件传输程序的设计环境要求:Windows95/98/2000/XP功能要求:能将键盘上指定的文件发送到另一台计算机上;能将接收到的数据显示到屏幕窗口内,并显示收到文件的字节数和文件传输速率;一、设计目标用TC、Visual BASIC、Visual C++、Java 等编程工具和路由器、交换机、主机等网络设备提供的接口,解决网络用户之间的交互式对话问题,或计算通信网络的延迟、信道容量分配,或编码分析、通信协议分析,网络互连互通、网络规划。
进一步深入掌握网络设计和通信程序的设计原理。
使学生对计算机通信网络的设计实现有较深的了解,培养较高的通信网络设计能力。
本课设实验将基于2013版Visual Studio进行WINSOCK的文件传输的编程实现。
在WINDOWS95/98,WINDOWSNT进行WINSOCK开发使用的编程语言有很多,VC++,JAVA,DELPHI,VB等。
其中VC时使用最普遍,和WINSOCK结合最紧密的。
并且VC++对原来的WindowsSockets 库函数进行了一系列封装,继而产生了CAsynSocket、CSocket、CSocketFile等类,它们封装着有关Socket的各种功能,是编程变得更加简单。
SOCKET实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有SOCKET 接口的计算机通信。
应用程序在网络上传输,接收的信息都通过这个SOCKET接口来实现。
在应用开发中就像使用文件句柄一样,可以对SOCKET句柄进行读,写操作。
二、设计原理套接字(Socket) 是一种网络编程接口,它是对通信端点的一种抽象,提供了一种发送和接收数据的机制。
套接字有两种类型:流式套接字(St ream Socket s) 和数据报套接字(Datagram Socket s) 。
数据报套接字提供了一种不可靠的、非连接的数据包通信方式,它使用用户数据报协议(UDP) ;而流式套接字可以将数据按顺序无重复地发送到目的地,它提供的是一种可靠的面向连接的数据传输方式。
C上传⽂件到服务器(含接收端源码)实例向⼤家展⽰了如何⽤Visual C#实现⽹络⽂件的下载,使⽤Visual C#进⾏Internet通讯编程是⾮常⽅便的。
在上⾯的程序中,我们仅仅⽤到了WebClient类的⼀些⽅法,⽽WebClient类不光提供了⽹络⽂件下载的⽅法,还提供了⽂件上传的⽅法【实例简介】⼀.概述:本⽂通过⼀个实例向⼤家介绍⽤Visual C#进⾏Internet通讯编程的⼀些基本知识。
我们知道.Net类包含了请求/响应层、应⽤协议层、传输层等层次。
在本程序中,我们运⽤了位于请求/响应层的WebRequest类以及WebClient类等来实现⾼抽象程度的Internet通讯服务。
本程序的功能是完成⽹络⽂件的下载。
⼆.实现原理:程序实现的原理⽐较简单,主要⽤到了WebClient类和FileStream类。
其中WebClient类处于名字空间中,该类的主要功能是提供向URI标识的资源发送数据和从URI标识的资源接收数据的公共⽅法。
我们利⽤其中的DownloadFile()⽅法将⽹络⽂件下载到本地。
然后⽤FileStream类的实例对象以数据流的⽅式将⽂件数据写⼊本地⽂件。
这样就完成了⽹络⽂件的下载。
三.实现步骤:⾸先,打开Visual ,新建⼀个Visual C# Windows应⽤程序的⼯程,不妨命名为"MyGetCar"。
接着,布置主界⾯。
我们先往主窗体上添加如下控件:两个标签控件、两个⽂本框控件、⼀个按钮控件以及⼀个状态栏控件。
最终的主窗体如下图所⽰:完成主窗体的设计,我们接着完成代码的编写。
在理解了基本原理的基础上去完成代码的编写是相当容易。
程序中我们主要⽤到的是WebClient类,不过在我们调⽤WebClient类的实例对象前,我们需要⽤WebRequest类的对象发出对统⼀资源标识符(URI)的请求。
try{WebRequest myre=WebRequest.Create(URLAddress);}catch(WebException exp){MessageBox.Show(exp.Message,"Error");}这是⼀个try-catch语句,try块完成向URI的请求,catch块则捕捉可能的异常并显⽰异常信息。
网络通信一般都是通过Socket进行的,称为进程通信机制,通常也称作"套接字",用于描述ip地址和端口,是一个通信链的句柄。
先学习一下socket基本原理:socket原理:在Internet上有很多这样的主机,这些主机一般运行了多个服务软件,同时提供几种服务。
每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。
Socket 正如其英文原意那样,象一个多孔插座。
一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电,有的提供110伏交流电,有的则提供有线电视节目。
客户软件将插头插到不同编号的插座,就可以得到不同的服务。
Socket的形像理解:socket非常类似于电话插座。
以一个国家级电话网为例。
电话的通话双方相当于相互通信的2个进程,区号是它的网络地址;区内一个单位的交换机相当于一台主机,主机分配给每个用户的局内号码相当于socket号。
任何用户在通话之前,首先要占有一部电话机,相当于申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。
然后向对方拨号呼叫,相当于发出连接请求(假如对方不在同一区内,还要拨对方区号,相当于给出网络地址)。
对方假如在场并空闲(相当于通信的另一主机开机且可以接受连接请求),拿起电话话筒,双方就可以正式通话,相当于连接成功。
双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。
通话结束后,一方挂起电话机相当于关闭socket,撤消连接。
Socket 通信应用示图TCP/IP 客户端/服务端通信流程图通过Socket通信原理的认识,下面可以开始写一个简单的通信程序,进行验证。
在这里,做了一个局域网内文件传输程序,其实模仿飞鸽传输的功能。
在公司里面是禁用QQ,不能使用QQ,有时发现挺麻烦的,传输文件不方便,搞得经常要用U盘来拷东西。
掌握实现网络文件传输的方法,并了解TCP连接是字符流的1.使用CSocket实现单线程文件传输2.应用多线程技术结合CAsyncSocket实现文件传输3.加入异常控制语句,增强程序的鲁棒性(Robust)4.了解如何提高套接字传输的速率及如何加强传输的稳定性·单线程文件传输:在第一章中曾经讲过用windows socket传输数据的方法,本章的单线程文件传输实验要在这个基础上融入文件操作,以实现基本的文件传输。
·多线程文件传输:在文件传输程序中,一个线程是一个搬运工,负责把文件数据从网络(看作是一条走廊)的一个端点搬到另一个端点;使用多线程,就相当于请了多个搬运工来做这项工作,效率自然会提高。
但是有一个疑问:“既然多个工人一起搬会提高工作进度,那是不是人越多越好呢?”——当然不是。
因为既然把网络看成一条走廊,那么当走廊中拥满了人的时候,这些人就成了搬运工作的瓶颈。
所以选择一个适当的线程数,对多线程传输的性能(传输速率)会有重要的影响(这一问题还会在后面探讨)。
由于多线程文件传输的实现涉及线程的同步的问题,所以实现和调试起来都有一定的困难。
但应当相信这是一项有趣的工作,因为多线程技术能把局域网传输的潜力充分的挖掘出来!建议读者再看实验步骤之前,先阅读一下文章末尾的【注意事项】一节。
这将有助于更好的理解本章的实现。
一.单线程文件传输(I):·服务器端(负责发送数据)的实现1.建立一个基于对话框的工程Server,并在建立的过程中选择支持windows socket。
2.在对话框上添加“发送”按钮。
3.为“发送”按钮添加事件BN_CLICKED的响应函数OnSend()。
void CServerDlg::OnSend(){// TODO: Add your control notification handler code hereCFileDialog fd(TRUE); // CFileDialog是MFC提供的一个用于选择文件的对话框类CString filename;char fn[40];CSocket listenSocket, socketSend;CFile file;long FileLength;char* data;if(IDOK==fd.DoModal()) // 启动用于选择文件的对话框{//选择了文件filename=fd.GetFileName(); // 获取用户选择的文件的文件名if(!file.Open(filename.GetBuffer(0),CFile::modeRead| File::typeBinary)){AfxMessageBox(" 打开文件错误,取消发送!");return;}strcpy(fn,filename.GetBuffer(0));}else return; //按了取消按钮listenSocket.Create(7000,SOCK_STREAM);listenSocket.Listen(5);listenSocket.Accept(socketSend);FileLength = file.GetLength(); // 获取文件的长度socketSend.Send(&FileLength, 4); // 把要发送的文件的长度传送给对方socketSend.Send(fn,40); // 发送要传送的文件的文件名data = new char[FileLength]; //分配一块和要传输的文件一样大小的内存空间file.ReadHuge(data, FileLength); //把文件中所有的数据一次性读入datasocketSend.Send(data, FileLength); //把data中的数据都发送出去file.Close();delete data;socketSend.Close();}·客户端(负责接收数据)的实现1.建立一个基于对话框的工程,并在建立的过程中选择支持windows socket。
(为了能够利用Server端的代码,在程序编写时,可以复制Server的代码到Client目录,并在Server的基础上修改或添加代码)2.在对话框上添加“接收”按钮。
3.为“发送”按钮添加事件BN_CLICKED的响应函数OnReceive ()。
void CServerDlg::OnReceive(){// TODO: Add your control notification handler code hereCSocket socketReceive;CFile file;long FileLength;char * data;char fn[40];socketReceive.Create();socketReceive.Connect("127.0.0.1", 7000); //这里为了测试的方便,我们使用127.0.0.1本地回路IPsocketReceive.Receive(&FileLength, 4); //获取要接受的文件的长度socketReceive.Receive(fn, 40); //获取要接受的文件的文件名data = new char[FileLength];socketReceive.Receive(data, FileLength); //获取要接受的文件内容file.Open(fn,CFile::modeCreate|CFile::modeWrite | CFile::typeBinary); //在当前目录建立文件file.WriteHuge(data, FileLength);file.Close();delete data;socketReceive.Close();AfxMessageBox("接收文件成功");}上面的程序以最简单的方式实现了文件传输功能。
但正是因为它的简单,所以忽略了很多重要的东西。
读者读到这里的时候可以考虑一下可能存在着些什么问题。
在这里给出一些上面程序的不足:1.在本书的原理部分曾经提到阻塞式和非阻塞式两种套接字的工作方式。
在上面的程序中使用的CSocket类提供了阻塞式的服务,这令编写程序非常方便。
然而这却不利于程序的友好性和可用性——服务器端在没有获得客户端连接的时候固执的侦听,一点也不理会用户的命令。
对话框不再响应消息,用户只能用强制关闭来结束程序。
2.希望一次性地动态分配整个文件大小的堆存贮区作为读入数据的内存空间。
这个空间一方面受到堆大小的限制,另一方面也受到CFile类成员函数ReadHuge()和WriteHuge()的读写能力限制,还有Receive()和Send()函数一次能够发送的数据也以其参数的最大值为限,所以在遇到大中型文件的时候,系统将不能满足程序提出的动态分配大块内存的要求,这样传输便不得不终止。
3.在实际的网络传输中,网络情况是十分多变和复杂的。
通过CSocket的成员函数们的返回值可以了解传输的状态,然而在上面的程序中却没有使用这些异常控制,带来的直接后果就是当遇到网络拥塞或对方传送的数据暂时未到时,程序就会认为传输结束而出乎意料的终止。
4.在上面的程序中,程序没有传送文件的属性数据,而只通过套接字传送文件数据。
在实际应用中这种假设通常是不存在的,所以应当把文件属性也传给对方,以达到文件的完全复制。
改进方法:1.使用CAsyncSocket实现异步套接字,避免阻塞2.化整为零,把文件分若干次读入并发送3.在程序中加入异常控制语句已应付网络多变的情况4.在传送文件数据之前获取文件属性值传给对方下面将给出基于上述改进方案的前三点而重新编写的程序,对于第四点,有兴趣的读者可以自己实现。
二.单线程文件传输(II):·服务器端(负责发送数据)的实现1.建立一个基于对话框的工程Server,并在建立的过程中选择支持windows socket。
2.在对话框上添加“侦听”,“发送”按钮。
3.用Class Wizard添加一个派生于CAsyncSocket的类CMySocket。
4.添加CMySocket类的全局变量listenSocket用于侦听。
添加CMySocket类全局变量的位置,最宜在MySocket.cpp的头部。
如下:// MySocket.cpp : implementation file//#include "stdafx.h"#include "Server.h"#include "MySocket.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif/////////////////////////////////////////////////////////////////////////////// CMySocketCMySocket listenSocket;CMySocket::CMySocket(){}CMySocket::~CMySocket(){}本章中以后所有的CMySocket型全局变量都应仿效本段程序添加。
5.添加CMySocket类的全局变量sendSocket,用于传输数据6.为“发送”按钮添加事件BN_CLICKED的响应函数OnSend() #define ReadSize 500void CServerDlg ::OnSend(){// TODO: Add your control notification handler code hereCFile file;char data[ReadSize]; // 用于存放读入的文件数据块long ByteSended=0, FileLength,count;CFileDialog fd(TRUE);CString filename;char fn[40];if(IDOK==fd.DoModal()) // 启动用于选择文件的对话框{//选择了文件filename=fd.GetFileName(); //获取选择的文件的文件名if(!file.Open(filename.GetBuffer(0),CFile::modeRead|CFile::typeBinary)){AfxMessageBox("打开文件错误,取消发送!");return;}strcpy(fn,filename.GetBuffer(0));}else return; //按了取消按钮FileLength=file.GetLength();sendSocket.Send(&FileLength,sizeof(long));sendSocket.Send(fn,40);memset(data,0,sizeof(data));do{// 从文件读取数据,每次最多读入ReadSize个字节。