遗憾的是,当我尝试将文件从 nfoserver 传输到 PC 时,读取的文件大小不正确,并且永远不会结束 do while 循环。任何帮助将不胜感激!当发送了正确的文件大小但未达到 eof 时,它会停止,因为它从服务器读取了错误的文件大小。我真的不知道是什么原因造成的,因为我对 boost::asio 库或一般网络没有太多经验。这就是为什么任何帮助将不胜感激,这样我就可以更好地理解图书馆了:)
客户:
void receive_file_content_over_tcp(const std::string& filePath, boost::asio::ip::tcp::socket& socket) {
std::ofstream file(filePath, std::ios::binary | std::ios::trunc);
if (!file.is_open()) {
std::cerr << "Failed to open file for receiving." << std::endl;
return;
}
boost::asio::streambuf receive_buffer;
boost::asio::streambuf fileSizeBuffer;
boost::system::error_code error;
std::size_t totalBytesReceived = 0;
std::size_t fileSize = 0;
// Receive file size
boost::asio::read(socket, fileSizeBuffer, boost::asio::transfer_exactly(sizeof(std::size_t)), error);
if (error) {
std::cerr << "Failed to receive file size: " << error.message() << std::endl;
return;
}
// Extract file size from the buffer
std::istream fileSizeStream(&fileSizeBuffer);
fileSizeStream.read(reinterpret_cast<char*>(&fileSize), sizeof(std::size_t));
std::cout << "Receiving file. File size: " << fileSize << " bytes." << std::endl;
// Receive file content
while (totalBytesReceived < fileSize) {
std::size_t bytesRead = boost::asio::read(socket, receive_buffer, boost::asio::transfer_at_least(1), error);
if (error && error != boost::asio::error::eof) {
std::cerr << "Error while receiving file content: " << error.message() << std::endl;
return;
}
if (bytesRead > 0) {
// Write the received data to the file
totalBytesReceived += bytesRead;
std::istream is(&receive_buffer);
file << is.rdbuf();
// Display progress
std::cout << "Received: " << totalBytesReceived << " bytes out of " << fileSize << " bytes (" << (totalBytesReceived * 100 / fileSize) << "%)." << std::endl;
}
}
std::cout << "File content received successfully. Total bytes received: " << totalBytesReceived << std::endl;
}
服务器:
void send_file_content_over_tcp(const std::string& filePath, boost::asio::ip::tcp::socket& socket) {
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
std::cerr << "Failed to open file for sending." << std::endl;
return;
}
// Get the file size
std::size_t fileSize = file.tellg();
file.seekg(0, std::ios::beg);
// Create a buffer to hold the file content
std::vector<char> buffer(fileSize);
if (!file.read(buffer.data(), fileSize)) {
std::cerr << "Failed to read file for sending." << std::endl;
return;
}
std::cout << "File read successfully. File size: " << fileSize << " bytes." << std::endl;
// Send the file content
boost::system::error_code error;
std::size_t bytesSent = boost::asio::write(socket, boost::asio::buffer(buffer), error);
if (error) {
std::cerr << "Error while sending file content: " << error.message() << std::endl;
}
else {
std::cout << "File content sent successfully. Bytes sent: " << bytesSent << std::endl;
}
}
我希望它能够读取正确的文件大小并传输它,但输出如下:
接收文件。文件大小:12894362189 字节。
已接收:12894362189 字节中的 512 字节 (0%)。
服务器输出:
文件读取成功。文件大小:2975744 字节。
文件内容发送成功。发送的字节数:2975744 该文件大约有 2.9mb 大。
过去几天我尝试了很多东西,但它从未用 eof 标志打破循环。因此,如果您能帮助我,那会让我非常高兴!谢谢。
您永远不会在
send_file_contents
中发送尺寸。你为什么想读它?
即使您这样做,您也需要确保以可移植的方式发送它(例如,考虑到字节顺序),例如使用
using NetworkSize = boost::endian::big_uint64_t;
我会简化发送,并包括尺寸:
static std::vector<char> read_file(path const& fspec) {
std::ifstream file(fspec, std::ios::binary);
return std::vector<char>(std::istreambuf_iterator<char>(file), {});
}
void send_file_content_over_tcp(path const& fspec, tcp::socket& socket) {
auto const contents = read_file(fspec);
std::cout << "File read successfully. File size: " << contents.size() << " bytes." << std::endl;
// Send the file content
NetworkSize fileSize = contents.size();
std::vector<asio::const_buffer> payload{
asio::buffer(&fileSize, sizeof(fileSize)),
asio::buffer(contents),
};
error_code ec;
auto n = write(socket, payload, ec);
std::cout << "Content bytes sent: " << n << " (" << ec.message() << ")\n";
}
现在您可以完全对称地接收它了:
void receive_file_content_over_tcp(path fspec, tcp::socket& socket) {
error_code ec;
NetworkSize expected = 0;
read(socket, asio::buffer(&expected, sizeof(expected)), ec);
std::cout << "Expected file size: " << expected << " bytes (" << ec.message() << ")" << std::endl;
if (!ec.failed()) {
std::vector<char> data(expected);
size_t actual = read(socket, asio::buffer(data), ec);
std::cout << "Received " << actual << "/" << expected << ": " << ec.message();
if (!ec || (actual == expected && ec == asio::error::eof)) {
std::cout << "File content received successfully." << std::endl;
std::ofstream(fspec, std::ios::binary | std::ios::trunc) //
.write(data.data(), data.size());
}
}
}
可以测试其是否有效:Live On Coliru
int main() {
asio::io_context ioc;
std::thread server([ex = ioc.get_executor()] {
auto s = tcp::acceptor(ex, {{}, 7878}).accept();
send_file_content_over_tcp("main.cpp", s);
});
::sleep(1); // make sure server is accepting
{
tcp::socket client(ioc);
client.connect({{}, 7878});
receive_file_content_over_tcp("main-clone.cpp", client);
}
server.join();
}
印刷:
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
md5sum *.cpp
File read successfully. File size: 2265 bytes.
Content bytes sent: 2273 (Success)
Expected file size: 2265 bytes (Success)
Received 2265/2265: SuccessFile content received successfully.
d47f7c90dfe3ca271f81ca203d53798e main-clone.cpp
d47f7c90dfe3ca271f81ca203d53798e main.cpp
您的发送未进行流式传输,因此我认为这并不重要。如果是是,请逐块阅读:Live On Coliru
std::ofstream ofs(fspec, std::ios::trunc);
// ofs.exceptions(std::ios::badbit | std::ios::failbit);
for (std::array<char, 1024> buf; expected && !ec;) {
size_t actual = read(socket, asio::buffer(buf), ec);
assert(actual <= expected);
std::cout << "Received " << actual << "/" << expected << ": " << ec.message() << "\n";
ofs.write(buf.data(), actual);
expected -= actual;
}
if (ofs.good() && expected == 0 && (!ec || ec == asio::error::eof))
std::cout << "File content received successfully." << std::endl;
印刷
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
md5sum *.cpp
File read successfully. File size: 2342 bytes.
Content bytes sent: 2350 (Success)
Expected file size: 2342 bytes (Success)
Received 1024/2342: Success
Received 1024/1318: Success
Received 294/294: End of file
File content received successfully.
fc9f108bcee2fd7f711ab7a63d81baae main-clone.cpp
fc9f108bcee2fd7f711ab7a63d81baae main.cpp
streambuf
呢?这不是适合这项工作的工具。您当然应该对所有读取使用单个缓冲区。如果你必须的话,我认为以下内容会很不错:
void receive_file_content_over_tcp(path fspec, tcp::socket& socket) {
asio::streambuf buf;
NetworkSize expected = 0;
error_code ec;
auto n = read(socket, buf, asio::transfer_exactly(sizeof(expected)), ec);
std::memcpy(&expected, buf.data().data(), n);
buf.consume(n);
std::cout << "Expected file size: " << expected << " bytes (" << ec.message() << "), REMAINING "
<< buf.size() << std::endl;
std::ofstream ofs(fspec, std::ios::trunc);
// ofs.exceptions(std::ios::badbit | std::ios::failbit);
while (expected && !ec) {
size_t actual = read(socket, buf, asio::transfer_at_least(1024), ec);
assert(actual <= expected);
assert(buf.size() == actual);
std::cout << "Received " << actual << "/" << expected << ": " << ec.message() << "\n";
ofs << &buf;
expected -= actual;
}
if (ofs.good() && expected == 0 && (!ec || ec == asio::error::eof))
std::cout << "File content received successfully." << std::endl;
}