C语言Socket网络文件传输(可循环发送多个文件)

本次文件传输的实现主要是通过客户端向服务器发送下载请求,然后在服务器中找到对应的文件并打开文件,再继续向客户端传送文件,而客户端就在不停的接收。这是因为文件可能比较大,一个缓冲数组只能保存一部分文件内容,因此服务器得不断从文件中读取内容并发给客户端,而客户端得不停的循环接收。

记住一定要先运行服务器,在运行客户端。这里是本地回环测试,如果有公网服务器或者局域网测试,请吧127.0.0.1改成对应的服务器所在的计算机的ip地址。

测试过7G的文件传输,总用时02m03s

  • 文件大小读取35s

  • 文件传输0m27s

文件结构:

  • tcpSocket.h简单封装的Tcp socket接口头文件

  • tcpSocket.c    简单封装的Tcp socket接口源文件

  • server.c          文件传输服务器

  • client.c            文件传输客户端

tcpSocket.h

#ifndef _TCPSOCKET_H_#define _TCPSOCKET_H_#include<stdbool.h>#include<stdio.h>#include<WinSock2.h>//头文件#pragma comment(lib,'ws2_32.lib')//库文件#define err(errMsg) printf('[error] %s failed,code %d \ line:%d\n',errMsg, WSAGetLastError(),__LINE__);#define PORT 8888//0~1024 是系统保留,我们一般不用#define SendSize((size_t)(100 * 1024 * 1024))//一次性发送的数据大小//初始化网络库bool init_Socket();//关闭网络库bool close_Socket();//服务器:创建服务器socketSOCKET create_serverSocket();//客户端:创建客户端socketSOCKET create_clientSocket(const char* ip);#endif // !_TCPSOCKET_H_

tcpSocket.c

#include'tcpSocket.h'bool init_Socket(){  WSADATA wsadata;  if (0 != WSAStartup(MAKEWORD(2, 2), &wsadata))    //wsa  windows socket ansyc  windows异步套接字  {    err('WSAStartup');    return false;  }  return true;}bool close_Socket(){  if (0 != WSACleanup())  {    err('WSACleanup');    return false;  }  return true;}SOCKET create_serverSocket(){  //1,创建一个空的socket  SOCKET fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  if (INVALID_SOCKET == fd)  {    err('socket');    return INVALID_SOCKET;  }  //~0  对于有符号来说是-1  对于无符号来说是最大值  //2,给socket绑定本地ip地址和端口号  struct sockaddr_in addr;  addr.sin_family = AF_INET;  addr.sin_port = htons(PORT);      //把本地字节序转为网络字节序, 大端存储和小端存储  addr.sin_addr.S_un.S_addr = ADDR_ANY;  //绑定本地任意ip  if (SOCKET_ERROR == bind(fd, (struct sockaddr*)&addr, sizeof(addr)))  {    err('bind');    return INVALID_SOCKET;  }  //2,开始监听  listen(fd, 10);  return fd;}SOCKET create_clientSocket(const char* ip){  //1,创建一个空的socket  SOCKET fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  if (INVALID_SOCKET == fd)  {    err('socket');    return INVALID_SOCKET;  }  //~0  对于有符号来说是-1  对于无符号来说是最大值  //2,给socket绑定服务端的ip地址和端口号  //struct sockaddr;  struct sockaddr_in addr;  addr.sin_family = AF_INET;  addr.sin_port = htons(PORT);      //把本地字节序转为网络字节序, 大端存储和小端存储  addr.sin_addr.S_un.S_addr = inet_addr(ip);      //绑定服务器ip  if (INVALID_SOCKET == connect(fd, &addr, sizeof(addr)))  {    err('connet');    return INVALID_SOCKET;  }  return fd;}

server.c

//获取文件大小(Byte)size_t fileSize(const char* fileName);bool readAndSendFile(SOCKET s, const char* fileName);#include'tcpSocket.h'int main(){ init_Socket(); SOCKET serfd = create_serverSocket();printf('server create successed ,wait client connet...\n');//等待客户端连接 SOCKET clifd = accept(serfd, NULL, NULL);if (clifd == INVALID_SOCKET) { err('accept'); }while (1) {//可以和客户端进行通信了printf('等待客户端,请求数据...\n');//接受客户端请求的文件名char clientFileNames[100] = '';int ret = recv(clifd, clientFileNames, 100, 0); if (ret <= 0) { closesocket(clifd);break; } readAndSendFile(clifd, clientFileNames); } closesocket(serfd); close_Socket(); system('pause');return 0;}//获取文件大小(Byte)size_t fileSize(const char* fileName){ FILE* fp = fopen(fileName, 'rb');if (fp == NULL) { perror('file open failed:');return -1; }char* buf = calloc(SendSize, sizeof(char));if (!buf) {printf('内存申请失败\n');return -1; }size_t size = 0;while (!feof(fp)) {int ret = fread(buf, sizeof(char), SendSize, fp); size += ret; } fclose(fp);return size;}bool readAndSendFile(SOCKET s, const char* fileName){//获取文件大小size_t fileSzie = fileSize(fileName);if (fileSzie == (size_t)-1) {printf('get file size failed\n');return false; }//发送文件大小 send(s, &fileSzie, sizeof(size_t), 0); FILE* read = fopen(fileName, 'rb');if (!read) { perror('file open failed');return false; }size_t nblock = fileSzie / SendSize; //计算分成100M的包,可以分多少块 size_t remain = fileSzie - nblock * SendSize; //看有没有剩下的字节printf('nblock:%llu remain:%llu\n', nblock, remain);//分配内存char* fileBuf = calloc(SendSize, sizeof(char));if (!fileBuf) {printf('[error line:%d] memeory alloc failed\n', __LINE__);return false; }for (int i = 0; i < nblock; i++) {//把文件读到内存中来int len = fread(fileBuf, sizeof(char), SendSize, read);//发送int ret = send(s, fileBuf, len, 0);if (ret == SOCKET_ERROR) { err('Send');return false; }printf('第%02d块发送成功,共(%d)Byte\n',i,ret); }//发送多余的数据if (remain != 0) {//把文件读到内存中来 fread(fileBuf, sizeof(char), remain, read);//发送int ret = send(s, fileBuf, remain, 0);if (ret == SOCKET_ERROR) { err('Send');return false; }printf('最后一块发送成功,共(%d)Byte\n', ret); } printf('\nAll file send successfully,totoa %llu Byte\n', nblock * SendSize + remain);free(fileBuf); fclose(read);return true;}

client.c

#include'tcpSocket.h'const char* getFileName(const char* fullPath);bool recvAndSaveFile(SOCKET s, const char* fileName);int main(){  init_Socket();  SOCKET fd = create_clientSocket('127.0.0.1');printf('connet server successed..\n');while (true)  {//向服务器发送需要获取的文件名char filePath[100] = '';printf('请输入要downLoad的文件名:');    gets_s(filePath, 100);    send(fd, filePath, strlen(filePath), 0);const char * saveFileName = getFileName(filePath);    recvAndSaveFile(fd, saveFileName);  }  closesocket(fd);  close_Socket();  system('pause');return 0;}//从完整路径中获取文件名const char* getFileName(const char* fullPath){char* resStr = NULL;if ((resStr = strrchr(fullPath, '/')) == NULL)  {    resStr = strrchr(fullPath, '\\');  }return resStr + 1;}//从服务器接受并保存文件bool recvAndSaveFile(SOCKET s, const char* fileName){//总文件大小size_t fileSize = -1;//接收文件大小  recv(s, &fileSize, sizeof(size_t), 0);printf('recv filesize:%llu\n', fileSize);  FILE* fp = fopen(fileName, 'wb+');if (fp == NULL)  {    perror('file open failed:');return false;  }char* fileBuf = calloc(SendSize, sizeof(char));if (!fileBuf)  {printf('[error line:%d] memeory alloc failed\n', __LINE__);return false;  }size_t recvSize = 0;        //当前接受到的文件总大小while (recvSize < fileSize)  //如果没有接收完整,则继续接收  {int ret = recv(s, fileBuf, SendSize, 0);if (ret > 0)    {//实际接收到多少数据,就写入多少数据      fwrite(fileBuf, sizeof(char), ret, fp);    }else if (ret <= 0)    {printf('服务器下线...\n');break;    }    recvSize += ret;printf('%lfM/%lfM\n', (double)recvSize/1024/1024, (double)fileSize /1024/1024);//printf('当前接受到(%.2lf)MB的数据,剩余(%.2lf)MB未接收\n', (double)ret / 1024 / 1024, (double)(g_fileSzie - recvSize)/1024/1024);//printf('当前接受到(%d)Byte的数据,剩余(%llu)Byte未接收\n', ret,g_fileSzie-recvSize);  }  fclose(fp);printf('总共接受到 %llu Byte的数据\n', recvSize);free(fileBuf);return false;}
来源:网络

(0)

相关推荐