做百度推广送的网站,网站栏目策划方案,微餐饮网站建设,垡头做网站的公司程序要实现的功能#xff1a;client 从 server 下载一个文件并保存到本地。
编写这个程序需要注意两个问题#xff1a;
#xff08;1#xff09;文件大小不确定
有可能比缓冲区大很多#xff0c;调用一次 write()/send() 函数不能完成文件内容的发送。接收数据时也会遇…程序要实现的功能client 从 server 下载一个文件并保存到本地。
编写这个程序需要注意两个问题
1文件大小不确定
有可能比缓冲区大很多调用一次 write()/send() 函数不能完成文件内容的发送。接收数据时也会遇到同样的情况。
要解决这个问题可以使用 while 循环例如
//Server 代码
int nCount;
while( (nCount fread(buffer, 1, BUF_SIZE, fp)) 0 ){
send(sock, buffer, nCount, 0);
}
//Client 代码
int nCount;
while( (nCount recv(clntSock, buffer, BUF_SIZE, 0)) 0 ){
fwrite(buffer, nCount, 1, fp);
}
对于 Server 端的代码当读取到文件末尾fread() 会返回 0从而结束循环。
但对于 Client 端代码有一个关键的问题读取完缓冲区中的数据后 recv() 并不会返回 0而是被阻塞直到缓冲区中再次有数据。也就是说客户端的recv函数因为阻塞而没有返回值卡在循环判断语句处。在文件传输完成后如何让 recv() 返回 0结束 while 循环呢
2Client 端何时结束 while 循环
Client 端 的 recv() 函数返回 0 的唯一时机就是收到 Server 端 发给它 FIN 包的时候。 FIN 包表示数据传输完毕Client 端收到 FIN 包后就知道 Server 端不会再向自己传输数据当调用 read()/recv() 函数时如果缓冲区中没有数据就会返回 0表示读到了”socket文件的末尾“。
那么如何让 Server 端发出FIN包呢这里我们调用 shutdown() 来发送FIN包。
server 端直接调用 close()/closesocket() 会使输出缓冲区中的数据失效文件内容很有可能没有传输完毕连接就断开了而调用 shutdown() 会等待输出缓冲区中的数据传输完毕。
本节以Windows为例演示文件传输功能Linux与此类似不再赘述。请看下面完整的代码。 服务器端 server.cpp
#include stdio.h
#include stdlib.h
#include winsock2.h
#pragma comment (lib, ws2_32.lib) //加载 ws2_32.dll#define BUF_SIZE 1024int main(){
//先检查文件是否存在
char *filename D:\\send.avi; //文件名
FILE *fp fopen(filename, rb); //以二进制方式打开文件
if(fp NULL){
printf(Cannot open file, press any key to exit!\n);
system(pause);
exit(0);
}WSADATA wsaData;
WSAStartup( MAKEWORD(2, 2), wsaData);
SOCKET servSock socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in sockAddr;
memset(sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family PF_INET;
sockAddr.sin_addr.s_addr inet_addr(127.0.0.1);
sockAddr.sin_port htons(1234);
bind(servSock, (SOCKADDR*)sockAddr, sizeof(SOCKADDR));
listen(servSock, 20);SOCKADDR clntAddr;
int nSize sizeof(SOCKADDR);
SOCKET clntSock accept(servSock, (SOCKADDR*)clntAddr, nSize);//循环发送数据直到文件结尾
char buffer[BUF_SIZE] {0}; //缓冲区
int nCount;
while( (nCount fread(buffer, 1, BUF_SIZE, fp)) 0 ){
send(clntSock, buffer, nCount, 0);
}shutdown(clntSock, SD_SEND); //文件读取完毕断开输出流向客户端发送FIN包
recv(clntSock, buffer, BUF_SIZE, 0); //阻塞等待客户端接收完毕fclose(fp);
closesocket(clntSock);
closesocket(servSock);
WSACleanup();system(pause);
return 0;
}
客户端代码
#include stdio.h
#include stdlib.h
#include WinSock2.h
#pragma comment(lib, ws2_32.lib)#define BUF_SIZE 1024int main(){
//先输入文件名看文件是否能创建成功
char filename[100] {0}; //文件名
printf(Input filename to save: );
gets(filename);
FILE *fp fopen(filename, wb); //以二进制方式打开创建文件
if(fp NULL){
printf(Cannot open file, press any key to exit!\n);
system(pause);
exit(0);
}WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), wsaData);
SOCKET sock socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);struct sockaddr_in sockAddr;
memset(sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family PF_INET;
sockAddr.sin_addr.s_addr inet_addr(127.0.0.1);
sockAddr.sin_port htons(1234);
connect(sock, (SOCKADDR*)sockAddr, sizeof(SOCKADDR));//循环接收数据直到文件传输完毕
char buffer[BUF_SIZE] {0}; //文件缓冲区
int nCount;
while( (nCount recv(sock, buffer, BUF_SIZE, 0)) 0 ){
fwrite(buffer, nCount, 1, fp);
}
puts(File transfer success!);//文件接收完毕后直接关闭套接字无需调用shutdown()
fclose(fp);
closesocket(sock);
WSACleanup();
system(pause);
return 0;
}
在D盘中准备好send.avi文件先运行 server再运行 client Input filename to save: D:\\recv.avi↙ //稍等片刻后 File transfer success! 打开D盘就可以看到 recv.avi大小和 send.avi 相同可以正常播放。 注意 server.cpp 第42行代码recv() 并没有接收到 client 端的数据当 client 端调用 closesocket() 后server 端会收到FIN包recv() 就会返回后面的代码继续执行。