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,创建一个空的socketSOCKET 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; //绑定本地任意ipif (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,创建一个空的socketSOCKET 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); //绑定服务器ipif (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)
