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();
//服务器:创建服务器socket
SOCKET create_serverSocket();
//客户端:创建客户端socket
SOCKET 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)