第一部分.. .. .. .. .. .. .. .. . .. . .. .. .. .. .. .项目背景介绍
第二部分.. .. .. .. .. .. .. .. . .. . . .. .. .. .. .. .. .. . 任务概述
第三部分.. .. .. .. .. .. .. .. .. . .. . . .. ... ... .. ... 项目流程
第四部分.. .. .. .. .. .. .. .. . .. . . .. ... .. . .. . 负责部分
第五部分.. .. .. .. .. .. .. .. .. .. .. . . 设计中遇见的问题以及解决方案
第六部分.. .. .. .. .. .. .. .. .. .. 项目完成情况及需进一步完善的地方
第七部分.. .. .. .. .. .. .. .. .. .. .. .. ... .项目调试和结果
第八部分.. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .个人收获和总结
第九部分.. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .参考文献
第十部分. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..附录(实现源代码)
采用C程序设计语言,设计基于WinSock的文件传输系统。网络连接采用C/S架构,首先由服务端启动,在某个端口上侦听,等待客户端的连接; 一旦客户端向服务端发出连接时,服务端负责则发送文件。
二、 任务概述
1. 通过windows网络编程的相关知识,结合网络检索,深刻理解WinSock的原理;理解C/S架构的内涵;
2. 在客户端和服务端都能够正确的创建套接字 SOCKET ,并且能够绑定到某个端口上;同时对Winsock DLL进行初始化,协商Winsock的版本支持并分配必要的资源。
3. 服务端调用Winsock的 accep t方法,实现套接字侦听,等待客户端的连接请求;客户端调用Winsock的connect方法建立连接;先是服务端启动,等待客户端的连接建立;
4. 考虑,若是服务端还没启动,客户端已发出连接建立的要求,这种情况下,客户端能够处理建立不成功的情况,程序中并不是退出,而是等待一段时间,如2s后,在向服务端建立连接;
5. 考虑客户端向服务端建立连接时,或者服务端在处理客户端的连接时,发生错误,要有相应的错误显示信息;
6. 服务端,将文件读入后,首先,向客户端发送文件的基本信息。包含:文件名,文件大小。客户端根据接收到的文件信息和输入的目录建立相应的文件,并在屏幕打印相关文件信息提示;
7. 服务端和客户端都要将发送或接收的文件基本信息打印出来(文件的内容不需要打印,只打印相关接收提示信息);
8. 服务端读取指定文件目录中文件内容,发送;客户端接收到文件的内容后,将之写入建立好的文件中没,并保存到指定的文件目录中(客户端的文件和服务端的文件内容完全相同);
9. 所传输文件大小,若达100M时,仍能够正确传输;
10. 当文件传送和接收完毕,服务端和客户端关闭套接字,释放所占的资源;
三、 项目流程
通讯的事例简易图流程图
从以上简易的结构图,可以看出这个项目的客户端和服务器端分别由几个不同的模块。
服务器端的主要模块有:套接字的定义和初始化 , 地址信息,文件信息(使用文件实现文件信息的获取),文件的分割,
四、 负责部分
1. 主要负责server端的逻辑设计与实现,并且自己设计一个简单的client端(主要用于调试使用),
2. 设计的方法主要采取模块化设计,
---- 首先启动服务器方,以提供相应服务:
---- (1)打开一个通信通道并通知网络:本机将在某一公认的端口上等待客户(Client)请求;
---- (2)服务器进入阻塞等待状态,等待客户请求的到来;
---- (3)当服务器接收到一个客户的连接请求时, 激活一个新的进程用于处理客户请求并建立C/S 对话,服务完成后 ,关闭此新进程与户的通信链路, 并将其终止;
---- (4)返回第(2)步,等待另一客户请求;
---- (5)关闭服务器。
---- 客户端进程:
---- (1)指定想与之建立连接的服务器相应服务的保留端口号;
---- (2)向服务器发送CONNECT 请求并等待服务器的应答;
---- (3)接收到服务器建立连接的响应后接受服务器相应服务;
---- (4)服务请求结束后关闭通信通道并终止。
所涉及的主要问题以及解决方法
文件信息的存储
文件的分割(在server分割)
文件的拼接(在client拼接)
解决方法:
文件信息的存储,采用结构体来存放文件的信息(例如文件名,文件大小等)
文件结构体的定义如下:
struct FILEMSG
{
char filename[50]; /*文件名*/
long int filesize; /*文件大小*/
long int fileblock; /*文件块*/
int blocknum; /*文件块的数量*/
}; //定义结构体类型filemsg表示文件的相关信息
文件的分割和拼接
使用文件信息结构体中的fileblock(文件块)和blocknum(文件块的数量),配合文件读写函数
fread(),fwrite()
服务器设计大体流程
采用的设计语言:C语言
使用vc++6.0编辑源代码以及调试程序
程序大体结构(使用伪代码表示)
#include <stdio.h>
#include <string.h>
#include <winsock2.h>
#pragma comment( lib, "ws2_32.lib" )
struct FILEMSG
{
char filename[50];
long int filesize;
long int fileblock;
int blocknum;
}; //定义结构体类型filemsg表示文件的相关信息
V oid main()
{
W onsocket版本的定义;
定义套接字;
初始化本机地址;
绑定套接字;
监听;
W hile(1)
{
调用accept()接收来自client的请求;
建立连接套接字;
文件操作部分(包括文件信息的获得,以及文件的分块);
使用缓冲区发送分块文件;
}
C losesocket();
}
F ileget()
{
文件信息的获得;
}
五、 设计中遇见问题及解决方案
此项目中所涉及的技术和相关知识概述:
Winsock的相关知识
套接字的含义:
◆ Windows Sockets 相当于进行网络通信两端的插座,只要对方的Socket和自己的Socket有通信联接,双方就可以发送和接收数据了。其定义类似于文件句柄的定义。(win_sockets在网络中所处的位置如下图)
通讯的基石是套接口,一个套接口是通讯的一端。在这一端上你可以找到与其对应的一个名字。一个正在被使用的套接口都有它的类型和与其相关的进程。套接口存在于通讯域中。通讯域是为了处理一般的线程通过套接口通讯而引进的一种抽象概念。套接口通常和同一个域中的套接口交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。 Windows Sockets 规范支持单一的通讯域,即 Internet 域。各种进程使用这个域互相之间用 Internet 协议族来进行通讯( Windows Sockets 1.1 以上的版本支持其他的域,例如 Windows Sockets 2 )。
套接口可以根据通讯性质分类;这种性质对于用户是可见的。应用程序一般仅在同一类的套接口间通讯。不过只要底层的通讯协议允许,不同类型的套接口间也照样可以通讯。
用户目前可以使用两种套接口,即流式套接口和数据报套接口。流式套接口提供了双向的,有序的,无重复并且无记录边界的数据流服务。数据报套接口支持双向的数据流,但并不保证是可靠,有序,无重复的。也就是说,一个从数据报套接口接收信息的进程有可能发现信息重复了,或者和发出时的顺序不同。数据报套接口的一个重要特点是它保留了记录边界。对于这一特点,数据报套接口采用了与现在许多包交换网络(例如以太网)非常类似的模型。
TCP/IP特点
TCP/IP协议的核心部分是传输层协议(TCP、UDP),网络层协议(IP)和物理接口层,这三层通常是在操作系统内核中实现。因此用户一般不涉及。编程时,编程界面有两种形式:一、是由内核心直接提供的系统调用;二、使用以库函数方式提供的各种函数。前者为核内实现,后者为核外实现。用户服务要通过核外的应用程序才能实现,所以要使用套接字(socket)来实现。
客户机 / 服务器模型
目前大部分网络架构都采用C/S架构(客户机 / 服务器模型),采用C/S架构可以在客户端和服务器端分别实现通讯双方的收发,这样在客户端和服务端分别实现不同的功能,就降低了服务器端的系统要求,也提高了系统的整体的速度。
C/S架构的模型如下图所示:
一个在建立分布式应用时最常用的范例便是客户机 / 服务器模型。在这种方案中客户应用程序向服务器程序请求服务。这种方式隐含了在建立客户机 / 服务器间通讯时的非对称性。客户机 / 服务器模型工作时要求有一套为客户机和服务器所共识的惯例来保证服务能够被提供(或被接受)。这一套惯例包含了一套协议。它必须在通讯的两头都被实现。根据不同的实际情况,协议可能是对称的或是非对称的。在对称的协议中,每一方都有可能扮演主从角色;在非对称协议中,一方被不可改变地认为是主机,而另一方则是从机。一个对称协议的例子是 Internet 中用于终端仿真的 TELNET 。而非对称协议的例子是 Internet 中的 FTP 。无论具体的协议是对称的或是非对称的,当服务被提供时必然存在“客户进程”和“服务进程”。
一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被“惊醒”并且为客户提供服务-对客户的请求作出适当的反应。这一请求 / 相应的过程可以简单的用图 2-1 表示。虽然基于连接的服务是设计客户机 / 服务器应用程序时的标准,但有些服务也是可以通过数据报套接口提供的。
所涉及的函数
<1> socket()函数的使用
下面是详细介绍:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
但是它们的参数是什么? 首先,domain 应该设置成 "AF_INET",就 象数据结构struct sockaddr_in 中一样。然后,参数 type 告诉内核 是 SOCK_STREAM 类型还是 SOCK_DGRAM 类型。最后,把 protocol 设置为 "0"。(注意:有很多种 domain、type,我不可能一一列出了,请看 msdn帮助。当然)
socket() 只是返回你以后在系统调用种可能用到的 socket 描述符,或 者在错误的时候返回-1。全局变量 errno 中将储存返回的错误值。
<2> bind()函数的使用
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
sockfd 是调用 socket 返回的文件描述符。my_addr 是指向数据结构 struct sockaddr 的指针,它保存你的地址(即端口和 IP 地址) 信息。 addrlen 设置为 sizeof(struct sockaddr)。
<3> connect() 函数的使用
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
sockfd 是系统调用 socket() 返回的套接字文件描述符。serv_addr 是 保存着目的地端口和 IP 地址的数据结构 struct sockaddr。addrlen 设置 为 sizeof(struct sockaddr)。
<4> listen()函数的使用
int listen(int sockfd, int backlog);
sockfd 是调用 socket() 返回的套接字文件描述符。backlog 是在进入 队列中允许的连接数目。什么意思呢? 进入的连接是在队列中一直等待直 到你接受 (accept() 请看下面的文章)连接。它们的数目限制于队列的允许。 大多数系统的允许数目是20,你也可以设置为5到10,
和别的函数一样,在发生错误的时候返回-1,并设置全局错误变量 errno。
<5> accept()函数的使用
int accept(int sockfd, void *addr, int *addrlen);
sockfd 相当简单,是和 listen() 中一样的套接字描述符。addr 是个指 向局部的数据结构 sockaddr_in 的指针。这是要求接入的信息所要去的地 方(你可以测定那个地址在那个端口呼叫你)。在它的地址传递给 accept 之 前,addrlen 是个局部的整形变量,设置为 sizeof(struct sockaddr_in)。 accept 将不会将多余的字节给 addr。如果你放入的少些,那么它会通过改
变 addrlen 的值反映出来。
<6> send() and recv()函数
int send(int sockfd, const void *msg, int len, int flags);
sockfd 是你想发送数据的套接字描述符(或者是调用 socket() 或者是 accept() 返回的。)msg 是指向你想发送的数据的指针。len 是数据的长度。 把 flags 设置为 0 就可以了
int recv(int sockfd, void *buf, int len, unsigned int flags);
sockfd 是要读的套接字描述符。buf 是要读的信息的缓冲。len 是缓 冲的最大长度。flags 可以设置为0。(请参考recv() 的 man page。) recv() 返回实际读入缓冲的数据的字节数。或者在错误的时候返回-1, 同时设置 errno。
<7> sprintf()的使用方法
在将各种类型的数据构造成字符串时,sprintf 的强大功能很少会让你失望。由于sprintf 跟printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。这也导致sprintf 比printf 有用得多。
sprintf 是个变参函数,定义如下:
int sprintf( char *buffer, const char *format [, argument] ... );
除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:
格式化字符串上。
printf 和sprintf 都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要的字符串。
格式化数字字符串
sprintf 最常见的应用之一莫过于把整数打印到字符串中,所以,spritnf 在大多数场合可以替代
itoa。
<8> inet_ntoa()的使用方法
简述:
将网络地址转换成“.”点隔的字符串格式。
#include <winsock.h>
char FAR* PASCAL FAR inet_ntoa( struct in_addr in);
in:一个表示Internet主机地址的结构。
注释:
本函数将一个用in参数所表示的Internet地址结构转换成以“.” 间隔的诸如“a.b.c.d”的字符串形式。请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。应用程序不应假设该内存是如何分配的。在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。
返回值:
若无错误发生,inet_ntoa()返回一个字符指针。否则的话,返回NVLL。其中的数据应在下一个WINDOWS套接口调用前复制出来。
<9> inet_addr()的使用方法
#include <winsock.h>
unsigned long PASCAL FAR inet_addr( const struct FAR* cp);
本函数解释cp参数中的字符串,这个字符串用Internet的“.”间隔格式表示一个数字的Internet地址。返回值可用作Internet地址。
<10> htons()的使用方法
简述:
将主机的无符号短整形数转换成网络字节顺序。
#include <winsock.h>
u_short PASCAL FAR htons( u_short hostshort);
hostshort:主机字节顺序表达的16位数。
注释:
本函数将一个16位数从主机字节顺序转换成网络字节顺序。
<11> 操作文件的相关函数
fread(),fwrite(),fopen(),fclose(),fseek(),ftell();
操作文件的相关函数的相关函数参见谭浩强版的C程序设计
六、 项目完成情况及需进一步完善的地方
基本完成此项目,需要完善一下界面,以及添加一些聊天功能
七、 项目调试和结果
当客户端和服务端启动的时候如下图,此时server端在监听来自client的请求,如下图所示:
当client和server端都打开的时候,此时先在 客户端 输入要连接的服务器的IP地址,等待服务端的响应,当服务端接收到客户端的请求的是,做出反应,发给客户端一个信息,提示客户端已经成功地连接上服务器,提示客户端确认输入,并输入接收的文件所要保存的路径(这里以d:/123文件夹为保存路径),输入完毕后,再次等待响应。服务端再次接收到来自客户端的请求的时候,服务器是提示用户输入要发送给客户端的文件所在的路径,输入完成后,再输入在此路径下所要发送的文件名。
当完成了客户端和服务器端的文件传输的准备工作后,即进入文件的传输,如下图所示:
以上完成以后,即可在d:/123文件夹下,看见传入的文件(这里是1234.jpg)
八、 个人收获和总结
通过此次C项目的实践,了解了在网络环境下,使用windows进行网络编程的基本步骤。对网络的
TCP/IP协议的架构体系有了更深的了解。
C/S架构体系的了解,客户端和服务器端分别编写,把一部分原先由服务器完成的工作现在分流到客户端计算机来做,这样可以减小对服务器端的计算性能的要求,也提高了系统得整体的性能。
套接字的创建,是两台计算机通讯建立的通道。
通过亲手实践实现两台计算机之间的通讯,以此为基础实现了计算机之间的文件传输。
九、 参考文献
windows 网络编程 清华大学出版社
C程序设计 谭浩强版
十、附 :代码
客户端和服务器端的完整实现代码如下:
客户端源代码
#include <stdio.h>
#include <string.h>
#include <winsock2.h>
#pragma comment( lib, "ws2_32.lib" )
struct FILEMSG
{
char filename[50];
long int filesize;
long int fileblock;
int blocknum;
}; //定义结构体类型filemsg表示文件的相关信息
void main()
{
WORD wVersionRequested; //定义版本
WSADATA wsadata;
int err; //存储启动函数的返回值的变量
wVersionRequested=MAKEWORD(1,1); //定义版本
err=WSAStartup(wVersionRequested,&wsadata);
if(err!=0) return; //检查版本
//错误处理
if(HIBYTE(wsadata.wVersion)!=1 || LOBYTE(wsadata.wVersion)!=1)
{
WSACleanup();
return;
}
//定义套接字
SOCKET mirage_1; //定义套接字
mirage_1=socket(AF_INET,SOCK_STREAM,0);
if(mirage_1==INVALID_SOCKET)
{
printf("socket error !/n");
exit(1);
}
//定义套接字地址
SOCKADDR_IN add_1;
add_1.sin_family=AF_INET;
add_1.sin_port=htons(6000);
char ip[50];
system("@color 0D");
Sleep(100);
system("@color 03");
Sleep(100);
system("@color 0D");
printf(" *******************************************************************/n");
printf(" * */n");
printf(" * */n");
printf(" * 本程序主要功能为C/S模式下使用TCP/IP协议, */n");
printf(" * 完成两台计算机之间传输文件 */n");
printf(" * */n");
printf(" * 此程序分为客户端和服务器端 */n");
printf(" * 客户端完成下载请求,服务端完成发送传输 */n");
printf(" * Hefei Teachers College */n");
printf(" *******************************************************************/n/n/n");
printf("请输入服务端ip地址: ....../n");
scanf("%s",ip);
add_1.sin_addr.S_un.S_addr=inet_addr(ip); //inet_addr() 是将字符处转换成网络传输类型
// 和服务端连接
connect(mirage_1,(SOCKADDR *)&add_1,sizeof(SOCKADDR));
//接受提示信息
char ipstring[80];
int len_ip=0;
len_ip=recv(mirage_1,ipstring,sizeof(ipstring),0);
printf("%s/n",ipstring);
char msg_1[50];
recv(mirage_1,msg_1,strlen(msg_1)+1,0);
Sleep((DWORD)100);
printf("已经成功接入服务器:/n");
printf("%s/n",msg_1);
//定义确认元素
char judge;
getchar();
printf("下载y或者Y,/n");
judge=getchar();
send(mirage_1,&judge,sizeof(char),0);
while(1)
{
if(judge=='Y'||judge=='y')break;
else{
printf("下载y或者Y,/n");
getchar();
judge=getchar();
send(mirage_1,&judge,sizeof(char),0);
}
}
//-----定义路径
char path[50];
printf("请输入存储文件夹的路径 ,例如输入d://123///n");
scanf("%s",path);
/*****************************/
//*******************************************************************
// struct FILEMSG *p,filemsg; //定义接受存储文件信息的结构体指针
// p=&filemsg;
//*********************************************************************
//接受文件信息
FILE *fp;
struct FILEMSG p;
int len_4;
len_4=recv(mirage_1,(char *)&p,sizeof(struct FILEMSG),0);
if(len_4==SOCKET_ERROR)
{
printf("服务器出错:/n");
Sleep((DWORD)100);
exit(0);
}
Sleep((DWORD)100);
printf("文件:%s ,文件大小 :%2d,文件传输块大小:%2d,文件块数量;%d/n",p.filename,p.filesize,p.fileblock,p.blocknum);
int data=p.blocknum;
//将路径与文件名,连接起来
strcat(path,p.filename);
strcpy(p.filename,path);
//strcat(p.filename,"//");
//建立文件
if((fp=fopen(p.filename,"ab+"))==NULL)
{
printf("ERROR !/n");
exit(0);
}
//文件接受
char *buf;
// int i=0;
int j=1;
if(p.blocknum==1) //小文件接收
{
int len_1=0;
printf("小文件接收/n");
buf=(char *)malloc( sizeof(char)*p.filesize);
len_1= recv(mirage_1,(char *)buf,p.filesize,0);
fwrite(buf,len_1,1,fp);
Sleep((DWORD)100);
free(buf);
exit(0);
}
while(p.blocknum>0)
{
if(p.blocknum>1)
{
buf=(char * )malloc(sizeof(char)*p.fileblock);
int len=0;
len=recv(mirage_1,(char*)buf,p.fileblock,0);
fwrite(buf,len,1,fp);
printf("接受到%2d快/n",j++);
p.blocknum--;
free(buf);
Sleep((DWORD)100);
}
//最后一块的处理或者小于客户端文件缓冲区的文件的接受
else
{
int len_2=0; //用于接受最后一块的大小
char *buf_1;
buf_1=(char *)malloc( sizeof(char)*(p.filesize - p.fileblock*(p.blocknum-1)));
len_2=recv(mirage_1,(char*)buf_1,p.filesize - p.fileblock*(data-1),0);
Sleep((DWORD)100);
fwrite(buf_1,len_2,1,fp);
printf("接受到最后一快---接受到%2d快/n",j);
fclose(fp);
exit(0);
}
}
}
服务端源代码
#include <stdio.h>
#include <winsock2.h>
#include <time.h>
#include <process.h>
#pragma comment( lib, "ws2_32.lib" )
struct FILEMSG
{
char filename[50];
long int filesize;
long int fileblock;
int blocknum;
}; //定义结构体类型filemsg表示文件的相关信息
//-----------------------------------函数定义部分-----------------
void fileget(char name[] , struct FILEMSG *p, FILE *fp) ; // 定义文件信息获取函数
//----------------------------------------------------------------
void main()
{
//-----------------------------------------------------
WORD wVersionRequested; //定义版本
WSADATA wsadata;
int err; //存储启动函数的返回值的变量
wVersionRequested=MAKEWORD(1,1); //定义版本
err=WSAStartup(wVersionRequested,&wsadata);
if(err!=0) return; //检查版本
//错误处理
if(HIBYTE(wsadata.wVersion)!=1 || LOBYTE(wsadata.wVersion)!=1)
{
WSACleanup();
return;
}
system("@color 0D");
Sleep(100);
system("@color 03");
Sleep(100);
system("@color 0D");
//---------------------------------------------------
printf(" *******************************************************************/n");
printf(" * */n");
printf(" * */n");
printf(" * 本程序主要功能为C/S模式下使用TCP/IP协议, */n");
printf(" * 完成两台计算机之间传输文件 */n");
printf(" * */n");
printf(" * 此程序分为客户端和服务器端 */n");
printf(" * 客户端完成下载请求,服务端完成发送传输 */n");
printf(" * Hefei Teachers College */n");
printf(" *******************************************************************/n/n/n");
//定义套接字
SOCKET mirage_1;
mirage_1=socket(AF_INET,SOCK_STREAM,0);
//定义套接字地址并初始化
SOCKADDR_IN add_1; //定义地址变量
//以下为初始化地址变量
add_1.sin_family=AF_INET; //设置网络类型 ,AF_INET 为windows类型
add_1.sin_port=htons(6000); //设置端口
add_1.sin_addr.S_un.S_addr=INADDR_ANY; //在网络上设置ip地址号,INADDR_ANY为在网络上使用本机ip地址
//--------------------------------------------------------------------------------------------------
//以下为绑定套接字
int bind_err; //定义绑定错误返回bianliang
bind_err=bind(mirage_1,(SOCKADDR *)&add_1,sizeof(SOCKADDR));
if(bind_err==SOCKET_ERROR)
{
printf("the error of bind()/n");
}
// 以下为监听
printf("server start ........./n");
printf("--------------------------/n");
if( (listen(mirage_1,5))>SOCKET_ERROR)
{
printf("正在监听......./n");
}
//----------------------------------------------------------------------------
SOCKADDR_IN add_2; //用来存储监听的信息的地址信息
int len;
len=sizeof(SOCKADDR_IN) ;
while(1)
{
SOCKET mirage_2; //定义接受套接字
mirage_2=accept(mirage_1,(SOCKADDR *)&add_2,&len);
if(mirage_2==INVALID_SOCKET)
{
printf(" 套接字 mirage_2 创建发生错误 :/n ");
exit(0);
}
printf("客户端已经连接上:");
//------------------文件操作开始------------
FILE *fp; //定义文件指针
//**************************************************************************
//struct FILEMSG *filemsg,p;
struct FILEMSG filemsg; //定义文件信息的结构体变量指针filemsg
//********************传送提示信息到客户端**********************************
char ipstring[80];
sprintf(ipstring," ----------------欢迎 %s 访问本服务器,祝你愉快-----------/n",inet_ntoa(add_2.sin_addr));
send(mirage_2,ipstring,strlen(ipstring)+1,0);
send(mirage_2,"客户端是否已经准备接受文件信息:/n",strlen("客户端是否已经准备接受文件信息:/n")+1,0);
Sleep((DWORD)100);
//定义来自client的是否已经做好准备接收
//char judge[2];
char judge;
recv(mirage_2,&judge,sizeof(char),0);
while(1)
{
if(judge=='Y'||judge=='y')break;
//if(strcmp(judge,"Y")==0 || strcmp(judge,"y")==0) break;
else recv(mirage_2,&judge,sizeof(char),0);
}
char path[50];
printf("请输入文件所在的路径/n(例如,如果想发送的文件在d://123文件夹下,则输入d://123//:)");
scanf("%s",path);
char name[50]; //定义文件的名称,用于用户的输入
printf("请输入该文件下的想要发送的文件名: /n");
scanf("%s",name);
//********************************************************
// filemsg=&p;
//************************************************************
strcat(path,name);
//strcpy(name,path);
//-------------------文件的打开并获得文件的信息----------------------
if(((fp=fopen(path,"rb"))==NULL))
{
printf("文件创建失败:!/n");
exit(0);
}
//*************************************************************
// filemsg = (struct FILEMSG*)malloc(sizeof(struct FILEMSG));
//*************************************************************
fileget(name,&filemsg,fp); //获的读入的文件的信息
rewind(fp); //文件指针移动到文件首部
//---------------------------------------------------------------------
//----------------传送文件信息到client---------------------------------
send(mirage_2, (char*)&filemsg,sizeof(struct FILEMSG),0);
Sleep((DWORD)100);
//-------------------------传送文件块-----------------------------------
char *buffer; //定义文件块缓冲区
if(filemsg.fileblock > filemsg.filesize) //如果文件小于每一次传送文件大小则直接传送
{
buffer =(char * )malloc( sizeof(char)*filemsg.filesize); //按文件实际大小建立缓冲区
fread(buffer,filemsg.filesize,1,fp);
send(mirage_2,buffer,filemsg.filesize,0);
Sleep((DWORD)100);
printf("小文件传送:/n");
free(buffer);
exit(0);
}
else //大于缓冲区的传送
{
int i;
int j=1;
for(i=0;i<filemsg.blocknum-1;i++)
{
buffer =(char * )malloc( sizeof(char)*filemsg.fileblock);//按文件块大小建立缓冲区
fread(buffer,filemsg.fileblock,1,fp);
send(mirage_2,buffer,filemsg.fileblock,0);
Sleep((DWORD)100);
printf("传送%d个文件:/n",j++);
free(buffer);
}
//最后一次传送
// Sleep((DWORD)100);
buffer=(char *)malloc( sizeof(char)*(filemsg.filesize - filemsg.fileblock*(filemsg.blocknum-1) ) );
fread(buffer,filemsg.filesize - filemsg.fileblock*(filemsg.blocknum-1),1,fp);
Sleep((DWORD)100);
send(mirage_2,buffer,filemsg.filesize - filemsg.fileblock*(filemsg.blocknum-1),0);
Sleep((DWORD)100);
printf("传送到最后一快 ----传送%d个文件/n",j);
return;
}
fclose(fp);
closesocket(mirage_2);
}
}
void fileget(char name[] , struct FILEMSG *p, FILE *fp) //fileget()函数的功能实现文件基本信息的获取
{
//---------------文件的信息的获得------------------------------
strcpy(p->filename,name); //对信息结构体中文件名赋值
fseek(fp,0,SEEK_END);
p->filesize=ftell(fp); //求出文件大小
p->fileblock=10000;
p->blocknum=p->filesize/p->fileblock+1;
}