socket programming twenty-two: socket programming file transfer capabilities

This section we will complete  socket  file transfer program, which is a very practical example. To achieve the function as: client to download a file from the server and save it locally.

Write this program requires attention to two issues:
1) the file size of uncertainty, there may be much larger than the buffer, called once write () / send () function to send the contents of the file can not be completed. You will encounter the same situation when receiving data.

To solve this problem, you can use a while loop, for example:


//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);
}

For coding Server side, when the end of the file is read, fread () returns 0, the loop ends.

For Client-side code, there is a critical issue is the file transfer is complete let recv () returns 0, the end of the while loop.

Note: The data has been read recv buffer () does not return 0, but was blocked until the buffer has data again.

2) Client terminal determines how the received document is complete, i.e. the problems mentioned above - when the end of the while loop.

The easiest way to end the cycle while, of course, is to make the file been received recv () function returns 0, then, how to recv () returns 0 it? recv () returns 0 only time when the FIN packet is received.

FIN packet data transfer is completed, after the computer receives a FIN packet know each other will no longer own the transmission of data, when you call read () / recv () function, if there is no data in the buffer, it will return 0, indicating a read to the "end of the socket file."

Here we call the shutdown () to send FIN packets: server end direct call close () / closesocket () will fail output data buffer, the contents of the file transfer is completed is likely no connection is disconnected, and the call to shutdown ( ) waits for transmission of the output data buffer is completed.

This section provides an example to show the Windows file transfer capabilities, Linux is similar, not repeat them. See complete code below.

Server-side server.cpp:


#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll

#define BUF_SIZE 1024

int 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);

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;
}


Client code:


#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

#define BUF_SIZE 1024

int 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);

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;
}

In the D drive is ready send.avi file, first run the server, and then run the Client:
the Input filename to the Save: D: \\ recv.avi↙
after // Wait a moment
! File transfer success

to open the D drive, you can see recv .avi, the same size and send.avi, can be played normally.

Note server.cpp line 42, the recv () does not receive data on the client side, when the client-side call closesocket (), server end receives a FIN packet, the recv () returns, the latter code continues to execute.

Published 33 original articles · won praise 30 · views 20000 +

Guess you like

Origin blog.csdn.net/baidu_15547923/article/details/90230405