C++网络编程详细讲解

 更新时间:2022年10月31日 10:39:23   作者:无水先生  
计算机是通过TCP/IP协议进行互联从而进行通信的,为了把复杂的TCP/IP协议隐藏起来,更方便的实现计算机中两个程序进行通信,引出了socket这个概念

一、网络编程

尽管 Boost.Asio 可以异步处理任何类型的数据,但它主要用于网络编程。这是因为 Boost.Asio 早在添加额外的 I/O 对象之前就支持网络功能。网络函数非常适合异步操作,因为通过网络传输数据可能需要很长时间,这意味着确认和错误可能不会像发送或接收数据的函数那样快。

二、库示例

Boost.Asio 提供了许多 I/O 对象来开发网络程序。示例 32.5 使用类 boost::asio::ip::tcp::socket 与另一台计算机建立连接。此示例向网络服务器发送 HTTP 请求以下载主页。

示例 32.5。带有 boost::asio::ip::tcp::socket 的网络客户端

#include <boost/asio/io_service.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <array>
#include <string>
#include <iostream>
using namespace boost::asio;
using namespace boost::asio::ip;
io_service ioservice;
tcp::resolver resolv{ioservice};
tcp::socket tcp_socket{ioservice};
std::array<char, 4096> bytes;
void read_handler(const boost::system::error_code &ec,
  std::size_t bytes_transferred)
{
  if (!ec)
  {
    std::cout.write(bytes.data(), bytes_transferred);
    tcp_socket.async_read_some(buffer(bytes), read_handler);
  }
}
void connect_handler(const boost::system::error_code &ec)
{
  if (!ec)
  {
    std::string r =
      "GET / HTTP/1.1\r\nHost: theboostcpplibraries.com\r\n\r\n";
    write(tcp_socket, buffer(r));
    tcp_socket.async_read_some(buffer(bytes), read_handler);
  }
}
void resolve_handler(const boost::system::error_code &ec,
  tcp::resolver::iterator it)
{
  if (!ec)
    tcp_socket.async_connect(*it, connect_handler);
}
int main()
{
  tcp::resolver::query q{"theboostcpplibraries.com", "80"};
  resolv.async_resolve(q, resolve_handler);
  ioservice.run();
}

Example32.5

示例 32.5 使用了三个处理程序:connect_handler() 和 read_handler() 在建立连接并接收到数据时被调用。 resolve_handler() 用于名称解析。

因为只有在建立连接之后才能接收数据,并且因为只有在解析名称之后才能建立连接,所以各种异步操作都是在处理程序中启动的。在 resolve_handler() 中,指向从名称解析的端点的迭代器 it 与 tcp_socket 一起用于建立连接。在 connect_handler() 中,访问 tcp_socket 以发送 HTTP 请求并开始接收数据。由于所有操作都是异步的,处理程序被传递给各自的函数。根据操作,可能需要传递其他参数。例如,迭代器它指的是从名称解析的端点。数组字节用于存储接收到的数据。

在 main() 中,boost::asio::ip::tcp::resolver::query 被实例化以创建对象 q。 q 表示对名称解析器的查询,一个类型为 boost::asio::ip::tcp::resolver 的 I/O 对象。通过将 q 传递给 async_resolve(),启动异步操作来解析名称。示例 32.5 解析名称 theboostcpplibraries.com。异步操作启动后,在 I/O 服务对象上调用 run() 以将控制权传递给操作系统。

解析名称后,将调用 resolve_handler()。处理程序首先检查名称解析是否成功。在这种情况下,ec 为 0。只有这样才能访问套接字以建立连接。要连接的服务器地址由第二个参数提供,其类型为 boost::asio::ip::tcp::resolver::iterator。该参数是名称解析的结果。

对 async_connect() 的调用之后是对处理程序 connect_handler() 的调用。再次首先检查 ec 以确定是否可以建立连接。如果是这样,则在套接字上调用 async_read_some()。通过此调用,开始读取数据。接收到的数据存储在数组字节中,作为第一个参数传递给 async_read_some()。

当接收到一个或多个字节并将其复制到字节时调用 read_handler()。 std::size_t 类型的参数 bytes_transferred 包含已接收的字节数。像往常一样,处理程序应该首先检查异步操作是否成功完成。只有在这种情况下,才会将数据写入标准输出。

请注意,在将数据写入 std::cout 后,read_handler() 会再次调用 async_read_some()。这是必需的,因为您无法确定整个主页是否已在单个异步操作中下载并复制到字节中。对 async_read_some() 的重复调用和对 read_handler() 的重复调用仅在连接关闭时结束,这发生在网络服务器发送整个主页时。然后 read_handler() 在 ec 中报告错误。此时,不会向 std::cout 写入更多数据,并且不会在套接字上调用 async_read()。因为没有挂起的异步操作,程序退出。

示例 32.6。具有 boost::asio::ip::tcp::acceptor 的时间服务器

#include <boost/asio/io_service.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <string>
#include <ctime>
using namespace boost::asio;
using namespace boost::asio::ip;
io_service ioservice;
tcp::endpoint tcp_endpoint{tcp::v4(), 2014};
tcp::acceptor tcp_acceptor{ioservice, tcp_endpoint};
tcp::socket tcp_socket{ioservice};
std::string data;
void write_handler(const boost::system::error_code &ec,
  std::size_t bytes_transferred)
{
  if (!ec)
    tcp_socket.shutdown(tcp::socket::shutdown_send);
}
void accept_handler(const boost::system::error_code &ec)
{
  if (!ec)
  {
    std::time_t now = std::time(nullptr);
    data = std::ctime(&now);
    async_write(tcp_socket, buffer(data), write_handler);
  }
}
int main()
{
  tcp_acceptor.listen();
  tcp_acceptor.async_accept(tcp_socket, accept_handler);
  ioservice.run();
}

Example32.6

示例 32.6 是一个时间服务器。您可以连接 telnet 客户端以获取当前时间。之后时间服务器关闭。

时间服务器使用 I/O 对象 boost::asio::ip::tcp::acceptor 来接受来自另一个程序的传入连接。您必须初始化对象,以便它知道在哪个端口上使用哪个协议。在示例中,boost::asio::ip::tcp::endpoint 类型的变量 tcp_endpoint 用于告诉 tcp_acceptor 在端口 2014 上接受 Internet 协议版本 4 的传入连接。

接收器初始化后,调用listen() 使接收器开始监听。然后调用 async_accept() 以接受第一次连接尝试。必须将套接字作为第一个参数传递给 async_accept(),该参数将用于在新连接上发送和接收数据。

一旦另一个程序建立连接,就会调用accept_handler()。如果连接建立成功,当前时间会通过 boost::asio::async_write() 发送。此函数将 data 中的所有数据写入套接字。 boost::asio::ip::tcp::socket 还提供了成员函数 async_write_some()。此函数在至少发送一个字节时调用处理程序。然后处理程序必须检查发送了多少字节以及还需要发送多少字节。然后,它必须再次调用 async_write_some()。使用 boost::asio::async_write() 可以避免重复计算要发送的字节数和调用 async_write_some()。使用此函数开始的异步操作仅在数据中的所有字节都发送完毕后才完成。

发送数据后,会调用 write_handler()。该函数使用参数 boost::asio::ip::tcp::socket::shutdown_send 调用shutdown(),表示程序已完成通过套接字发送数据。由于没有待处理的异步操作,示例 32.6 退出。请注意,虽然 data 仅在 accept_handler() 中使用,但它不能是局部变量。数据通过 boost::asio::buffer() 引用传递到 boost::asio::async_write()。当 boost::asio::async_write() 和 accept_handler() 返回时,异步操作已开始,但尚未完成。数据必须存在,直到异步操作完成。如果数据是一个全局变量,这是有保证的。

练习

开发可以将文件从一台计算机传输到另一台计算机的客户端和服务器。当服务器启动时,它应该显示所有本地接口的 IP 地址列表并等待客户端连接。当客户端启动时,来自服务器的 IP 地址和本地文件的名称应作为命令行选项传递。客户端应将文件传输到服务器,服务器将其保存到当前工作目录。在传输期间,客户端应该显示某种进度指示器,以便用户知道传输正在进行中。使用回调实现客户端和服务器。

到此这篇关于C++网络编程详细讲解的文章就介绍到这了,更多相关C++网络编程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • c语言中字符串函数(库函数使用)和模拟实现图文教程

    c语言中字符串函数(库函数使用)和模拟实现图文教程

    C语言中对字符和字符串的处理很是频繁,但是C语言本身并没有字符串类型,这篇文章主要给大家介绍了关于c语言中字符串函数(库函数使用)和模拟实现的相关资料,需要的朋友可以参考下
    2024-01-01
  • C++ std::function的用法详解

    C++ std::function的用法详解

    这篇文章主要介绍了C++ std::function使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-10-10
  • C++多重继承二义性原理实例解析

    C++多重继承二义性原理实例解析

    这篇文章主要介绍了C++多重继承二义性原理实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • C++深入浅出讲解内存四区与new关键字的使用

    C++深入浅出讲解内存四区与new关键字的使用

    内存四区,一个非常重要的知识点,搞懂了内存四区,才能更快的去搞懂指针。我们写的C语言代码,不夸张的说,都是直接或者间接的在操作内存。C语言之所以能够开发操作系统,就是指针的存在,而指针说白了就是地址,内存地址,指针变量说白了就是存储地址的变量
    2022-05-05
  • 基于C中含有if的宏定义详解

    基于C中含有if的宏定义详解

    本篇文章是对C中含有if的宏定义进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • OpenCV实现直线拟合

    OpenCV实现直线拟合

    这篇文章主要为大家详细介绍了OpenCV实现直线拟合,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • C++单例模式应用实例

    C++单例模式应用实例

    这篇文章主要介绍了C++单例模式应用实例,详细讲述了单例模式的原理与结构,及相关的打印机应用实例,需要的朋友可以参考下
    2014-10-10
  • 四个例子说明C语言 全局变量

    四个例子说明C语言 全局变量

    这篇文章主要介绍了四个例子说明C语言 全局变量,全局变量是C语言语法和语义中一个很重要的知识点,首先它的存在意义需要从三个不同角度去理解,下面来看看这三个不同的内容分别是什么吧
    2022-04-04
  • 一篇文章了解c++中的new和delete

    一篇文章了解c++中的new和delete

    C语言提供了malloc和free两个系统函数,完成对堆内存的申请和释放,而C++则提供了两个关键字new和delete,下面这篇文章主要给大家介绍了如何通过一篇文章了解c++中new和delete的相关资料,需要的朋友可以参考下
    2021-12-12
  • C++ 实现线程安全的频率限制器(推荐)

    C++ 实现线程安全的频率限制器(推荐)

    这篇文章主要介绍了在 C++ 中实现一个线程安全的频率限制器,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05

最新评论