通过网络编程实现基于网络的应用程序,实现计算机之间的通信和数据交换。
- 套接字(Socket):套接字是网络编程中用于通信的一种抽象概念。在C++中使用<sys/socket.h>头文件中定义的套接字函数来创建和操作套接字。目的是开发采用套接字通信的C/S网络应用程序。
1. 套接字是一个主机本地应用程序所创建的, 为操作系统所控制的接口 (“门”) .
2. 应用进程通过这个接口,使用传输层提供的服务, 跨网络发送(接收)报文到(从)其他应用进程。
client/server模式
通过Socket API,提供传输层的2类传输服务:
不可靠的数据报传输、可靠的字节流传输
1. TCP进行套接字编程
Socket: 应用进程和传输层协议(UCP or TCP)之间的门。
TCP服务: 从1个进程到另1个进程的字节流的可靠传输。
Socket是从UNIX的I/O命令集发展而来的。Socket为上层实体提供一种透明的访问网络的能力,本质上说是传输层的服务原语。
TCP 中的套接字系统调用
编辑 客户必须初始联系服务器 服务器进程必须先运行
服务器进程必须创建套接字 (门) 来迎候客户的初始联系
客户如何初始联系服务器
创建客户本地TCP socket
指定服务器进程的IP地址, 端口号
一旦客户创建套接字, 客户TCP 就发起3次握手并建立与服务器 TCP的连接
一旦客户初始联系(敲门)服务器, 服务器TCP为服务器进程创建1个新的socket 与客户进程通信
允许服务器与多个客户通信
源端口号被用来区分客户
从应用程序的角度来看TCP 为客户和服务器提供了可靠的, 顺序的,字节流的传输 (“管道”) 。
2、IP地址和端口号:每个主机在网络上都有一个唯一的IP地址,用于标识主机。端口号是为了区分一个主机上的不同应用程序而存在的。在C++中,我们可以使用<arpa/inet.h>头文件中的函数来处理IP地址和端口号。
3、客户端和服务器:网络应用程序通常分为客户端和服务器端。客户端向服务器请求服务,而服务器则提供服务。客户端和服务器之间通过套接字进行通信。
使用C++进行网络编程
在C++中,可以使用Socket库来实现网络编程。以下展示了如何使用C++创建一个基于网络的应用程序:
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string>
#include <thread>
#include <vector>
// 客户端处理线程函数
void clientHandler(int clientSocket)
{
// 接收客户端用户名
char username[1024];
recv(clientSocket, username, sizeof(username), 0);
// 向其他客户端广播新用户加入信息
std::string joinMessage = username;
joinMessage += "加入了聊天室\n";
std::cout << joinMessage;
for (auto socket : connectedClients)
{
if (socket != clientSocket)
{
send(socket, joinMessage.c_str(), joinMessage.length(), 0);
}
}
// 接收和转发消息
while (true)
{
char buffer[1024] = {0};
int bytesRead = recv(clientSocket, buffer, sizeof(buffer), 0);
if (bytesRead <= 0)
{
// 客户端断开连接
close(clientSocket);
// 向其他客户端广播用户离开信息
std::string leaveMessage = username;
leaveMessage += "离开了聊天室\n";
std::cout << leaveMessage;
for (auto socket : connectedClients)
{
if (socket != clientSocket)
{
send(socket, leaveMessage.c_str(), leaveMessage.length(), 0);
}
}
// 从已连接客户端列表中移除该客户端
connectedClients.erase(std::find(connectedClients.begin(), connectedClients.end(), clientSocket));
break;
}
// 向其他客户端广播消息
std::string message = username;
message += ": ";
message += buffer;
std::cout << message;
for (auto socket : connectedClients)
{
if (socket != clientSocket)
{
send(socket, message.c_str(), message.length(), 0);
}
}
}
}
int main()
{
// 创建服务器套接字
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == -1)
{
std::cerr << "无法创建套接字" << std::endl;
return -1;
}
// 定义服务器地址
struct sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(8080);
serverAddress.sin_addr.s_addr = INADDR_ANY;
// 绑定套接字到服务器地址
if (bind(serverSocket, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) == -1)
{
std::cerr << "绑定套接字失败" << std::endl;
return -1;
}
// 监听连接请求
if (listen(serverSocket, SOMAXCONN) == -1)
{
std::cerr << "监听连接请求失败" << std::endl;
return -1;
}
std::cout << "聊天室服务器已启动,等待客户端连接..." << std::endl;
// 接受客户端连接并创建处理线程
std::vector<std::thread> clientHandlers;
while (true)
{
struct sockaddr_in clientAddress;
socklen_t clientAddressSize = sizeof(clientAddress);
int clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddress, &clientAddressSize);
if (clientSocket == -1)
{
std::cerr << "无法接受客户端连接" << std::endl;
return -1;
}
// 将客户端套接字添加到已连接客户端列表中
connectedClients.push_back(clientSocket);
// 创建客户端处理线程
std::thread handlerThread(clientHandler, clientSocket);
handlerThread.detach();
}
// 关闭服务器套接字
close(serverSocket);
return 0;
}
Boost:Beast库是一个作为 Boost 库一部分的 C++ 库,它提供了低级网络编程接口,专为HTTP和WebSocket设计。此库旨在让开发者能够高效地构建高性能的网络服务而无需过多关注底层细节。通过利用C++的现代特性,Beast使得在TCP上实现标准Web协议变得更加直观和强大。
1. 项目目录结构及介绍
Boost.Beast 的GitHub仓库大致结构如下:
目录解析:
docs : 提供详细的API文档和教程。
example : 实际可用的小型应用程序示例,帮助理解如何使用Beast进行编程。
include : 包含所有Beast库的头文件,是开发者日常工作中频繁访问的区域。
test : 包含单元测试,对于了解库的内部逻辑和功能覆盖度非常有用。
2. 项目的启动文件介绍
Boost.Beast本身不直接提供一个“启动文件”,因为它的使用依赖于嵌入到用户自己的应用程序中。然而,在example目录下,有多个入门级别的示例,如http.server.basic, http.client.sync等,这些可以视作“启动点”去学习如何开始编写基于Beast的应用。
例如,从http.server.basic开始,主要启动逻辑通常位于一个main函数内,该函数设置服务器监听端口,接收请求并响应。这通常涉及到创建Beast的核心对象,比如http::server类实例(虽然实际的类名可能有所不同,具体取决于你的实现细节)。
3. 项目的配置文件介绍
Boost.Beast作为一个库,并没有要求特定的配置文件。其配置主要通过编码方式实现,即在代码中设定参数、端口号、日志级别等。对于应用程序来说,如果需要配置Beast的行为,通常是自定义的配置逻辑,通过环境变量、命令行参数或外部配置文件(XML、JSON、YAML等)来读取,并在应用初始化阶段处理这些配置信息。
例如,如果你的应用需要动态指定服务器地址和端口,你会在代码里添加解析这类信息的逻辑,而不是Beast库直接支持的配置文件读取机制。
Boost.Asio是一个跨平台的C++库,用于网络和底层I/O编程,可以在I/O对象(如socket)上执行同步和异步操作。
该库可以让C++异步地处理数据,且平台独立。异步数据处理就是指,任务触发后不需要等待它们完成。 相反,Boost.Asio 会在任务完成时触发一个应用。 异步任务的主要优点在于,在等待任务完成时不需要阻塞应用程序,可以去执行其它任务。
异步任务的典型例子是网络应用。如果数据被发送出去了,比如发送至 Internet,通常需要知道数据是否发送成功。如果没有一个象 Boost.Asio这样的库,就必须对函数的返回值进行求值。但是,这样就要求待至所有数据发送完毕,并得到一个确认或是错误代码。而使用 Boost.Asio,这个过程被分为两个单独的步骤:第一步是作为一个异步任务开始数据传输。一旦传输完成,不论成功或是错误,应用程序都会在第二步中得到关于相应的结果通知。主要的区别在于,应用程序无需阻塞至传输完成,而可以在这段时间里执行其它操作。
I/O 服务与 I/O 对象
使用Boost.Asio进行异步数据处理的应用程序基于两个概念:I/O 服务和 I/O 对象。I/O 服务抽象了操作系统的接口,允许第一时间进行异步数据处理,而 I/O 对象则用于初始化特定的操作。鉴于Boost.Asio只提供了一个名为
boost::asio::io_service 的类作为 I/O 服务,它针对所支持的每一个操作系统都分别实现了优化的类,另外库中还包含了针对不同 I/O 对象的几个类。 其中,类 boost::asio::ip::tcp::socket 用于通过网络发送和接收数据,而类 boost::asio::deadline_timer 则提供了一个计时器,用于测量某个固定时间点到来或是一段指定的时长过去了。