注意:in_fd规定指向真实的文件,不能是socket等管道文件描述符,一般使open返回值,而out_fd则是socket描述符
//打开文件
int file_fd = open("./wyg.txt",O_RDWR | O_CREAT,664);
if(file_fd < 0)
{
perror("open");
return -1;
}
//设置file_fd文件描述符属性
struct stat stat_buf;
fstat(file_fd,&stat_buf);
//把目标文件传递给服务器
int ret = sendfile(newts.GetFd(),file_fd,NULL,stat_buf.st_size);
if(ret < 0)
{
perror("sendfile");
return -1;
}
实现功能:客户端连接服务器,会自动下载服务器的文件
主要实现:其实就是基于tcp套接字编程 + sendfile函数来实现的,传输效率高
1、服务器
2、客户端
3、总代码实现如下
tcp_svr.hpp
#include
#include
#include
#include
#include
#include
#include
class Tcpsvr
{
public:
Tcpsvr()
{
_sockfd = -1;
}
~Tcpsvr()
{}
bool Createsocket()//创建套接字
{
_sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(_sockfd < 0)
{
perror("socket");
return false;
}
return true;
}
bool Bind(const std::string& ip,uint16_t port)//绑定地址信息
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = bind(_sockfd,(struct sockaddr*)&addr,sizeof(addr));
if(ret < 0)
{
perror("bind");
return false;
}
return true;
}
bool Listen(int backlog = 5)//监听
{
int ret = listen(_sockfd,backlog);
if(ret < 0)
{
perror("listen");
return false;
}
return true;
}
bool Accept(Tcpsvr *newts,struct sockaddr_in* peeraddr)//获取连接
{
socklen_t addrlen = sizeof(struct sockaddr_in);
int newfd = accept(_sockfd,(struct sockaddr*)peeraddr,&addrlen);
if(newfd < 0)
{
perror("accept");
return false;
}
newts->_sockfd = newfd;
return true;
}
bool Connect(const std::string& ip,uint16_t port)//发起连接
{
struct sockaddr_in dest_addr;
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(port);
dest_addr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = connect(_sockfd,(struct sockaddr*)&dest_addr,sizeof(dest_addr));
if(ret < 0)
{
perror("connect");
return false;
}
return true;
}
bool Recv(std::string* data)//接收数据
{
char buf[1024] = {0};
int recv_size = recv(_sockfd,buf,sizeof(buf) - 1,0);
if(recv_size < 0)
{
perror("recv");
return false;
}
else if(recv_size == 0)
{
//对端关闭了连接
printf("peer shutdown connect\n");
return false;
}
data->assign(buf,recv_size);
return true;
}
int GetFd()//获取套接字描述符
{
return _sockfd;
}
void Close()//关闭套接字
{
close(_sockfd);
}
private:
int _sockfd;
};
服务器svr.cpp
#include "tcp_svr.hpp"
#include
#include
#include
#include
#include
#define CHECK_RET(p) if(p == false){return 0;}//判断封装接口是否调用成功,失败直接返回
int main()
{
Tcpsvr tp;
//1、创建套接字
CHECK_RET(tp.Createsocket());
//2、绑定地址信息
CHECK_RET(tp.Bind("192.168.163.129",18888));
//3、监听
CHECK_RET(tp.Listen());
Tcpsvr newts;
while(1)
{
//4、获取连接
struct sockaddr_in cli_addr;
CHECK_RET(tp.Accept(&newts,&cli_addr));
//5、打开文件
int file_fd = open("./wyg.txt",O_RDWR | O_CREAT,664);
if(file_fd < 0)
{
perror("open");
return -1;
}
//6、//设置file_fd文件描述符属性
struct stat stat_buf;
fstat(file_fd,&stat_buf);
//7、//把目标文件传递给服务器
int ret = sendfile(newts.GetFd(),file_fd,NULL,stat_buf.st_size);
if(ret < 0)
{
perror("sendfile");
return -1;
}
}
//8、关闭套接字
tp.Close();
newts.Close();
return 0;
}
客户端cli.cpp
#include "tcp_svr.hpp"
#define CHECK_RET(p) if(p == false){return 0;}//判断封装的接口是否调用成功,失败直接返回
int main()
{
Tcpsvr tp;
//1、创建套接字
CHECK_RET(tp.Createsocket());
//2、发起连接
CHECK_RET(tp.Connect("192.168.163.129",18888));
//3、接收
std::string buf;
tp.Recv(&buf);
printf("%s",buf.c_str());
//4、关闭套接字
tp.Close();
return 0;
}
makefile
all:cli svr
cli:cli.cpp
g++ $^ -o $@
svr:svr.cpp
g++ $^ -o $@
wyg.txt
hello world
1、我在要传输的文件wyg.txt就写了hello world这一句话,其实是可以实现大文件传输的,感兴趣的可以传大文件试一下,也可以把程序改成并发的,这样传的更快
2、我在让客户端接收的时候只是让文件内容打印出来了,其实是可以写入到另外一个文件的