docs: update docs/c.md (#821)
This commit is contained in:
parent
660dfe103d
commit
ddc7f2dd47
223
docs/c.md
223
docs/c.md
@ -1301,6 +1301,229 @@ void main (){
|
||||
// 文件大小: 18 bytes
|
||||
```
|
||||
|
||||
## C 网络编程
|
||||
|
||||
### 网络编程介绍
|
||||
|
||||
C使用sockets进行网络通信。包含头文件:
|
||||
|
||||
- `#include <sys/socket.h>`: 套接字操作,如创建、绑定和监听套接字
|
||||
- `#include <arpa/inet.h>`: IP 地址转换
|
||||
- `#include <unistd.h>`: 关闭套接字等
|
||||
- `#include <netinet/in.h>`: 网络地址结构定义和相关敞亮
|
||||
|
||||
### 创建套接字
|
||||
|
||||
网络通信的第一步是创建套接字。套接字是网络通信的基础,通过它可以与远程主机进行数据交换。
|
||||
|
||||
#### 服务端
|
||||
|
||||
```cpp
|
||||
int server_fd, new_socket; // 定义服务器文件描述符和新连接的套接字
|
||||
int port = 8080; // 服务器使用的端口号
|
||||
|
||||
// 创建套接字文件描述符
|
||||
// AF_INET 表示使用 IPv4 协议,SOCK_STREAM 表示使用 TCP 协议,协议参数通常为 0(默认 TCP)
|
||||
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
||||
perror("socket failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
|
||||
#### 客户端
|
||||
|
||||
```cpp
|
||||
int sock = 0; // 客户端的套接字描述符
|
||||
struct sockaddr_in serv_addr; // 定义服务器地址结构体
|
||||
|
||||
// 创建套接字
|
||||
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
perror("Socket creation failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
|
||||
### 绑定套接字
|
||||
|
||||
服务端创建套接字后,需要将其绑定到特定的 IP 地址和端口,以便客户端能够连接。
|
||||
|
||||
#### 服务端
|
||||
|
||||
```cpp
|
||||
struct sockaddr_in address; // 定义存储地址信息的结构体
|
||||
address.sin_family = AF_INET; // 设置地址族为 IPv4
|
||||
address.sin_addr.s_addr = INADDR_ANY; // 将服务器绑定到所有可用的网络接口(即本机的所有 IP 地址)
|
||||
address.sin_port = htons(port); // 将端口号转换为网络字节序,大端模式
|
||||
|
||||
// 将套接字绑定到指定的地址和端口上
|
||||
// bind() 将服务器的文件描述符与 IP 地址和端口号进行绑定,以便客户端能够通过该地址和端口访问服务器
|
||||
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||
perror("bind failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
|
||||
### 监听和接收连接
|
||||
|
||||
服务端在绑定套接字之后,需要进入监听状态,以等待客户端的连接请求。
|
||||
|
||||
#### 服务端
|
||||
|
||||
```cpp
|
||||
// 开始监听客户端连接
|
||||
// 监听连接请求
|
||||
// listen() 函数将套接字设置为被动模式,准备接收来自客户端的连接请求
|
||||
if (listen(server_fd, 3) < 0) { // 第二个参数 3 表示连接请求的队列大小
|
||||
perror("listen failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int addrlen = sizeof(address); // 获取地址结构体的大小
|
||||
// accept() 函数会阻塞等待客户端的连接请求,一旦连接请求到来,创建一个新的套接字 new_socket 用于数据传输
|
||||
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
|
||||
perror("accept failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
|
||||
### 连接到服务端
|
||||
|
||||
客户端使用 `connect()` 函数连接到服务器的 IP 地址和端口。
|
||||
|
||||
#### 客户端
|
||||
|
||||
```cpp
|
||||
// 设置服务器地址
|
||||
serv_addr.sin_family = AF_INET; // 设置地址族为 IPv4
|
||||
serv_addr.sin_port = htons(port); // 将端口号转换为网络字节序
|
||||
|
||||
// 将 IP 地址转换为二进制并存储在 serv_addr 结构体中
|
||||
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
|
||||
perror("Invalid address/ Address not supported");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// 连接服务器
|
||||
// connect() 函数将客户端的套接字与服务器的地址绑定,从而建立连接
|
||||
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
|
||||
perror("Connection Failed");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
```
|
||||
|
||||
### 发送和接收数据
|
||||
|
||||
一旦连接建立,服务端和客户端可以通过套接字发送和接收数据。
|
||||
|
||||
#### 服务端
|
||||
|
||||
```cpp
|
||||
// 服务端从客户端接收数据
|
||||
char buffer[1024] = {0}; // 缓冲区,用于存储接收的数据
|
||||
int valread = read(new_socket, buffer, 1024); // 从客户端读取数据
|
||||
printf("Client: %s\n", buffer); // 打印接收到的客户端数据
|
||||
|
||||
// 服务端发送响应数据给客户端
|
||||
const char *response = "Hello from server"; // 响应消息
|
||||
send(new_socket, response, strlen(response), 0); // 发送数据到客户端
|
||||
printf("Server message sent\n");
|
||||
```
|
||||
|
||||
#### 客户端
|
||||
|
||||
```cpp
|
||||
// 客户端发送数据给服务端
|
||||
const char *message = "Hello from client"; // 要发送的消息
|
||||
send(sock, message, strlen(message), 0); // 发送数据到服务端
|
||||
printf("Client message sent\n");
|
||||
|
||||
// 客户端从服务端接收响应数据
|
||||
char buffer[1024] = {0}; // 缓冲区,用于存储接收到的数据
|
||||
int valread = read(sock, buffer, 1024); // 读取服务端的响应数据
|
||||
printf("Server: %s\n", buffer); // 打印接收到的服务端数据
|
||||
```
|
||||
|
||||
### 关闭套接字
|
||||
|
||||
完成通信后,双方都应关闭各自的套接字以释放资源。
|
||||
|
||||
#### 服务端
|
||||
|
||||
```cpp
|
||||
// 关闭服务端套接字
|
||||
close(new_socket); // 关闭用于数据传输的客户端套接字
|
||||
close(server_fd); // 关闭服务器的监听套接字
|
||||
|
||||
```
|
||||
|
||||
#### 客户端
|
||||
|
||||
```cpp
|
||||
// 关闭客户端套接字
|
||||
close(sock); // 关闭客户端的套接字
|
||||
```
|
||||
|
||||
## I/O多路复用
|
||||
|
||||
### 多路复用介绍
|
||||
|
||||
在网络编程中,服务端可以使用 I/O 多路复用 技术,如 `select`、`poll` 或 `epoll`。这些技术允许服务端同时监听多个文件描述符(如套接字),并在其中一个发生事件时进行处理,提升系统效率。包含头文件:
|
||||
|
||||
- `#include <sys/select.h>`: 提供 `select`
|
||||
- `#include <poll.h>`: 提供 `poll`
|
||||
- `#include <sys/epoll.h>`: 提供`epoll`
|
||||
|
||||
### 使用select
|
||||
|
||||
```c
|
||||
fd_set read_fds; // 定义文件描述符集合
|
||||
FD_ZERO(&read_fds); // 清空集合
|
||||
FD_SET(server_socket, &read_fds); // 将服务端套接字加入集合
|
||||
|
||||
int max_fd = server_socket;
|
||||
int activity = select(max_fd + 1, &read_fds, NULL, NULL, NULL); // 等待事件发生
|
||||
|
||||
if (activity < 0 && errno != EINTR) {
|
||||
perror("select error");
|
||||
}
|
||||
```
|
||||
|
||||
### 使用poll
|
||||
|
||||
```c
|
||||
struct pollfd fds[2]; // 定义文件描述符数组
|
||||
fds[0].fd = server_socket;
|
||||
fds[0].events = POLLIN; // 监听读事件
|
||||
|
||||
int poll_count = poll(fds, 2, -1); // 等待事件
|
||||
|
||||
if (poll_count < 0) {
|
||||
perror("poll error");
|
||||
}
|
||||
```
|
||||
|
||||
### 使用epoll
|
||||
|
||||
```c
|
||||
int epoll_fd = epoll_create1(0); // 创建 epoll 文件描述符
|
||||
struct epoll_event event;
|
||||
event.events = EPOLLIN;
|
||||
event.data.fd = server_socket;
|
||||
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_socket, &event) == -1) {
|
||||
perror("epoll_ctl failed");
|
||||
}
|
||||
|
||||
struct epoll_event events[10]; // 事件数组
|
||||
int event_count = epoll_wait(epoll_fd, events, 10, -1); // 等待事件发生
|
||||
|
||||
for (int i = 0; i < event_count; i++) {
|
||||
if (events[i].data.fd == server_socket) {
|
||||
// 处理服务端套接字上的事件
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
杂项
|
||||
---
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user