【C++网络编程】第1篇:网络编程基础概念
工具/C++库安装相关
注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。
一、网络模型核心:OSI与TCP/IP
1. OSI七层模型
- 分层结构:物理层 → 数据链路层 → 网络层 → 传输层 → 会话层 → 表示层 → 应用层
- 核心作用:标准化网络通信流程,各层独立工作(如传输层负责端到端连接,网络层处理IP寻址)。
2. TCP/IP四层模型
实际应用模型:
应用层(HTTP/FTP)
↓
传输层(TCP/UDP)
↓
网络层(IP/ICMP)
↓
网络接口层(以太网/Wi-Fi)
- 与OSI对比:TCP/IP更贴近实际协议(如HTTP基于TCP,DNS基于UDP)。
二、Socket编程基础
1. 什么是Socket?
定义:操作系统提供的网络通信接口,可视为“网络文件描述符”。
通信要素:
- IP地址:标识设备(IPv4如192.168.1.1,IPv6如2001:db8::1)。
- 端口号:标识应用程序(0-65535,其中0-1023为系统保留)。
- 协议类型:TCP(可靠连接)或 UDP(无连接)。
2. Socket通信流程(TCP)
- 服务器端:
socket() → bind() → listen() → accept() → recv()/send() → close()
- 客户端:
socket() → connect() → send()/recv() → close()
三、Winsock库入门
1. 初始化与清理
WSAStartup()
:初始化Winsock库,必须调用。-
WSACleanup()
:清理资源,程序退出前调用。 - 代码示例:
#include <iostream>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib") // 自动链接Winsock库
int main() {
// 1. 初始化Winsock
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) {
std::cerr << "WSAStartup failed: " << result << std::endl;
return 1;
}
// 2. 检查版本号
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
std::cerr << "Winsock 2.2 not supported!" << std::endl;
WSACleanup();
return 1;
}
std::cout << "Winsock initialized successfully!" << std::endl;
// 3. 清理Winsock
WSACleanup();
return 0;
}
2. 关键数据结构
- sockaddr_in:存储IPv4地址和端口。
struct sockaddr_in {
short sin_family; // 地址族(AF_INET)
u_short sin_port; // 端口号(网络字节序)
struct in_addr sin_addr; // IP地址
char sin_zero[8]; // 填充
};
- in_addr:32位IPv4地址。
struct in_addr {
union {
struct { u_char s_b1, s_b2, s_b3, s_b4; } S_un_b;
struct { u_short s_w1, s_w2; } S_un_w;
u_long S_addr; // 常用(如inet_addr("127.0.0.1"))
} S_un;
};
四、实战:解析IP与端口
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib") // 自动链接Winsock库
#include <ctype.h>
#include <limits.h>
int main() {
// 1. 初始化Winsock
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup failed!" << std::endl;
return 1;
}
// 2. 创建Socket
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET) {
std::cerr << "Socket creation failed: " << WSAGetLastError() << std::endl;
WSACleanup();
return 1;
}
// 3. 绑定地址和端口
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
inet_pton(AF_INET,"127.0.0.1", &(serverAddr.sin_addr.s_addr)); // 本地回环地址
serverAddr.sin_port = htons(8080); // 监听8080端口
if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
std::cerr << "Bind failed: " << WSAGetLastError() << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
// 4. 开始监听
if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
std::cerr << "Listen failed: " << WSAGetLastError() << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
std::cout << "Server started. Listening on 127.0.0.1:8080..." << std::endl;
// 5. 清理资源
closesocket(serverSocket);
WSACleanup();
return 0;
}
五、常见错误与调试
1. 错误码获取
- WSAGetLastError():获取最后一次Winsock错误。
// 接受客户端连接
sockaddr_in clientAddr;
int clientAddrLen = sizeof(clientAddr);
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrLen);
if (clientSocket == INVALID_SOCKET) {
std::cerr << "Accept failed: " << WSAGetLastError() << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}
2. 典型错误码
错误码 | 含义 |
---|---|
WSAEADDRINUSE | 端口已被占用 |
WSAECONNREFUSED | 连接被拒绝(目标未监听) |
WSAETIMEDOUT | 连接超时 |