- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我必须说的第一件事是这篇文章受到了 [Remy Lebeau][1] https://stackoverflow.com/users/65863/remy-lebeau 的回答的强烈启发。在这里的问题:
Send binary file over TCP/IP connection .
我正在使用 poco 库和 C++。
目标是通过互联网发送一些大的二进制文件。因此,为了实现这一点,我使用了以下消息 header :
class FileDesc
{
size_t file_size = 0;
size_t chunk_size = 0;
size_t file_name_len = 0;
char file_name[0];
public:
FileDesc(size_t file_sz, size_t buf_sz)
: file_size(ByteOrder::toNetwork(file_sz)),
chunk_size(ByteOrder::toNetwork(buf_sz)) {}
size_t get_file_size() const { return ByteOrder::fromNetwork(file_size); }
size_t get_chunk_size() const { return ByteOrder::fromNetwork(chunk_size); }
string get_file_name() const
{
const auto len = get_file_name_len();
string ret; ret.reserve(len + 1);
for (size_t i = 0; i < len; ++i)
ret.push_back(file_name[i]);
return ret;
}
size_t get_file_name_len() const
{
return ByteOrder::fromNetwork(file_name_len);
}
size_t size() const
{
return sizeof(*this) + ByteOrder::fromNetwork(file_name_len);
}
static shared_ptr<FileDesc> allocate(size_t file_sz, size_t buf_sz,
const string & name)
{
const auto n = name.size();
const size_t sz = sizeof(FileDesc) + n;
shared_ptr<FileDesc> ret((FileDesc*) malloc(sz), free);
new (ret.get()) FileDesc(file_sz, buf_sz);
memcpy(ret->file_name, name.data(), n);
ret->file_name_len = ByteOrder::toNetwork(n);
return ret;
}
};
文件将以固定长度的 block 发送,可能最后一个除外。此 header 在传输开始时发送。发送方部分通知接收方部分 file_size
的文件字节将按大小 chunk_size
的 block 发送并且文件名有file_name_len
人物。这条消息的末尾是文件名,通过字符数组 file_name
,其长度是可变的,取决于文件名长度。静态方法allocate
负责为这个 header 分配足够的大小来存储完整的文件名。
这里是发件人部分的代码:
bool send_file(StreamSocket & sock, const string & file_name, long buf_size)
{
stringstream s;
ifstream file(file_name, ios::binary | ios::ate);
if (not file.is_open() or not file.good()) // verify file reading
{
s << "cannot open file " << file_name;
throw domain_error(s.str());
}
long file_size = file.tellg();
if (file_size == 0)
{
s << "file " << file_name << " has zero bytes";
throw domain_error(s.str());
}
file.seekg(0);
unique_ptr<char[]> chunk(new char[buf_size]);
auto desc_ptr = FileDesc::allocate(file_size, buf_size, file_name);
// send the file description
int status = sock.sendBytes(desc_ptr.get(), desc_ptr->size());
if (status != desc_ptr->size())
return false;
do // now send the file
{
long num_bytes = min(file_size, buf_size);
file.read(chunk.get(), num_bytes);
char * pbuf = chunk.get();
auto n = num_bytes;
while (n > 0)
{
status = sock.sendBytes(pbuf, n);
pbuf += status;
n -= status;
}
file_size -= num_bytes;
}
while (file_size > 0);
char ok; // now I wait for receiving part notifies reception
status = sock.receiveBytes(&ok, sizeof(ok));
return ok == 0;
}
send_file(sock, file_name, buf_size)
打开文件 file_name
并通过 poco 套接字发送其内容 sock
按大小块 buf_len
.所以它将执行 file_size/buf_len
传输加上文件描述符的初始传输。
接收端实例化一个poco ServerSocket
如下:
ServerSocket server_socket(addr);
auto sock = server_socket.acceptConnection();
auto p = receive_file(sock, 256);
receive_file(sock, 256)
在套接字处表示 sock
它将收到一个最大文件长度为 256 字节的文件(足够我的应用程序使用)。这个例程,它是 send_file()
的反面部分, 保存文件,实现如下:
pair<bool, string>
receive_file(StreamSocket & sock, size_t max_file_name_len)
{
stringstream s;
size_t buf_sz = sizeof(FileDesc) + max_file_name_len;
unique_ptr<char[]> buffer(new char[buf_sz]);
int num = sock.receiveBytes(buffer.get(), buf_sz);
FileDesc * msg = reinterpret_cast<FileDesc*>(buffer.get());
if (msg->size() > num)
return make_pair(false, "");
long chunk_size = msg->get_chunk_size();
if (chunk_size == 0)
throw domain_error("chunk size is zero");
const string file_name = msg->get_file_name();
ofstream file(file_name, ios::binary);
if (not file.is_open() or not file.good())
{
s << "cannot create file" << file_name;
throw domain_error(s.str());
}
long file_size = msg->get_file_size();
unique_ptr<char[]> chunk(new char[chunk_size]);
do
{ // num_bytes is the quantity that I must receive for this chunk
auto num_bytes = min(file_size, chunk_size);
char * pbuf = chunk.get();
long n = num_bytes;
while (n > 0)
{
num = sock.receiveBytes(pbuf, n, 0); // <-- here is potentially the problem
if (num == 0) // connection closed?
return make_pair(file_size == 0, file_name);
pbuf += num;
n -= num;
}
file.write(chunk.get(), num_bytes);
file_size -= num_bytes;
}
while (file_size > 0);
char ok;
num = sock.sendBytes(&ok, sizeof(ok)); // notify reception to sender
return make_pair(true, file_name);
}
receive_file()
返回 pair<bool,string>
其中 first
表明我接收正确完成并且second
文件名。
出于我无法理解的原因,有时,当num = sock.receiveBytes(pbuf, n, 0)
收到的字节少于预期 n
并且循环完成读取余数,即与最后发送 block 对应的最后接收。
奇怪的是,直到现在,我的问题只发生在我使用环回 (127.0.0.1) 时,这对于测试来说非常实用,因为我在我的机器上测试了所有内容。相反,当传输在两个不同的对等点之间时,一切正常,当然这并不意味着一切都是正确的。
所以,如果有人能帮我批评我的实现并最终指出问题出在哪里,我将不胜感激
最佳答案
如果你不想阻塞 I/O,你应该设置 socket 为 non-blocking mode .
但是,看起来简单的事情太复杂了;这是一个简单的例子:
$ fallocate -l 1G test.in
$ ls -al test.in
-rw-r--r-- 1 alex alex 1073741824 Oct 15 23:31 test.in
$ ls -al test.out
ls: cannot access test.out: No such file or directory
$./download
$ ls -al test.out
-rw-rw-r-- 1 alex alex 1073741824 Oct 15 23:32 test.out
$ diff test.out test.in
$
所以,我们很好 - 文件大小为 1G 且相同(尽管很无聊)。
客户端代码:
#include "Poco/StreamCopier.h"
#include "Poco/FileStream.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/SocketStream.h"
using namespace Poco;
using namespace Poco::Net;
int main()
{
StreamSocket ss;
ss.connect(SocketAddress("localhost", 9999));
SocketStream istr(ss);
std::string file("test.out");
Poco::FileOutputStream ostr(file, std::ios::binary);
StreamCopier::copyStream(istr, ostr);
return 0;
}
这是服务器:
#include "Poco/Net/SocketAddress.h"
#include "Poco/Net/Socket.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/SocketStream.h"
#include "Poco/StreamCopier.h"
#include "Poco/Timespan.h"
#include "Poco/FileStream.h"
using namespace Poco;
using namespace Poco::Net;
int main()
{
ServerSocket srvs(SocketAddress("localhost", 9999));
Timespan span(250000);
while (true)
{
if (srvs.poll(span, Socket::SELECT_READ))
{
StreamSocket strs = srvs.acceptConnection();
SocketStream ostr(strs);
std::string file("test.in");
Poco::FileInputStream istr(file, std::ios::binary);
StreamCopier::copyStream(istr, ostr);
}
}
return 0;
}
享受POCO !
关于c++ - 通过 poco 流套接字发送文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33103923/
这个问题在这里已经有了答案: Why filter() after flatMap() is "not completely" lazy in Java streams? (8 个答案) 关闭 6
我正在创建一个应用程序来从 Instagram 收集数据。我正在寻找像 Twitter 流 API 这样的流 API,这样我就可以自动实时收集数据而无需发送请求。 Instagram 有类似的 API
我正在使用 Apache Commons 在 Google App Engine 中上传一个 .docx 文件,如此链接中所述 File upload servlet .上传时,我还想使用 Apach
我尝试使用 DynamoDB 流和 AWS 提供的 Java DynamoDB 流 Kinesis 适配器捕获 DynamoDB 表更改。我正在 Scala 应用程序中使用 AWS Java 开发工具
我目前有一个采用 H.264 编码的 IP 摄像机流式视频 (RTSP)。 我想使用 FFmpeg 将此 H.264 编码流转换为另一个 RTSP 流,但 MPEG-2 编码。我该怎么做?我应该使用哪
Redis 流是否受益于集群模式?假设您有 10 个流,它们是分布在整个集群中还是都分布在同一节点上?我计划使用 Redis 流来实现真正的高吞吐量(200 万条消息/秒),所以我担心这种规模的 Re
这件事困扰了我一段时间。 所以我有一个 Product 类,它有一个 Image 列表(该列表可能为空)。 我想做 product.getImages().stream().filter(...) 但
是否可以使用 具有持久存储的 Redis 流 还是流仅限于内存数据? 我知道可以将 Redis 与核心数据结构的持久存储一起使用,但我已经能够理解是否也可以使用 Redis 中的流的持久存储。 最佳答
我开始学习 Elixir 并遇到了一个我无法轻松解决的挑战。 我正在尝试创建一个函数,该函数接受一个 Enumerable.t 并返回另一个 Enumerable.t ,其中包含下 n 个项目。它与
我试图从 readLine 调用创建一个无限的字符串流: import java.io.{BufferedReader, InputStreamReader} val in = new Buffere
你能帮我使用 Java 8 流 API 编写以下代码吗? SuperUser superUser = db.getSuperUser; for (final Client client : super
我正在尝试服用补品routeguide tutorial,并将客户端变成rocket服务器。我只是接受响应并将gRPC转换为字符串。 service RouteGuide { rpc GetF
流程代码可以是run here. 使用 flow,我有一个函数,它接受一个键值对对象并获取它的值 - 它获取的值应该是字符串、数字或 bool 值。 type ValueType = string
如果我有一个函数返回一个包含数据库信息的对象或一个空对象,如下所示: getThingFromDB: async function(id:string):Promise{ const from
我正在尝试使用javascript api和FB.ui将ogg音频文件发布到流中, 但是我不知道该怎么做。 这是我给FB.ui的电话: FB.ui( { method: '
我正在尝试删除工作区(或克隆它以使其看起来像父工作区,但我似乎两者都做不到)。但是,当我尝试时,我收到此消息:无法删除工作区 test_workspace,因为它有一个非空的默认组。 据我所知,这意味
可以使用 Stream|Map 来完成此操作,这样我就不需要将结果放入外部 HashMap 中,而是使用 .collect(Collectors.toMap(...)); 收集结果? Map rep
当我们从集合列表中获取 Stream 时,幕后到底发生了什么?我发现很多博客都说Stream不存储任何数据。如果这是真的,请考虑代码片段: List list = new ArrayList(); l
我对流及其工作方式不熟悉,我正在尝试获取列表中添加的特定对象的出现次数。 我找到了一种使用Collections来做到这一点的方法。其过程如下: for (int i = 0; i p.conten
我希望将一个 map 列表转换为另一个分组的 map 列表。 所以我有以下 map 列表 - List [{ "accId":"1", "accName":"TestAcc1", "accNumber
我是一名优秀的程序员,十分优秀!