TCP实现文件传输

  一直想着给之前的CloudDisk项目加上一个C/S架构的文件传输模块,因为之前是nginx+fastcgi架构的B/S架构,自己又不会前段代码,没有办法继续增加新的功能块。最近终于抽出时间开始写项目了,已经选用TCP完成linux下的CS架构文件上传功能模块,这里展示TCP文件传输模块。

一、Socket

  Socket类里仅仅封装了TCP连接所需的API和一些必要变量。

//Socket.h
#include<sys/socket.h>
#include<sys/types.h>
#include<cstring>
#include<netinet/in.h>
#include<memory.h> 
#include<cstdlib> 
#include<iostream>
#include<fcntl.h>
#include<sys/stat.h>
#include<unistd.h>
#include<arpa/inet.h> 
#include"file.h"
#include"logger.h"

#define BUFFER_SIZE 1024
#define PORT 8888
#define MAX_CONNECT 20

using namespace std;

class Socket {
public:
	
	Socket();
	~Socket() {}

	bool SockInitServer();
	bool SockInitClient();
	bool SockBind();
	bool SockListen();
	int SockConnect();
	int SockAccpet();
	int SockRecv(int recvfd, char buf[], int len);
	int SockSend(int sendfd, const char buf[], int len);
	bool SockClose();

	int m_sockfd;
	int recvLen;
	int sendLen;
	struct sockaddr_in m_sockaddr;
};

//Socket.cpp
#include"Socket.h"

Socket::Socket() {
	if((m_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		LOG("socket build failed.");
	} else {
		LOG("socket build success.");
	}
}

bool Socket::SockInitServer() {
	memset(&m_sockaddr, 0, sizeof(m_sockaddr));
	m_sockaddr.sin_family = AF_INET;
	m_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	m_sockaddr.sin_port = htons(PORT);
	LOG("init success.");

	return true;
}

bool Socket::SockInitClient() {
	memset(&m_sockaddr, 0, sizeof(m_sockaddr));
	m_sockaddr.sin_family = AF_INET;
	m_sockaddr.sin_port = htons(PORT);
	m_sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	return true;
}

bool Socket::SockBind() {
	if(bind(m_sockfd, (struct sockaddr*)&m_sockaddr, sizeof(m_sockaddr)) == -1) {
		LOG("bind error.");
		return false;
	} else {
		LOG("bind success.");
	}

	return true;
}

bool Socket::SockListen() {
	if(listen(m_sockfd, MAX_CONNECT) == -1) {
		LOG("listen error.");
		return false;
	} else {
		LOG("listen success.");
	}

	return true;
}

int Socket::SockConnect() {
	int sendfd;
	if((sendfd = connect(m_sockfd, (struct sockaddr*)&m_sockaddr, sizeof(m_sockaddr))) == -1) {
		LOG("connect failed.");
	} else {
		LOG("connect success.");
	}


	return sendfd;
}

int Socket::SockAccpet() {
	int recvfd;
	unsigned int addrLen = sizeof(m_sockaddr);
	if((recvfd = accept(m_sockfd, (struct sockaddr*)&m_sockaddr, &addrLen)) == -1) {
		LOG("accept fialed.");
	} else {
		LOG("accept success.");
	}

	return recvfd;
}

int Socket::SockRecv(int recvfd, char buf[], int len) {
	recvLen = recv(recvfd, buf, len, 0);
	//cout << buf << endl;
	if(recvLen < 0) {
		LOG("recv error.");
		return -1;
	} else {
		LOG("recv success.");
	}
	
	return recvLen;
}

int Socket::SockSend(int sendfd, const char buf[], int len) {
	sendLen = send(sendfd, buf, len, 0);
	//cout << buf << endl;
	if(sendLen < 0) {
		LOG("send failed.");
		return -1;
	} else {
		LOG("send success.");
	}

	return sendLen;
}


bool Socket::SockClose() {
	close(m_sockfd);
	LOG("close success.");
	
	return true;
}

二、Server

  Server类里面完成了对文件数据的接收解析,拿到文件的名字并创建文件,然后将其传给fdfs模块。这个类后续还会做很多优化,以及加上文件操作的其他功能模块。

//Server.h
#include"Socket.h"
#include"uploadfile.h"

class Server {
public:

	Socket socket;
	File file;
	Upload upload;

	Server() {}
	~Server() {}

	bool ServerInit();
	bool ServerAccept();
	bool GetFileName();
	bool WriteFile(int recvfd, int fd);
};

//Server.cpp
#include"Server.h"

bool Server::ServerInit() {
	socket.SockInitServer();
	socket.SockBind();
	socket.SockListen();
	file.fileData = (char*)malloc(BUFFER_SIZE);
	memset(file.fileData, '\0', sizeof(file.fileData));

	return true;
}
	
bool Server::ServerAccept() {
	int recvfd = socket.SockAccpet();
	cout << socket.SockRecv(recvfd, file.filePath, 100) << endl;
	cout << "filepath is: " << file.filePath << endl;
	if(recvfd < 0) {
		cout << "recv error." << endl;
		return false;
	} else {
		GetFileName();
	}
	cout  << "filename: " << file.fileName << endl;
	
	int fd = open(file.fileName, O_CREAT | O_RDWR, 0664);
	if(fd < 0) {
		cout << "open file failed." << endl;
		return false;
	} else {
		WriteFile(recvfd, fd);
	}

	return true;
}

bool Server::GetFileName() {
	int i = 0, k = 0;
	for(i = strlen(file.filePath); i >= 0; --i) {
		if(file.filePath[i] != '/') {
			++k;
		} else {
			break;
		}
	}
	strcpy(file.fileName, file.filePath + (strlen(file.filePath) - k) + 1);

	return true;
}

bool Server::WriteFile(int recvfd, int fd) {
	int times = 1;
	while(socket.recvLen = socket.SockRecv(recvfd, file.fileData, BUFFER_SIZE)) {
		cout << "times: " << times << endl;
		cout << socket.recvLen << endl;
		++times;
		if(socket.recvLen < 0) {
			cout << "recv2 error." << endl;
			break;
		}
		write(fd, file.fileData, socket.recvLen);
		if(socket.recvLen < BUFFER_SIZE) {
			cout << "write finished." << endl;
			break;
		} else {
			cout << "write success." << endl;
		}
		cout << "continue..." << endl;
		memset(file.fileData, 0, sizeof(file.fileData));
	}
	cout << "recv finished." << endl;
	close(fd);

	return true;
}

int main() {
	Server server;
	if (!server.upload.mysql.MysqlInit()) {
		cout << "Init failed!" << endl;
		return -1;
	}
	
	if(server.ServerInit()) {
		cout << "server init success." << endl;
	} else {
		cout << "server init failed." << endl;
		return -1;
	}

	while(true) {
		if(server.ServerAccept()) {
			server.upload.UploadFile(server.file.fileName);
			server.upload.SaveToMysql();
			continue;
		} else {
			break;
		}
	}

	server.socket.SockClose();

	return 0;
}

三、Client

  Client主要用来传输文件,后面将作为界面程序的接口,会增加其他的功能模块。

//Client.h
#include"Socket.h"

class Client {
public:

	File file;
	Socket socket;
	int sendLen;
	int fd;

	Client() {}
	~Client() {}

	bool ClientInit();
	bool ClientSend();
	bool SendFilePath();
	bool SendFileData();
};

//Client.cpp
#include"Client.h"

bool Client::ClientInit() {
	socket.SockInitClient();
	socket.SockConnect();
	file.fileData = (char*)malloc(BUFFER_SIZE);
	memset(file.fileData, '\0', sizeof(file.fileData));
	
	return true;
}

bool Client::ClientSend() { 		
	if(!SendFilePath()) {
		cout << "sendPath failed." << endl;
		return false;
	}
	
	SendFileData();
	
	return true;
}

bool Client::SendFilePath() {
	cout << "please input file path: " << endl;
	cin >> file.filePath;
 
	fd = open(file.filePath, O_RDWR, 0664);
	if(fd < 0) {
		cout << "filepath not found!" << endl;
		return false;
	} else {
		cout << "filepath : " << file.filePath << endl;
	}
	
	sendLen = socket.SockSend(socket.m_sockfd, file.filePath, 100);
	if(sendLen < 0) {
		cout << "filepath send error!" << endl;
		return false;
	} else {
		cout << "filepath send success!" << endl;
	}

	return true;
}

bool Client::SendFileData() {
	cout << "begin send data..." << endl;
	int times = 1;
	while((sendLen = read(fd, file.fileData, BUFFER_SIZE))) {
		if(sendLen < 0) {
			cout << "read error." << endl;
			break;
		} else if(sendLen == BUFFER_SIZE) {
			cout << "times = " << times << endl;
			++times;
			if(socket.SockSend(socket.m_sockfd, file.fileData, BUFFER_SIZE) < 0) {
				cout << "send failed!" << endl;
				break;		
			} else {
				LOG("data len is: %d", sendLen);
				cout << "send successful!" << endl;
			}
		} else if(sendLen < BUFFER_SIZE) {
			cout << "times = " << times << endl;
			++times;
			if(socket.SockSend(socket.m_sockfd, file.fileData, sendLen) < 0) {
				cout << "send failed!" << endl;
				break;		
			} else {
				LOG("data len is: %d", sendLen);
				cout << "send successful!" << endl;
			}
			break;
		}
		memset(file.fileData, 0, BUFFER_SIZE); 
	}
	
	close(fd);
	return true;
}


int main() {
	Client client;

	if(client.ClientInit()) {
		cout << "client init success." << endl;
	} else {
		cout << "client init failed." << endl;
	}

	while(true) {
		if(client.ClientSend()) {
			continue;
		} else {
			break;
		}
	}

	client.socket.SockClose();
	
	return 0;
}

四、总结

  目前该CS架构只是作为前期的功能实现,后续将会做进一步的优化,例如对文件发送接收的组包加密之类的,对网络传输模型的优化,对文件接收端做异步处理、消峰处理、容灾处理。合入CS框架之后,发现原来的代码耦合性还是有点高,后面会针对文件上传下载等功能作进一步的解耦设计。
  学习如逆水行舟,不进则退。加油~

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现一个基于 TCP文件传输服务器,你可以按照以下步骤进行: 1. 创建一个 TCP 服务器端程序。你可以使用编程语言(如Python、Java、C++等)提供的网络编程库来实现。在服务器端程序中,你需要创建一个 TCP socket,并绑定到指定的 IP 地址和端口上。 2. 等待客户端的连接请求。使用 socket 的 listen() 函数开始监听来自客户端的连接请求。 3. 接受客户端连接。一旦有客户端发起连接请求,使用 socket 的 accept() 函数接受连接,并返回一个新的 socket 对象用于与客户端进行通信。 4. 接收文件请求。与客户端建立连接后,你可以定义一种协议来传输文件。例如,可以约定客户端发送一个特定的命令来请求文件传输。 5. 打开文件并读取内容。在服务器端,根据客户端请求的文件名,打开相应的文件,并读取文件内容。 6. 将文件内容发送给客户端。使用 socket 的 send() 函数将文件内容逐个数据包发送给客户端。你可以定义合适的数据包大小(如 1024 字节)来进行分包发送。 7. 客户端接收并保存文件内容。在客户端程序中,接收服务器发送的数据包,并将数据包逐个写入文件中,最终组合成完整的文件。 8. 关闭连接。文件传输完成后,关闭与客户端的连接,释放相关资源。 需要注意的是,以上只是一个简单的文件传输服务器的基本流程。实际上,你可能还需要考虑错误处理、并发连接、文件权限等其他方面的问题。此外,为了提高性能和安全性,你还可以对文件传输进行优化和加密处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值