Skip to content

Commit ddc7f2d

Browse files
authored
docs: update docs/c.md (#821)
1 parent 660dfe1 commit ddc7f2d

File tree

1 file changed

+223
-0
lines changed

1 file changed

+223
-0
lines changed

docs/c.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,229 @@ void main (){
13011301
// 文件大小: 18 bytes
13021302
```
13031303

1304+
## C 网络编程
1305+
1306+
### 网络编程介绍
1307+
1308+
C使用sockets进行网络通信。包含头文件:
1309+
1310+
- `#include <sys/socket.h>`: 套接字操作,如创建、绑定和监听套接字
1311+
- `#include <arpa/inet.h>`: IP 地址转换
1312+
- `#include <unistd.h>`: 关闭套接字等
1313+
- `#include <netinet/in.h>`: 网络地址结构定义和相关敞亮
1314+
1315+
### 创建套接字
1316+
1317+
网络通信的第一步是创建套接字。套接字是网络通信的基础,通过它可以与远程主机进行数据交换。
1318+
1319+
#### 服务端
1320+
1321+
```cpp
1322+
int server_fd, new_socket; // 定义服务器文件描述符和新连接的套接字
1323+
int port = 8080; // 服务器使用的端口号
1324+
1325+
// 创建套接字文件描述符
1326+
// AF_INET 表示使用 IPv4 协议,SOCK_STREAM 表示使用 TCP 协议,协议参数通常为 0(默认 TCP)
1327+
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
1328+
perror("socket failed");
1329+
exit(EXIT_FAILURE);
1330+
}
1331+
```
1332+
1333+
#### 客户端
1334+
1335+
```cpp
1336+
int sock = 0; // 客户端的套接字描述符
1337+
struct sockaddr_in serv_addr; // 定义服务器地址结构体
1338+
1339+
// 创建套接字
1340+
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
1341+
perror("Socket creation failed");
1342+
exit(EXIT_FAILURE);
1343+
}
1344+
```
1345+
1346+
### 绑定套接字
1347+
1348+
服务端创建套接字后,需要将其绑定到特定的 IP 地址和端口,以便客户端能够连接。
1349+
1350+
#### 服务端
1351+
1352+
```cpp
1353+
struct sockaddr_in address; // 定义存储地址信息的结构体
1354+
address.sin_family = AF_INET; // 设置地址族为 IPv4
1355+
address.sin_addr.s_addr = INADDR_ANY; // 将服务器绑定到所有可用的网络接口(即本机的所有 IP 地址)
1356+
address.sin_port = htons(port); // 将端口号转换为网络字节序,大端模式
1357+
1358+
// 将套接字绑定到指定的地址和端口上
1359+
// bind() 将服务器的文件描述符与 IP 地址和端口号进行绑定,以便客户端能够通过该地址和端口访问服务器
1360+
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
1361+
perror("bind failed");
1362+
exit(EXIT_FAILURE);
1363+
}
1364+
```
1365+
1366+
### 监听和接收连接
1367+
1368+
服务端在绑定套接字之后,需要进入监听状态,以等待客户端的连接请求。
1369+
1370+
#### 服务端
1371+
1372+
```cpp
1373+
// 开始监听客户端连接
1374+
// 监听连接请求
1375+
// listen() 函数将套接字设置为被动模式,准备接收来自客户端的连接请求
1376+
if (listen(server_fd, 3) < 0) { // 第二个参数 3 表示连接请求的队列大小
1377+
perror("listen failed");
1378+
exit(EXIT_FAILURE);
1379+
}
1380+
1381+
int addrlen = sizeof(address); // 获取地址结构体的大小
1382+
// accept() 函数会阻塞等待客户端的连接请求,一旦连接请求到来,创建一个新的套接字 new_socket 用于数据传输
1383+
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
1384+
perror("accept failed");
1385+
exit(EXIT_FAILURE);
1386+
}
1387+
```
1388+
1389+
### 连接到服务端
1390+
1391+
客户端使用 `connect()` 函数连接到服务器的 IP 地址和端口。
1392+
1393+
#### 客户端
1394+
1395+
```cpp
1396+
// 设置服务器地址
1397+
serv_addr.sin_family = AF_INET; // 设置地址族为 IPv4
1398+
serv_addr.sin_port = htons(port); // 将端口号转换为网络字节序
1399+
1400+
// 将 IP 地址转换为二进制并存储在 serv_addr 结构体中
1401+
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
1402+
perror("Invalid address/ Address not supported");
1403+
exit(EXIT_FAILURE);
1404+
}
1405+
1406+
// 连接服务器
1407+
// connect() 函数将客户端的套接字与服务器的地址绑定,从而建立连接
1408+
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
1409+
perror("Connection Failed");
1410+
exit(EXIT_FAILURE);
1411+
}
1412+
```
1413+
1414+
### 发送和接收数据
1415+
1416+
一旦连接建立,服务端和客户端可以通过套接字发送和接收数据。
1417+
1418+
#### 服务端
1419+
1420+
```cpp
1421+
// 服务端从客户端接收数据
1422+
char buffer[1024] = {0}; // 缓冲区,用于存储接收的数据
1423+
int valread = read(new_socket, buffer, 1024); // 从客户端读取数据
1424+
printf("Client: %s\n", buffer); // 打印接收到的客户端数据
1425+
1426+
// 服务端发送响应数据给客户端
1427+
const char *response = "Hello from server"; // 响应消息
1428+
send(new_socket, response, strlen(response), 0); // 发送数据到客户端
1429+
printf("Server message sent\n");
1430+
```
1431+
1432+
#### 客户端
1433+
1434+
```cpp
1435+
// 客户端发送数据给服务端
1436+
const char *message = "Hello from client"; // 要发送的消息
1437+
send(sock, message, strlen(message), 0); // 发送数据到服务端
1438+
printf("Client message sent\n");
1439+
1440+
// 客户端从服务端接收响应数据
1441+
char buffer[1024] = {0}; // 缓冲区,用于存储接收到的数据
1442+
int valread = read(sock, buffer, 1024); // 读取服务端的响应数据
1443+
printf("Server: %s\n", buffer); // 打印接收到的服务端数据
1444+
```
1445+
1446+
### 关闭套接字
1447+
1448+
完成通信后,双方都应关闭各自的套接字以释放资源。
1449+
1450+
#### 服务端
1451+
1452+
```cpp
1453+
// 关闭服务端套接字
1454+
close(new_socket); // 关闭用于数据传输的客户端套接字
1455+
close(server_fd); // 关闭服务器的监听套接字
1456+
1457+
```
1458+
1459+
#### 客户端
1460+
1461+
```cpp
1462+
// 关闭客户端套接字
1463+
close(sock); // 关闭客户端的套接字
1464+
```
1465+
1466+
## I/O多路复用
1467+
1468+
### 多路复用介绍
1469+
1470+
在网络编程中,服务端可以使用 I/O 多路复用 技术,如 `select``poll``epoll`。这些技术允许服务端同时监听多个文件描述符(如套接字),并在其中一个发生事件时进行处理,提升系统效率。包含头文件:
1471+
1472+
- `#include <sys/select.h>`: 提供 `select`
1473+
- `#include <poll.h>`: 提供 `poll`
1474+
- `#include <sys/epoll.h>`: 提供`epoll`
1475+
1476+
### 使用select
1477+
1478+
```c
1479+
fd_set read_fds; // 定义文件描述符集合
1480+
FD_ZERO(&read_fds); // 清空集合
1481+
FD_SET(server_socket, &read_fds); // 将服务端套接字加入集合
1482+
1483+
int max_fd = server_socket;
1484+
int activity = select(max_fd + 1, &read_fds, NULL, NULL, NULL); // 等待事件发生
1485+
1486+
if (activity < 0 && errno != EINTR) {
1487+
perror("select error");
1488+
}
1489+
```
1490+
1491+
### 使用poll
1492+
1493+
```c
1494+
struct pollfd fds[2]; // 定义文件描述符数组
1495+
fds[0].fd = server_socket;
1496+
fds[0].events = POLLIN; // 监听读事件
1497+
1498+
int poll_count = poll(fds, 2, -1); // 等待事件
1499+
1500+
if (poll_count < 0) {
1501+
perror("poll error");
1502+
}
1503+
```
1504+
1505+
### 使用epoll
1506+
1507+
```c
1508+
int epoll_fd = epoll_create1(0); // 创建 epoll 文件描述符
1509+
struct epoll_event event;
1510+
event.events = EPOLLIN;
1511+
event.data.fd = server_socket;
1512+
1513+
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_socket, &event) == -1) {
1514+
perror("epoll_ctl failed");
1515+
}
1516+
1517+
struct epoll_event events[10]; // 事件数组
1518+
int event_count = epoll_wait(epoll_fd, events, 10, -1); // 等待事件发生
1519+
1520+
for (int i = 0; i < event_count; i++) {
1521+
if (events[i].data.fd == server_socket) {
1522+
// 处理服务端套接字上的事件
1523+
}
1524+
}
1525+
```
1526+
13041527
杂项
13051528
---
13061529

0 commit comments

Comments
 (0)