la programmation socket vingt-deux: capacités de transfert de fichiers de programmation socket

Cette section nous terminerons  socket  programme de transfert de fichiers, ce qui est un exemple très pratique. Pour obtenir la fonction: client de télécharger un fichier à partir du serveur et l' enregistrer localement.

Notez ce programme nécessite l' attention sur deux questions:
1) la taille du fichier d'incertitude, il peut y avoir beaucoup plus grande que la mémoire tampon, appelé une fois la fonction d'écriture () / send () pour envoyer le contenu du fichier ne peuvent pas être terminés. Vous rencontrerez la même situation lors de la réception des données.

Pour résoudre ce problème, vous pouvez utiliser une boucle while, par exemple:


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

Pour coder côté serveur, lors de la fin du fichier est lu, retourne fread () 0, les extrémités de la boucle.

Pour le code côté client, il y a un problème critique est le transfert de fichiers est terminé let recv () retourne 0, la fin de la boucle while.

Note: Les données ont été lues ne retourne pas 0 tampon recv (), mais il a été bloqué jusqu'à ce que le tampon a données à nouveau.

2) terminal client détermine la façon dont le document reçu est complet, à savoir les problèmes mentionnés ci - dessus - lorsque la fin de la boucle while.

La meilleure façon de mettre fin au cycle alors que, bien sûr, est de rendre le fichier a été reçu fonction retourne recv () 0, puis, comment retourne recv () 0 il? recv () retourne 0 fois seulement lorsque le paquet FIN est reçu.

FIN transfert de données par paquets est terminé, après que l'ordinateur reçoit un paquet FIN connaître ne sera plus propriétaire de la transmission de données, lors de l' appel lu () / fonction recv (), s'il n'y a pas de données dans la mémoire tampon, il retournera 0, ce qui indique une lecture à la « fin de fichier socket ».

Ici , nous appelons l'arrêt () pour envoyer des paquets FIN: fin du serveur près d'appel direct () / closesocket () échouera mémoire tampon de données de sortie, le contenu du transfert de fichiers est terminé est probablement pas de connexion est déconnecté, et l'appel à l' arrêt ( ) attend la transmission de la mémoire tampon de données de sortie est terminée.

Cette section fournit un exemple pour montrer les capacités de transfert de fichiers Windows, Linux est similaire, ne pas les répéter. Voir le code complet ci - dessous.

server.cpp côté serveur:


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


Code client:


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

Dans le lecteur D est prêt fichier send.avi, d' abord exécuter le serveur, puis exécutez le client:
le nom du fichier d'entrée à la Save: D: \\ recv.avi↙
après // Attendez un moment
! Succès de transfert de fichiers

pour ouvrir le lecteur D, vous pouvez voir recv .avi, la même taille et send.avi, peuvent être lus normalement.

Note ligne server.cpp 42, recv () ne reçoit pas de données sur le côté client, lorsque la fin de serveur d' appel côté client closesocket () reçoit un paquet FIN, le rendement recv (), le code ci continue à exécuter.

Publié 33 articles originaux · a gagné les éloges 30 · vues 20000 +

Je suppose que tu aimes

Origine blog.csdn.net/baidu_15547923/article/details/90230405
conseillé
Classement