目录
同步写write_some
同步写write_some,boost::asio提供了几种同步写的api,
write_some可以每次向指定的空间写入固定的字节数,如果写缓冲区满了,就只写一部分,返回写入的字节数。
void write_to_socket(asio::ip::tcp::socket& sock)
{
std::string buf = "Hello World";
std::size_t total_byte_written = 0;
//循环发送
//write_some 返回每次写入的字节数
//total_bytes_written是已经发送的字节数。
//每次发送buf.length()- total_bytes_written)字节数据
while (total_byte_written != buf.length()) {
total_byte_written += sock.write_some(
asio::buffer(buf.c_str() + total_byte_written,
buf.length() - total_byte_written));
}
}
配个图更通俗易懂
建立一个 TCP 连接,然后通过该连接发送数据
//建立一个 TCP 连接,然后通过该连接发送数据
int send_data_by_write_some() {
std::string raw_ip_address = "192.168.3.11";
unsigned short port_num = 3333;
try {
asio::ip::tcp::endpoint ep(asio::ip::address::from_string(raw_ip_address), port_num);
//创建了一个 TCP 终端点对象 ep,用于表示目标服务器的地址和端口。
//asio::io_service ios;
asio::io_context ioc;
//创建了一个 I/O 上下文对象 ioc,用于管理异步 I/O 操作。
asio::ip::tcp::socket sock(ioc, ep.protocol());
//创建了一个 TCP 套接字对象 sock,并将其与上下文 ioc 关联,以及与终端点 ep 关联。
sock.connect(ep);
//尝试通过套接字 sock 连接到目标服务器,即执行 TCP 连接操作
write_to_socket(sock);
//调用了一个名为 write_to_socket 的函数,该函数用于向套接字写入数据。
}
catch (system::system_error& e) {
std::cout << "Error occured!Error code=" << e.code()
<< ".Message:" << e.what();
return e.code().value();
}
return 0;
//如果出现系统错误,将会打印错误消息,其中包括错误代码和错误描述,
//然后返回相应的错误代码值。如果一切顺利,函数将返回 0 表示成功。
//整体来说,这段代码的作用是建立一个 TCP 连接,然后通过该连接发送数据。
}
同步写send
同步写sendwrite_some使用起来比较麻烦,需要多次调用,asio提供了send函数。
send函数会一次性将buffer中的内容发送给对端,
如果有部分字节因为发送缓冲区满无法发送,则阻塞等待,直到发送缓冲区可用,则继续发送完成。
//同步写sendwrite_some使用起来比较麻烦,需要多次调用,asio提供了send函数。
// send函数会一次性将buffer中的内容发送给对端,
// 如果有部分字节因为发送缓冲区满无法发送,则阻塞等待,直到发送缓冲区可用,则继续发送完成。
int send_data_by_send() {
std::string raw_ip_address = "192.168.3.11";
unsigned short port_num = 3333;
try {
asio::ip::tcp::endpoint ep(asio::ip::address::from_string(raw_ip_address), port_num);
//asio::io_service ios;
asio::io_context ioc;
asio::ip::tcp::socket sock(ioc, ep.protocol());
sock.connect(ep);
std::string buf = "Hello World!";
int send_length = sock.send(asio::buffer(buf.c_str(),buf.length()));
//asio::buffer(buf.c_str(), buf.length()): 这里使用 asio::buffer 函数将字符串转换为 Boost.Asio 缓冲区对象。
// asio::buffer 函数接受指向数据的指针和数据的长度作为参数,并返回一个表示数据的缓冲区对象。
//sock.send 是 Boost.Asio 库中用于通过套接字发送数据的函数。
// 它的作用是将指定的数据发送到已连接的远程端点(通常是另一个计算机上的网络服务)。
//sock.send(...): 这是通过 Boost.Asio 套接字对象 sock 发送数据的操作。
// 在这里,将使用 send 函数向连接的对端发送数据。
// 作为参数传递给 send 函数的是要发送的数据的缓冲区对象。
//int send_length = ...: 将发送的数据的长度存储在整型变量 send_length 中。
// send_length 变量将会接收 send 函数返回的结果,即实际发送的数据的字节数。
if (send_length <= 0) {
return 0;
}
//如果发送的数据长度小于等于 0,这可能表示发送过程中出现了错误或者没有发送任何数据。因此,通过检查 send_length 的值,
// 如果发现数据发送长度小于等于 0,代码会立即返回 0,表示发送操作失败或者没有发送任何数据。
}
catch (system::system_error& e) {
std::cout << "Error occured!Error code=" << e.code()
<< ".Message:" << e.what();
return e.code().value();
}
return 0;
}
同步写write
在 Boost.Asio 中,除了使用套接字对象的 send 函数来发送数据外,还可以使用 asio::write 函数来发送数据。下面是关于 asio::write 的说明:
SyncWriteStream
:表示支持同步写操作的流对象,通常是一个套接字对象。ConstBufferSequence
:表示要发送的数据的缓冲区序列。
asio::write
函数将指定的数据序列写入到给定的流对象中,对于套接字对象来说,即发送数据到远程端点。与 send
函数不同的是,asio::write
函数会一直尝试写入所有数据,直到所有数据都被写入或者发生错误。
int send_length = asio::write(sock, asio::buffer(buf.c_str(), buf.length()))
这行代码使用了 asio::write
函数来发送字符串 buf
中的数据,并将实际发送的数据长度存储在 send_length
变量中。这个操作与之前介绍的 sock.send
函数实现的功能类似,只是使用了不同的函数接口。
//同步写write,类似send方法,asio还提供了一个write函数,可以一次性将所有数据发送给对端,
// 如果发送缓冲区满了则阻塞,直到发送缓冲区可用,将数据发送完成。
int send_data_by_write() {
std::string raw_ip_address = "192.168.3.11";
unsigned short port_num = 3333;
try {
asio::ip::tcp::endpoint ep(asio::ip::address::from_string(raw_ip_address), port_num);
//asio::io_service ios;
asio::io_context ioc;
asio::ip::tcp::socket sock(ioc, ep.protocol());
sock.connect(ep);
std::string buf = "Hello World!";
int send_length = asio::write(sock, asio::buffer(buf.c_str(), buf.length()));
//asio::write 函数将指定的数据序列写入到给定的流对象中,对于套接字对象来说,
// 即发送数据到远程端点。与 send 函数不同的是,
// asio::write 函数会一直尝试写入所有数据,直到所有数据都被写入或者发生错误。
//在使用 asio::write 函数时,你需要提供要写入的流对象(通常是套接字对象)
// 以及表示要发送的数据的缓冲区序列。函数会阻塞当前线程,直到所有数据都被写入或者发生错误。
//总的来说,asio::write 提供了一种方便的方式来将数据写入到同步写流对象中
// 它对于发送数据到套接字非常有用,同时也简化了发送数据的操作。
if (send_length <= 0) {
return 0;
}
}
catch (system::system_error& e) {
std::cout << "Error occured!Error code=" << e.code()
<< ".Message:" << e.what();
return e.code().value();
}
return 0;
}
同步读read_some
同步读和同步写类似,提供了读取指定字节数的接口read_some
//同步读read_some,同步读和同步写类似,提供了读取指定字节数的接口read_some
std::string read_from_socket(asio::ip::tcp::socket& sock) {
const unsigned char MESSAGE_SIZE = 7;
char buf[MESSAGE_SIZE];
std::size_t total_byte_read = 0;
while (total_byte_read != MESSAGE_SIZE) {
total_byte_read += sock.read_some(asio::buffer(buf + total_byte_read,
MESSAGE_SIZE - total_byte_read));
}
return std::string(buf, total_byte_read);
}
作为socket把读的流程串联起来
//作为socket把读的流程串联起来
int read_data_by_read_some() {
std::string raw_ip_address = "127.0.0.1";
unsigned short port_num = 3333;
try {
asio::ip::tcp::endpoint ep(asio::ip::address::from_string(raw_ip_address), port_num);
asio::io_context ioc;
asio::ip::tcp::socket sock(ioc, ep.protocol());
sock.connect(ep);
read_from_socket(sock);
//注意此read_from_socket(sock)是上面那个代码段,我们自定义的函数
}
catch (system::system_error& e) {
std::cout << "Error occured!Error code=" << e.code()
<< ".Message:" << e.what();
return e.code().value();
}
}
同步读receive
可以一次性同步接收对方发送的数据
//同步读receive,可以一次性同步接收对方发送的数据
int read_data_by_receive() {
std::string raw_ip_address = "127.0.0.1";
unsigned short port_num = 3333;
try {
asio::ip::tcp::endpoint ep(asio::ip::address::from_string(raw_ip_address), port_num);
asio::io_context ioc;
asio::ip::tcp::socket sock(ioc, ep.protocol());
sock.connect(ep);
const unsigned char BUFF_SIZE = 7;
char buffer_receive[BUFF_SIZE];
int receive_length = sock.receive(asio::buffer(buffer_receive,BUFF_SIZE));
//在 Boost.Asio 中,sock.receive 函数用于从套接字接收数据。
//两个参数:buffer:要接收数据的缓冲区对象,可以使用 asio::buffer 来创建。
//flags:可选的附加标志参数,默认为0,表示无特殊标志。
//
//sock.receive 函数将从已连接的远程端点接收数据,并将接收到的数据存储在指定的缓冲区中。
// 它是一个同步操作,会阻塞当前线程直到数据接收完成或者出现错误。
//函数会返回实际接收的字节数,如果发生错误则返回一个特定的错误码。
// 你可以通过检查返回的字节数来确定实际接收到了多少数据。
//在使用 sock.receive 函数时,你需要提供一个缓冲区对象来存储接收到的数据,
// 并可以选择是否使用附加的标志参数。接收到的数据将会被存储在指定的缓冲区中,供后续处理和分析。
//总的来说,sock.receive 函数是用于从套接字接收数据的方法,
// 它允许你接收远程端点发送过来的数据,并将其存储在指定的缓冲区中。
if (receive_length <= 0) {
std::cout << "receive failed!"<< std::endl;
}
}
catch (system::system_error& e) {
std::cout << "Error occured!Error code=" << e.code()
<< ".Message:" << e.what();
return e.code().value();
}
}
同步读read
可以一次性同步读取对方发送的数据
//同步读read,可以一次性同步读取对方发送的数据
int read_data_by_read() {
std::string raw_ip_address = "127.0.0.1";
unsigned short port_num = 3333;
try {
asio::ip::tcp::endpoint ep(asio::ip::address::from_string(raw_ip_address), port_num);
asio::io_context ioc;
asio::ip::tcp::socket sock(ioc, ep.protocol());
sock.connect(ep);
const unsigned char BUFF_SIZE = 7;
char buffer_receive[BUFF_SIZE];
int receive_length = asio::read(sock,asio::buffer(buffer_receive, BUFF_SIZE));
//在 Boost.Asio 中,asio::read 是用于从流对象(例如套接字)中读取数据的函数。
//SyncReadStream:表示支持同步读操作的流对象,通常是一个套接字对象。
//MutableBufferSequence:表示用于存储接收数据的可变缓冲区序列。
//asio::read 函数将从给定的流对象中读取数据,并将其存储在指定的缓冲区中。
// 与 receive 函数不同的是,asio::read 函数会一直尝试读取指定数量的数据,直到所有数据都被读取或者发生错误。
//在使用 asio::read 函数时,你需要提供要读取的流对象(通常是套接字对象),
// 以及用于存储接收数据的可变缓冲区序列。函数会阻塞当前线程,直到指定数量的数据被读取或者发生错误。
//asio::read 函数返回实际读取的字节数,如果发生错误则返回一个特定的错误码。你可以通过检查返回的字节数来确定实际读取了多少数据。
//总的来说,asio::read 提供了一种方便的方式来从流对象中读取数据,对于接收套接字中的数据非常有用,同时也简化了读取数据的操作。
if (receive_length <= 0) {
std::cout << "receive failed!" << std::endl;
}
}
catch (system::system_error& e) {
std::cout << "Error occured!Error code=" << e.code()
<< ".Message:" << e.what();
return e.code().value();
}
}
读取直到指定字符
我们可以一直读取,直到读取指定字符结束
std::string read_data_by_until(asio::ip::tcp::socket& sock) {
asio::streambuf buf;
asio::read_until(sock, buf, '\n');
std::string message;
//由于缓冲区 'buf' 可能包含 '\n' 符号之后的其他数据,
//我们需要解析缓冲区并提取出分隔符之前字符。
std::istream input_stream(&buf);
std::getline(input_stream, message);
return message;
}