打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

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

2021.08.27

关注

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

记住一定要先运行服务器,在运行客户端。这里是本地回环测试,如果有公网服务器或者局域网测试,请吧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;}
来源:网络

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Windows下C语言的Socket编程例子(TCP和UDP)
3个学习Socket编程的简单例子:TCP Server/Client, Select
socket本地进程通信
深入探索MS SQL Server 2000网络连接的安全问题
inet_pton()和inet_ntop()
简化版udev, 抓取uevent事件
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服