06-Daytime.1-A synchronous TCP daytime client
本部分的教程程序展示了如何使用asio开发简单的客户端和服务器程序。这些教程程序基于daytime协议,该协议同时支持TCP和UDP。
前三个教程程序使用TCP实现了daytime协议。
#include <array>
#include <asio.hpp>
#include <spdlog/spdlog.h>
using asio::ip::tcp;
int main(int argc, char* argv[])
{
try {
// 此应用程序的目的是访问日间服务,因此我们需要用户指定服务器。
if (argc != 2) {
spdlog::error("Usage: {} <ip>", argv[0]);
return 1;
}
asio::io_context io;
// 我们需要将应用程序参数中指定的服务器名称转换为TCP端点。
// 为此,我们使用一个ip::tcp::resolver对象。
tcp::resolver resolver(io);
// 解析器接收主机名和服务名,并将其转换为一组端点列表。
// 我们使用服务器名称(在argv[1]中指定)和服务名称(本例中为"daytime")执行解析调用。
//
// 端点列表通过类型为ip::tcp::resolver::results_type的对象返回。该对象是一个范围,
// 具有begin()和end()成员函数,可用于遍历结果。
tcp::resolver::results_type endpoints = resolver.resolve(argv[1], "daytime");
// 现在我们创建并连接套接字。上面获取的端点列表可能同时包含IPv4和IPv6端点,因此我们需要逐一尝试直到找到可用的连接。
// 这使得客户端程序不依赖于特定的IP版本。asio::connect()函数会自动为我们完成这一过程。
tcp::socket socket(io);
asio::connect(socket, endpoints);
for (;;) {
// 连接已建立。现在我们需要做的就是读取来自daytime服务的响应。
// 我们使用std::array来保存接收到的数据。asio::buffer()函数会自动确定数组的大小,以防止缓冲区溢出。
// 除了std::array,我们也可以使用char[]或std::vector。
std::array<char, 128> buf;
std::error_code error;
size_t len = socket.read_some(asio::buffer(buf), error);
// 当服务器关闭连接时,ip::tcp::socket::read_some() 函数会以 asio::error::eof 错误退出,
// 这就是我们如何知道要退出循环的。
if (error == asio::error::eof) {
break;// 连接被对方正常关闭
} else if (error) {
throw std::system_error(error);// 其他错误
}
if (len) {
spdlog::info("Received: {}", std::string(buf.data(), len));
}
}
} catch (const std::exception& e) {
spdlog::error("Error: {}", e.what());
return 1;
}
}