C/C++ Linux 开发教程(GCC + CMake) / 网络编程基础(Socket/epoll)
网络编程基础(Socket/epoll)
1. TCP Socket 编程基本流程
服务器端 客户端
┌───────────────┐ ┌───────────────┐
│ socket() │ │ socket() │
│ ↓ │ │ ↓ │
│ bind() │ │ connect() │
│ ↓ │ 三次握手 │ ↓ │
│ listen() │ ←────────────→ │ │
│ ↓ │ │ ↓ │
│ accept() │ │ send/recv │
│ ↓ │ 数据交换 │ ↓ │
│ send/recv │ ←────────────→ │ │
│ ↓ │ │ ↓ │
│ close() │ 四次挥手 │ close() │
└───────────────┘ ←────────────→ └───────────────┘
2. 基本 TCP 服务器
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
// 1. 创建 socket
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket");
return 1;
}
// 设置 SO_REUSEADDR(避免 "Address already in use")
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 2. 绑定地址
struct sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY; // 监听所有接口
addr.sin_port = htons(8080); // 端口 8080
if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind");
close(server_fd);
return 1;
}
// 3. 监听
if (listen(server_fd, SOMAXCONN) < 0) {
perror("listen");
close(server_fd);
return 1;
}
std::cout << "服务器监听端口 8080...\n";
// 4. 接受连接
struct sockaddr_in client_addr{};
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd < 0) {
perror("accept");
close(server_fd);
return 1;
}
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));
std::cout << "客户端连接: " << client_ip << ":" << ntohs(client_addr.sin_port) << "\n";
// 5. 收发数据
char buffer[1024];
while (true) {
memset(buffer, 0, sizeof(buffer));
ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
if (n <= 0) {
if (n == 0) std::cout << "客户端断开连接\n";
else perror("recv");
break;
}
std::cout << "收到: " << buffer;
// 回显
send(client_fd, buffer, n, 0);
}
// 6. 关闭
close(client_fd);
close(server_fd);
return 0;
}
编译运行:
g++ -std=c++17 -o server server.cpp && ./server
3. TCP 客户端
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
// 1. 创建 socket
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0) {
perror("socket");
return 1;
}
// 2. 连接服务器
struct sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
if (connect(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
perror("connect");
close(sock_fd);
return 1;
}
std::cout << "已连接服务器\n";
// 3. 收发数据
const char* messages[] = {"Hello\n", "World\n", "你好\n"};
for (const auto* msg : messages) {
send(sock_fd, msg, strlen(msg), 0);
char buffer[1024] = {};
ssize_t n = recv(sock_fd, buffer, sizeof(buffer) - 1, 0);
if (n > 0) {
std::cout << "服务器回复: " << buffer;
}
sleep(1);
}
// 4. 关闭
close(sock_fd);
std::cout << "连接已关闭\n";
return 0;
}
4. UDP Socket
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// UDP 服务器(接收端)
void udp_server() {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) { perror("socket"); return; }
struct sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(9090);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind"); close(fd); return;
}
std::cout << "UDP 服务器监听端口 9090\n";
char buffer[1024];
struct sockaddr_in client_addr{};
socklen_t client_len = sizeof(client_addr);
while (true) {
ssize_t n = recvfrom(fd, buffer, sizeof(buffer) - 1, 0,
(struct sockaddr*)&client_addr, &client_len);
if (n < 0) { perror("recvfrom"); break; }
buffer[n] = '\0';
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, ip, sizeof(ip));
std::cout << "来自 " << ip << ":" << ntohs(client_addr.sin_port)
<< " → " << buffer;
// 回复
sendto(fd, buffer, n, 0, (struct sockaddr*)&client_addr, client_len);
}
close(fd);
}
// UDP 客户端(发送端)
void udp_client() {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) { perror("socket"); return; }
struct sockaddr_in server_addr{};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(9090);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
const char* msg = "UDP Hello\n";
sendto(fd, msg, strlen(msg), 0,
(struct sockaddr*)&server_addr, sizeof(server_addr));
char buffer[1024] = {};
ssize_t n = recvfrom(fd, buffer, sizeof(buffer) - 1, 0, nullptr, nullptr);
if (n > 0) {
buffer[n] = '\0';
std::cout << "服务器回复: " << buffer;
}
close(fd);
}
| 特性 | TCP | UDP |
|---|
| 连接 | 面向连接(三次握手) | 无连接 |
| 可靠性 | 可靠(保证送达、顺序) | 不可靠 |
| 速度 | 相对较慢 | 快 |
| 适用场景 | HTTP、文件传输、数据库 | 视频流、DNS、游戏 |
| API | send / recv | sendto / recvfrom |
5. Socket 地址结构与字节序转换
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
int main() {
// sockaddr_in 结构
struct sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080); // 主机字节序 → 网络字节序
inet_pton(AF_INET, "192.168.1.100", &addr.sin_addr);
// 字节序转换
uint16_t host_port = 8080;
uint16_t net_port = htons(host_port); // host to network short
uint16_t back = ntohs(net_port); // network to host short
uint32_t host_val = 0x12345678;
uint32_t net_val = htonl(host_val); // host to network long
uint32_t host_back = ntohl(net_val); // network to host long
std::cout << "端口: " << host_port << " → " << net_port << " → " << back << "\n";
std::cout << "0x12345678 → 0x" << std::hex << net_val
<< " → 0x" << host_back << "\n";
// IP 地址转换
// 文本 → 二进制
struct in_addr ip;
inet_pton(AF_INET, "192.168.1.100", &ip);
std::cout << "IP (binary): 0x" << std::hex << ip.s_addr << "\n";
// 二进制 → 文本
char ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &ip, ip_str, sizeof(ip_str));
std::cout << "IP (text): " << ip_str << "\n";
// 旧函数(不推荐,但代码中常见)
// inet_addr("192.168.1.100") → in_addr_t
// inet_ntoa(ip) → char*
return 0;
}
| 函数 | 作用 |
|---|
htons() | 16 位主机序 → 网络序 |
ntohs() | 16 位网络序 → 主机序 |
htonl() | 32 位主机序 → 网络序 |
ntohl() | 32 位网络序 → 主机序 |
inet_pton() | 文本 IP → 二进制(推荐) |
inet_ntop() | 二进制 → 文本 IP(推荐) |
⚠️ 注意:Linux x86/x86_64 是小端序(little-endian),网络字节序是大端序(big-endian),所以必须转换。
6. 多客户端处理:fork / select / poll
fork 多进程模型
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/wait.h>
void handle_client(int client_fd) {
char buffer[1024];
while (true) {
ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
if (n <= 0) break;
buffer[n] = '\0';
send(client_fd, buffer, n, 0);
}
close(client_fd);
}
int main() {
signal(SIGCHLD, SIG_IGN); // 自动回收子进程
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(server_fd, 128);
std::cout << "fork 模型服务器启动\n";
while (true) {
struct sockaddr_in client_addr{};
socklen_t len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
if (client_fd < 0) continue;
pid_t pid = fork();
if (pid == 0) {
close(server_fd);
handle_client(client_fd);
exit(0);
}
close(client_fd);
}
return 0;
}
select 多路复用
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <vector>
int main() {
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(server_fd, 128);
fd_set readfds;
int max_fd = server_fd;
std::vector<int> clients;
std::cout << "select 模型服务器启动\n";
while (true) {
FD_ZERO(&readfds);
FD_SET(server_fd, &readfds);
for (int fd : clients) {
FD_SET(fd, &readfds);
if (fd > max_fd) max_fd = fd;
}
int activity = select(max_fd + 1, &readfds, nullptr, nullptr, nullptr);
if (activity < 0) { perror("select"); continue; }
// 新连接
if (FD_ISSET(server_fd, &readfds)) {
struct sockaddr_in client_addr{};
socklen_t len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
if (client_fd >= 0) {
clients.push_back(client_fd);
std::cout << "新客户端: fd=" << client_fd << "\n";
}
}
// 客户端数据
for (auto it = clients.begin(); it != clients.end(); ) {
int fd = *it;
if (FD_ISSET(fd, &readfds)) {
char buffer[1024];
ssize_t n = recv(fd, buffer, sizeof(buffer), 0);
if (n <= 0) {
std::cout << "客户端断开: fd=" << fd << "\n";
close(fd);
it = clients.erase(it);
continue;
}
send(fd, buffer, n, 0);
}
++it;
}
}
return 0;
}
| 模型 | 最大连接数 | 性能 | 适用场景 |
|---|
fork | 受进程数限制 | 高内存开销 | 少量长连接 |
select | FD_SETSIZE (1024) | O(n) 遍历 | 小规模连接 |
poll | 无硬限制 | O(n) 遍历 | 中等规模 |
epoll | 百万级 | O(1) 事件通知 | 高并发服务器 |
7. epoll:边缘触发 vs 水平触发
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <vector>
#include <unordered_map>
// 设置非阻塞
void set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
int main() {
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
struct sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(server_fd, 128);
// 创建 epoll 实例
int epoll_fd = epoll_create1(0);
if (epoll_fd < 0) { perror("epoll_create1"); return 1; }
// 注册 server_fd
struct epoll_event ev{};
ev.events = EPOLLIN;
ev.data.fd = server_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev);
std::unordered_map<int, std::string> client_names;
constexpr int MAX_EVENTS = 64;
std::cout << "epoll 服务器启动,端口 8080\n";
while (true) {
struct epoll_event events[MAX_EVENTS];
int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (int i = 0; i < nfds; ++i) {
int fd = events[i].data.fd;
if (fd == server_fd) {
// 新连接
struct sockaddr_in client_addr{};
socklen_t len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
if (client_fd < 0) continue;
set_nonblocking(client_fd);
ev.events = EPOLLIN | EPOLLET; // 边缘触发
ev.data.fd = client_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev);
client_names[client_fd] = "client_" + std::to_string(client_fd);
std::cout << "新连接: fd=" << client_fd << "\n";
} else if (events[i].events & EPOLLIN) {
// 边缘触发必须一次性读完所有数据
char buffer[4096];
while (true) {
ssize_t n = recv(fd, buffer, sizeof(buffer), 0);
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) break; // 数据读完
perror("recv");
close(fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
client_names.erase(fd);
break;
} else if (n == 0) {
std::cout << "客户端断开: fd=" << fd << "\n";
close(fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
client_names.erase(fd);
break;
} else {
send(fd, buffer, n, 0); // 回显
}
}
} else if (events[i].events & (EPOLLERR | EPOLLHUP)) {
std::cout << "连接错误: fd=" << fd << "\n";
close(fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
client_names.erase(fd);
}
}
}
close(epoll_fd);
close(server_fd);
return 0;
}
| 触发模式 | 行为 | 编程复杂度 | 适用场景 |
|---|
| 水平触发 (LT) | 只要缓冲区有数据就持续通知 | 简单 | 默认模式 |
| 边缘触发 (ET) | 仅在状态变化时通知一次 | 复杂 | 高性能场景 |
⚠️ 注意:边缘触发(ET)必须使用非阻塞 socket,并且必须循环读取直到 EAGAIN,否则会丢失数据。
8. 非阻塞 Socket
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
int main() {
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
set_nonblocking(sock_fd);
struct sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
// 非阻塞 connect
int ret = connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0) {
if (errno == EINPROGRESS) {
std::cout << "连接正在进行中(非阻塞)\n";
// 用 epoll/select 等待可写事件
} else {
perror("connect");
}
}
// 非阻塞 recv
char buffer[1024];
ssize_t n = recv(sock_fd, buffer, sizeof(buffer), 0);
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
std::cout << "没有数据可读(非阻塞)\n";
} else {
perror("recv");
}
}
close(sock_fd);
return 0;
}
9. 简单 HTTP 请求解析
#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
#include <algorithm>
struct HttpRequest {
std::string method;
std::string path;
std::string version;
std::unordered_map<std::string, std::string> headers;
std::string body;
};
// 解析 HTTP 请求
HttpRequest parse_http_request(const std::string& raw) {
HttpRequest req;
std::istringstream stream(raw);
std::string line;
// 请求行: GET /path HTTP/1.1
if (std::getline(stream, line)) {
// 移除 \r
if (!line.empty() && line.back() == '\r') line.pop_back();
std::istringstream line_stream(line);
line_stream >> req.method >> req.path >> req.version;
}
// 头部
while (std::getline(stream, line)) {
if (!line.empty() && line.back() == '\r') line.pop_back();
if (line.empty()) break; // 空行分隔头部和正文
auto colon = line.find(':');
if (colon != std::string::npos) {
std::string key = line.substr(0, colon);
std::string value = line.substr(colon + 1);
// 去除前导空格
value.erase(0, value.find_first_not_of(" \t"));
req.headers[key] = value;
}
}
// 正文(读取剩余内容)
std::string remaining;
while (std::getline(stream, line)) {
remaining += line + "\n";
}
req.body = remaining;
return req;
}
int main() {
std::string raw =
"GET /index.html HTTP/1.1\r\n"
"Host: example.com\r\n"
"User-Agent: curl/7.68.0\r\n"
"Accept: */*\r\n"
"\r\n";
auto req = parse_http_request(raw);
std::cout << "方法: " << req.method << "\n";
std::cout << "路径: " << req.path << "\n";
std::cout << "版本: " << req.version << "\n";
std::cout << "头部:\n";
for (const auto& [k, v] : req.headers) {
std::cout << " " << k << ": " << v << "\n";
}
return 0;
}
10. 简单 HTTP 服务器实现
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <unordered_map>
void set_nonblocking(int fd) {
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
}
std::string build_response(int status, const std::string& status_text,
const std::string& content_type,
const std::string& body) {
std::ostringstream resp;
resp << "HTTP/1.1 " << status << " " << status_text << "\r\n"
<< "Content-Type: " << content_type << "\r\n"
<< "Content-Length: " << body.size() << "\r\n"
<< "Connection: close\r\n"
<< "\r\n"
<< body;
return resp.str();
}
std::string handle_request(const std::string& method, const std::string& path) {
if (path == "/" || path == "/index.html") {
return build_response(200, "OK", "text/html; charset=utf-8",
"<html><body><h1>Hello from C++ HTTP Server!</h1>"
"<p>这是用纯 C++ 写的 HTTP 服务器</p></body></html>");
} else if (path == "/api/time") {
time_t now = time(nullptr);
return build_response(200, "OK", "application/json",
"{\"time\": \"" + std::string(ctime(&now)) + "\"}");
} else {
return build_response(404, "Not Found", "text/html",
"<html><body><h1>404 Not Found</h1></body></html>");
}
}
int main() {
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
struct sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
listen(server_fd, 128);
set_nonblocking(server_fd);
int epoll_fd = epoll_create1(0);
struct epoll_event ev{};
ev.events = EPOLLIN;
ev.data.fd = server_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev);
std::cout << "HTTP 服务器启动: http://0.0.0.0:8080\n";
constexpr int MAX_EVENTS = 64;
while (true) {
struct epoll_event events[MAX_EVENTS];
int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (int i = 0; i < nfds; ++i) {
int fd = events[i].data.fd;
if (fd == server_fd) {
struct sockaddr_in client_addr{};
socklen_t len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
if (client_fd >= 0) {
set_nonblocking(client_fd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev);
}
} else if (events[i].events & EPOLLIN) {
char buffer[4096];
std::string request_data;
while (true) {
ssize_t n = recv(fd, buffer, sizeof(buffer), 0);
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) break;
if (n <= 0) break;
request_data.append(buffer, n);
}
if (!request_data.empty()) {
// 简单解析请求行
std::istringstream stream(request_data);
std::string method, path, version;
stream >> method >> path >> version;
std::string response = handle_request(method, path);
send(fd, response.c_str(), response.size(), 0);
}
close(fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
}
}
}
close(epoll_fd);
close(server_fd);
return 0;
}
编译测试:
g++ -std=c++17 -o http_server http_server.cpp && ./http_server
# 另一个终端:
curl http://localhost:8080/
curl http://localhost:8080/api/time
11. 网络库简介
| 库 | 特点 | 适用场景 |
|---|
| libevent | 成熟稳定,跨平台 | 通用网络编程 |
| libuv | Node.js 底层,异步 I/O | 高性能异步服务 |
| Boost.Asio | C++ 标准提案,功能全面 | C++ 项目首选 |
| libev | 轻量级事件循环 | 嵌入式/轻量场景 |
| muduo | 陈硕出品,Reactor 模式 | 学习网络编程 |
libevent 最简示例
// 编译: g++ -std=c++17 -o demo demo.cpp -levent
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <iostream>
void read_cb(struct bufferevent* bev, void* ctx) {
struct evbuffer* input = bufferevent_get_input(bev);
char buffer[1024];
ssize_t n;
while ((n = evbuffer_remove(input, buffer, sizeof(buffer))) > 0) {
bufferevent_write(bev, buffer, n); // 回显
}
}
void accept_cb(struct evconnlistener* listener, evutil_socket_t fd,
struct sockaddr* addr, int socklen, void* ctx) {
struct event_base* base = (struct event_base*)ctx;
struct bufferevent* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, read_cb, nullptr, nullptr, nullptr);
bufferevent_enable(bev, EV_READ | EV_WRITE);
}
int main() {
struct event_base* base = event_base_new();
struct sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
struct evconnlistener* listener = evconnlistener_new_bind(
base, accept_cb, base, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
128, (struct sockaddr*)&addr, sizeof(addr));
std::cout << "libevent 回显服务器启动\n";
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
扩展阅读