boost::asio中的write_some() 源码。
template <typename ConstBufferSequence>
std::size_t write_some(const ConstBufferSequence& buffers)
{
boost::system::error_code ec;
std::size_t s = this->impl_.get_service().send(
this->impl_.get_implementation(), buffers, 0, ec);
boost::asio::detail::throw_error(ec, "write_some");
return s;
}
boost::asio提供了几种同步写的api,write_some() 可以每次向指定的空间写入固定的字节数,如果写缓冲区满了,就只写一部分,返回写入的字节数。write_some() 是 Boost.Asio 库中用于同步写入数据到套接字的函数之一。它可以用来将数据块写入到套接字,并在数据写入一部分之后立即返回。这个函数适用于阻塞式的 I/O 操作,因此在数据全部写入之前,函数可能会阻塞当前线程。
void BoostAsio::WriteSomeData(boost::asio::ip::tcp::socket& socket) {
std::string buff("hello world");
std::size_t total_write_bytes = 0;
//循环发送
//write_some返回每次写入的字节数
//total_bytes_written是已经发送的字节数。
//每次发送buf.length()- total_bytes_written字节数据
while (total_write_bytes != buff.length()) {
total_write_bytes = total_write_bytes + socket.write_some(boost::asio::buffer(buff.c_str() + total_write_bytes, buff.length() - total_write_bytes));
}
}
int32_t BoostAsio::SendDataByWriteSome(std::string& raw_ip_address, uint16_t& port_num) {
try {
//Step 1: create endpoint
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);
//Step 2: create socket
boost::asio::io_context context;
boost::asio::ip::tcp::socket socket(context, ep.protocol());
//Step 3: socket connect endpoint
socket.connect(ep);
//Step 4: write_some data
WriteSomeData(socket);
}
catch (boost::system::system_error& e) {
std::cout << "Error occured!Error code: " << e.code() << " .Message: " << e.what();
return e.code().value();
}
return 0;
}
这段代码展示了如何循环使用 write_some() 函数将数据发送到套接字。让我对你的代码进行解释:在这个示例中,函数 WriteToSocket 接受一个已连接的 Boost.Asio TCP 套接字 socket,并使用 write_some() 循环发送数据。
循环的逻辑如下:
在循环内部:
在 SendDataByWriteSome 函数中:
这个代码示例中的 WriteSomeData() 函数负责循环发送数据,通过不断使用 write_some() 来发送数据块。需要注意的是,write_some() 可能会在每次写入时阻塞线程,这可能会影响性能,特别是在需要大规模数据传输时。在这种情况下,可能需要考虑使用异步的方式来进行写入操作,以避免阻塞线程。
boost::asio中的socket中的send源码:
template <typename ConstBufferSequence>
std::size_t send(const ConstBufferSequence& buffers)
{
boost::system::error_code ec;
std::size_t s = this->impl_.get_service().send(
this->impl_.get_implementation(), buffers, 0, ec);
boost::asio::detail::throw_error(ec, "send");
return s;
}
write_some() 使用起来比较麻烦,需要多次调用,asio提供了send() 函数。send()函数会一次性将buffer中的内容发送给对端,如果有部分字节因为发送缓冲区满无法发送,则阻塞等待,直到发送缓冲区可用,则继续发送完成。
int32_t BoostAsio::SendDataBySend(std::string& raw_ip_address, uint16_t& port_num) {
try {
//Step 1: create endpoint
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);
//Step 2: create socket
boost::asio::io_context context;
boost::asio::ip::tcp::socket socket(context, ep.protocol());
//Step 3: socket connect endpoint
socket.connect(ep);
//Step 4: write data
std::string buffer("hello world");
int32_t bytes=socket.send(boost::asio::buffer(buffer.c_str(), buffer.length()));
if (0 >= bytes) {
std::cout << "Send data failed!" << std::endl;
return -1;
}
}
catch (boost::system::system_error& e) {
std::cout << "Error occured!Error code: " << e.code() << ". Message" << e.what();
return e.code().value();
}
return 0;
}
这段代码展示了如何使用 Boost.Asio 进行 TCP 数据发送,包括创建套接字、连接到服务器,并使用 send() 函数来发送数据。我会对这段代码进行解释:
需要注意的是,send() 函数会阻塞当前线程直到所有数据被发送出去。在某些情况下,可能需要在发送之前设置套接字的发送选项,例如设置非阻塞模式。 此外,与使用循环的 write_some() 不同,send() 函数在一次调用中将数据一次性发送出去。
无论是使用循环的 write_some() 还是一次性的 send(),你都需要根据具体情况来选择合适的发送方式,以及是否考虑异步的发送操作。
asio中的write() 源码:
template <typename SyncWriteStream, typename ConstBufferSequence>
inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers,
typename constraint<
is_const_buffer_sequence<ConstBufferSequence>::value
>::type)
{
boost::system::error_code ec;
std::size_t bytes_transferred = write(s, buffers, transfer_all(), ec);
boost::asio::detail::throw_error(ec, "write");
return bytes_transferred;
}
类似send() 方法,asio还提供了一个write() 函数,可以一次性将所有数据发送给对端,如果发送缓冲区满了则阻塞,直到发送缓冲区可用,将数据发送完成。
int32_t BoostAsio::SendDataByAsioWrite(std::string& raw_ip_address, uint16_t& port_num) {
try {
//Step 1: create endpoint
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);
//Step 2: create socket
boost::asio::io_context context;
boost::asio::ip::tcp::socket socket(context, ep.protocol());
//Step 3: connect endpoint
socket.connect(ep);
//Step 4: send data;
std::string buff("hello world");
int32_t bytes=boost::asio::write(socket, boost::asio::buffer(buff.c_str(), buff.length()));
if (0 >= bytes) {
std::cout << "Send data failed!" << std::endl;
return -1;
}
}
catch (boost::system::system_error& e) {
std::cout << "Error occured!Error code : " << e.code().value() << ". Message :" << e.what();
return e.code().value();
}
return 0;
}
在这段代码示例演示了如何使用 Boost.Asio 的 write() 函数来进行 TCP 数据发送,这个函数可以更方便地将数据发送到套接字。我会解释一下你的代码:
与之前的例子相比,write() 函数的优势在于它可以一次性地将数据全部发送出去,而不需要手动迭代循环发送。此外,write() 函数可以将数据发送到套接字上,无需显式传递套接字作为参数,因为你已经在创建函数体内部的上下文中定义了套接字 socket。
需要注意的是,无论是 write() 还是其他发送方法,都应该考虑处理可能的错误情况,例如套接字连接失败或数据发送失败。
read_some的源代码:
template <typename MutableBufferSequence>
std::size_t read_some(const MutableBufferSequence& buffers)
{
boost::system::error_code ec;
std::size_t s = this->impl_.get_service().receive(
this->impl_.get_implementation(), buffers, 0, ec);
boost::asio::detail::throw_error(ec, "read_some");
return s;
}
同步读和同步写类似,提供了读取指定字节数的接口read_some
std::string BoostAsio::ReadSomeData(boost::asio::ip::tcp::socket& socket) {
const unsigned char SIZE = 10;
char buff[SIZE];
std::size_t total_read_bytes = 0;
while (total_read_bytes != SIZE) {
total_read_bytes = total_read_bytes + socket.read_some(boost::asio::buffer(buff + total_read_bytes, SIZE - total_read_bytes));
}
//C++ 非常量引用的初始值必须是左值
return std::string(buff, total_read_bytes);
}
int32_t BoostAsio::RecvDataByReadSome(std::string& raw_ip_adress, uint16_t& port_num) {
try {
//Step 1: create endpoint
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_adress), port_num);
//Step 2: create socket
boost::asio::io_context context;
boost::asio::ip::tcp::socket socket(context, ep.protocol()); //ep.protocol()可不写
//Step 3: socket connect endpoint
socket.connect(ep);
//Step 4: receive data
std::cout << ReadSomeData(socket) << std::endl;
}
catch (boost::system::system_error& e) {
std::cout << "Error occured!Error code : " << e.code().value() << ". Message :" << e.what();
return e.code().value();
}
return 0;
}
需要注意的是,ReadSomeData() 函数使用了 read_some() 函数来循环接收数据。但是,read_some() 可能会阻塞当前线程,直到至少有一个字节的数据到达。在实际应用中,可能需要考虑使用异步的方式来进行数据接收,以避免阻塞线程。此外,你的代码中固定了接收数据的大小为 SIZE,如果实际接收到的数据不足 SIZE 字节,可能会导致问题。最好的方式是根据实际情况动态地处理接收数据的大小。
receive() 源代码 :
template <typename MutableBufferSequence>
std::size_t receive(const MutableBufferSequence& buffers)
{
boost::system::error_code ec;
std::size_t s = this->impl_.get_service().receive(
this->impl_.get_implementation(), buffers, 0, ec);
boost::asio::detail::throw_error(ec, "receive");
return s;
}
可以一次性同步接收对方发送的数据:
int32_t BoostAsio::RecvDataByReceive(std::string& raw_ip_address, uint16_t& port_num) {
try {
//Step1: create endpoint
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);
//Step2: create socket
boost::asio::io_context context;
boost::asio::ip::tcp::socket socket(context, ep.protocol());
//Step3: socket connect endpoint
socket.connect(ep);
//Step4: receive data
const unsigned char SIZE = 10;
char buff[SIZE];
int32_t recv_size = socket.receive(boost::asio::buffer(buff, SIZE));
if (0 >= recv_size) {
std::cout << "receive data failed!" << std::endl;
return -1;
}
std::cout << buff << std::endl;
}
catch (boost::system::system_error& e) {
std::cout << "Error occured!Error code: " << e.code().value() << " . Message: " << e.what();
return e.code().value();
}
return 0;
}
这段代码展示了如何使用 Boost.Asio 进行 TCP 数据接收,包括创建套接字、连接到服务器,并使用 receive() 函数接收数据。我会解释这段代码:
在 RecvDataByReceive 函数中:
需要注意的是,receive() 函数会阻塞当前线程直到至少有一个字节的数据到达。与之前的例子类似,你可能需要考虑使用异步的方式来进行数据接收,以避免阻塞线程。另外,确保你适当地处理接收到的数据大小,以避免接收到的数据超出缓冲区大小。
**read()**函数源码:
template <typename SyncReadStream, typename MutableBufferSequence>
inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers,
typename constraint<
is_mutable_buffer_sequence<MutableBufferSequence>::value
>::type)
{
boost::system::error_code ec;
std::size_t bytes_transferred = read(s, buffers, transfer_all(), ec);
boost::asio::detail::throw_error(ec, "read");
return bytes_transferred;
}
可以一次性同步读取对方发送的数据:
int32_t BoostAsio::RecvDataByRead(std::string& raw_ip_address, uint16_t& port_num) {
try {
//Step1: create endpoint
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string(raw_ip_address), port_num);
//Step2: create socket
boost::asio::io_context context;
boost::asio::ip::tcp::socket socket(context, ep.protocol());
//Step3: socket connect endpoint
socket.connect(ep);
//Step: read data
const unsigned char SIZE = 10;
char buff[SIZE];
int32_t length = boost::asio::read(socket, boost::asio::buffer(buff, SIZE));
if (0 >= length) {
std::cout << "Read data failed!" << std::endl;
return -1;
}
std::cout << buff << std::endl;
}
catch (boost::system::system_error& e) {
std::cout << "Error occured!Error code: " << e.code().value() << ". Message: " << e.what();
return e.code().value();
}
return 0;
}
这段代码示例展示了如何使用 Boost.Asio 进行 TCP 数据接收,包括创建套接字、连接到服务器,并使用 read() 函数接收数据。我会解释这段代码:
在 RecvDataByRead() 函数中:
与之前的例子类似,read() 函数会阻塞当前线程直到接收到足够的数据。如果需要避免阻塞线程,你可以考虑使用异步的方式进行数据接收。另外,确保适当地处理接收到的数据大小,以避免数据溢出。
在 Boost.Asio 网络库中,socket.send()、boost::asio::write()、socket.receive() 和 boost::asio::read() 都是用于进行数据发送和接收的函数,但它们之间有一些区别。我会为你解释这些区别:
发送数据:
socket.send():
boost::asio::write():
接收数据:
socket.receive():
boost::asio::read():
总结:
read_util() 源码:
template <typename SyncReadStream, typename Allocator>
inline std::size_t read_until(SyncReadStream& s,
boost::asio::basic_streambuf<Allocator>& b, char delim)
{
return read_until(s, basic_streambuf_ref<Allocator>(b), delim);
}
这段代码是 Boost.Asio 库中 read_until() 函数的一个实现,用于从输入流中读取数据直到遇到指定的分隔符字符。这个函数的目的是读取数据并将其存储到一个 boost::asio::basic_streambuf 缓冲区中,直到遇到指定的分隔符。
**template
inline std::size_t read_until(SyncReadStream& s, boost::asio::basic_streambuf& b, char delim): 这是函数的签名。它接受三个参数:一个同步读取流 s,一个 boost::asio::basic_streambuf 缓冲区 b,以及一个指定的分隔符字符 delim。
return read_until(s, basic_streambuf_ref(b), delim): 这是函数体的内容。它调用另一个 read_until 函数,并将传递的参数进行适配。basic_streambuf_ref 是一个用于将 basic_streambuf 转换为适当类型的辅助类。
总体上,这段代码提供了一个便捷的方式,允许你通过指定的分隔符从给定的同步读取流(例如套接字)中读取数据并存储在 boost::asio::basic_streambuf 缓冲区中。这个函数的实现内部会处理适当的转换,以调用实际的 read_until 函数。
我们可以一直读取,直到读取指定字符结束:
std::string BoostAsio::ReadDataByUtil(boost::asio::ip::tcp::socket& socket) {
boost::asio::streambuf buff;
// Synchronously read data from the socket until
// '\n' symbol is encountered.
boost::asio::read_until(socket, buff, '\n');
std::string message;
// Because buffer 'buf' may contain some other data
// after '\n' symbol, we have to parse the buffer and
// extract only symbols before the delimiter.
std::istream input_stream(&buff);
std::getline(input_stream, message);
return message;
}
步骤解释:
这个函数的目的是从套接字中读取一行数据,以 \n 符号作为分隔符。返回的消息字符串将包含分隔符之前的所有字符。
TCP(Transmission Control Protocol)和UDP(User Datagram Protocol) 是两种常见的传输层协议,用于在计算机网络中传输数据。它们在功能和特性上有很多区别,以下是它们的主要区别:
连接性与无连接性:
可靠性:
顺序性:
延迟和效率:
适用场景:
用途:
TCP 和 UDP 在通信时的数据处理方式的确存在差异,其中一个显著的区别就是 TCP 在发送和接收数据时需要使用缓冲区,而 UDP 则不需要。
TCP:
UDP:
因此,TCP 在数据传输过程中需要使用缓冲区来处理数据的分段、重新组合和重传,以确保数据的可靠性和有序性。而 UDP 则不需要这样的缓冲区处理,因为它的设计目标更加注重实时性和轻量性。
std::getline 是 C++ 标准库提供的一个函数,用于从输入流中读取一行文本并存储到一个字符串中。它的基本语法如下:
#include
#include
// ...
std::istream& getline(std::istream& is, std::string& str, char delim);
其中:
用法示例:
#include
#include
int main() {
std::string line;
std::cout << "Enter a line of text: ";
std::getline(std::cin, line); // Read a line from standard input
std::cout << "You entered: " << line << std::endl;
return 0;
}
在这个示例中,程序会等待用户输入一行文本,然后使用 std::getline 从标准输入流中读取这一行,并将其存储在 line 字符串中。之后,程序会输出用户输入的内容。
在你之前提供的代码中,std::getline(input_stream, message) 会从 input_stream 中读取数据,直到遇到换行符(\n)为止,并将读取的数据存储在 message 字符串中。这样可以有效地从 boost::asio::streambuf 中提取一行数据。
输入流 (input stream) 和输出流 (output stream) 是 C++ 标准库中用于从数据源读取数据和向数据目标写入数据的抽象概念。这些概念可以用于各种类型的数据源和目标,包括文件、字符串、键盘、套接字等。以下是一些常见的输入流和输出流:
常见的输入流(用于从数据源读取数据):
常见的输出流(用于向数据目标写入数据):
关于为什么套接字 (socket) 被视为输入流,这涉及到套接字的通信模型。在网络通信中,套接字可以作为数据源和数据目标。例如,服务器可以通过套接字将数据发送给客户端,客户端可以通过套接字接收来自服务器的数据。从套接字读取数据的操作类似于从其他输入流(如文件输入流)读取数据。因此,套接字被视为输入流,用于从套接字中读取来自其他端点的数据。
总结起来,输入流和输出流是 C++ 标准库提供的一种抽象,用于处理从各种不同类型的数据源读取数据和向各种不同类型的数据目标写入数据的操作。套接字被视为输入流,因为它可以从远程端点读取数据。
套接字(socket)既可以是输入流,也可以是输出流,甚至可以同时兼具这两个角色,具体取决于你如何使用它。
在网络通信中,套接字是一种双向通信的工具,可以同时用于发送和接收数据。因此,套接字可以被视为既具有输入流特性(用于接收数据),又具有输出流特性(用于发送数据)。这种双向通信的特性使得套接字非常灵活,可以用于实现各种类型的通信模式。
当你从套接字中读取数据时(接收数据),你可以将套接字视为输入流,使用类似于输入流的操作,如 recv() 函数或 Boost.Asio 中的 read() 函数来读取数据。
当你向套接字发送数据时,你可以将套接字视为输出流,使用类似于输出流的操作,如 send() 函数或 Boost.Asio 中的 write() 函数来发送数据。
因此,套接字的角色取决于你是从中读取数据还是向其中写入数据。在网络通信中,套接字在发送和接收数据之间切换,以实现双向通信。
std::istream 和 std::iostream 都是 C++ 标准库中的类,用于处理输入流(input stream)操作,但它们在功能和用法上有一些不同。
std::istream:
std::iostream:
示例:
#include
int main() {
int number;
// Using std::cin from std::istream
std::cout << "Enter a number: ";
std::cin >> number; // Read input from standard input
// Using std::cout from std::ostream
std::cout << "You entered: " << number << std::endl; // Output to standard output
// Using std::iostream for both input and output
std::iostream io_stream(std::cin.rdbuf(), std::cout.rdbuf());
io_stream << "Hello, this is std::iostream!" << std::endl;
return 0;
}
总之,std::istream 和 std::iostream 都是用于输入流操作的类,但前者主要用于读取数据,后者则结合了输入和输出功能。
std::ostream 是 C++ 标准库中的一个抽象类,用于表示输出流。它定义了一组通用的输出操作,使得你可以将数据写入不同类型的数据目标,如屏幕、文件、字符串等。std::ostream 类是输出流操作的基类,它提供了各种方法来将数据写入输出流。
std::ostream 类提供了诸如 operator<<、write 等成员函数,用于向输出流中写入数据。其中,operator<< 运算符被广泛用于将不同类型的数据写入输出流。这个运算符支持重载,因此你可以通过重载来自定义输出格式。
以下是一个示例,展示如何使用 std::ostream 来输出数据到标准输出(屏幕):
#include
int main() {
int number = 42;
double pi = 3.14159;
std::cout << "The number is: " << number << std::endl;
std::cout << "The value of pi is: " << pi << std::endl;
return 0;
}
在这个示例中,std::cout 是一个实现了 std::ostream 接口的输出流,它用于将数据写入标准输出(屏幕)。通过使用 operator<< 运算符,你可以将数据输出到输出流中。
需要注意的是,std::ostream 是一个抽象类,不能直接实例化。你可以使用其派生类,如 std::cout(标准输出)、std::ofstream(文件输出流)、std::ostringstream(字符串输出流)等,来进行实际的输出操作。
boost::asio::streambuf 是 Boost.Asio 库提供的一个类,用于管理缓冲区,特别是在异步网络通信中。它可以用于将接收的数据存储在缓冲区中,然后进一步处理。boost::asio::streambuf 提供了一些用于读取和写入数据的成员函数。
std::istream 是 C++ 标准库提供的一个类,用于处理输入流。它提供了一组通用的输入操作,使得你可以从不同的数据源读取数据,如键盘、文件、字符串等。
boost::asio::streambuf 对象中存储的数据可以通过构造一个 std::istream 对象,从而使得你可以使用标准的输入流操作来处理其中的数据。这在需要将网络通信接收的数据以输入流的方式处理时非常有用。你可以通过将 boost::asio::streambuf 的缓冲区传递给 std::istream 构造函数来实现。
#include
#include
int main() {
boost::asio::streambuf buffer;
std::ostream output_stream(&buffer);
output_stream << "Hello, Boost.Asio and std::istream!";
std::istream input_stream(&buffer);
std::string data;
input_stream >> data;
std::cout << "Read from streambuf: " << data << std::endl;
return 0;
}
在这个示例中,我们首先使用 boost::asio::streambuf 将数据写入缓冲区,然后通过构造一个 std::istream 对象,从缓冲区中读取数据,就像处理标准输入流一样。这允许我们在使用标准输入流操作的同时,与 Boost.Asio 结合处理网络通信的数据。
输出结果:
Read from streambuf: Hello,
代码中似乎存在一个小问题,导致输出结果可能不符合预期。问题出现在对 std::istream 使用 >> 运算符时。
std::istream 的默认行为是将输入按空格分隔为不同的单词,并使用这些单词填充变量。在你的代码中,当你使用 input_stream >> data 时,data 只会获取第一个单词 “Hello,”,而后面的内容 “Boost.Asio and std::istream!” 并没有被读取。
std::string test;
std::cin >> test;
std::cout << test << std::endl;
输出结果:
nihao hi
nihao
为了读取整个行,你可以使用 std::getline() 函数来读取一行数据。这样,你就可以正确地读取整个输入。
修改后的代码如下:
#include
#include
int main() {
boost::asio::streambuf buffer;
std::ostream output_stream(&buffer);
output_stream << "Hello, Boost.Asio and std::istream!";
std::istream input_stream(&buffer);
std::string data;
std::getline(input_stream, data); // 使用 std::getline() 读取整行数据
std::cout << "Read from streambuf: " << data << std::endl;
return 0;
}
输出结果:
Read from streambuf: Hello, Boost.Asio and std::istream!
std::string test;
std::getline(std::cin, test);
std::cout << test << std::endl;