evconnlistener_new_bind 是 Libevent 库中的一个函数,用于分配一个监听器对象,并监听给定地址上的 TCP 连接。下面是关于 evconnlistener_new_bind 函数的详细解析:
函数原型
struct evconnlistener* evconnlistener_new_bind(
struct event_base* base,
evconnlistener_cb cb,
void* ptr,
unsigned flags,
int backlog,
const struct sockaddr* sa,
int socklen
);
参数说明
base:
类型:struct event_base*
描述:关联的 Libevent 框架上下文。
cb:
类型:evconnlistener_cb(函数指针类型)
描述:当新连接到来时,进行回调的函数。如果此函数为 NULL,则监听器按被禁用运行,直到函数被设置为非 NULL 值。
ptr:
类型:void*
描述:提供给回调函数的参数指针。通常用于传递一些上下文信息给回调函数。
flags:
类型:unsigned
描述:属性标志位,可以取多个值,用于控制监听器的行为。例如,LEV_OPT_REUSEABLE 表示端口复用,LEV_OPT_CLOSE_ON_FREE 表示释放资源时自动关闭套接字。
backlog:
类型:int
描述:类似于标准 listen() 函数的 backlog 参数,用于指定未完成连接队列的最大长度。如果设置为 -1,则使用默认值。
sa:
类型:const struct sockaddr*
描述:要绑定到的地址的套接字地址结构。这通常是一个指向 sockaddr_in 或 sockaddr_in6 结构体的指针,具体取决于你想要监听的 IP 版本(IPv4 或 IPv6)。
socklen:
类型:int
描述:sa 参数指向的套接字地址结构的字节长度。
返回值
如果成功,返回一个指向新分配的 evconnlistener 对象的指针。
如果失败,返回 NULL。
注意事项
使用 evconnlistener_new_bind 时,Libevent 会自动完成 socket()、bind() 和 listen() 的系统调用,并设置 accept() 的回调函数。
你不需要手动设置套接字为非阻塞模式,因为 Libevent 会为你处理这些细节。
如果想要监听所有可用的网络接口(即 INADDR_ANY),可以在 sockaddr_in 结构体的 sin_addr 字段中设置其 s_addr 成员为 0。
示例
// 假设已经包含了必要的头文件和设置了相应的变量
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有网络接口
sin.sin_port = htons(8080); // 监听 8080 端口
struct evconnlistener* listener = evconnlistener_new_bind(
base,
callback_function, // 自定义的回调函数
NULL,
LEV_OPT_REUSEABLE, // 设置端口复用
-1, // 使用默认的 backlog 值
(struct sockaddr*)&sin,
sizeof(sin)
);
if (!listener) {
// 处理错误
}
sockaddr_in 是 IPv4 网络编程中用于表示套接字地址的结构体。它定义在 <netinet/in.h> 头文件中,并通常与套接字函数(如 bind(), connect(), accept() 等)一起使用。
sockaddr_in 结构体的定义可能因操作系统和编译器而异,但大致如下:
struct sockaddr_in {
short sin_family; // 地址族,通常为 AF_INET
unsigned short sin_port; // 端口号,网络字节序
struct in_addr sin_addr; // IPv4 地址
char sin_zero[8]; // 填充字段,未使用,必须设置为 0
};
// in_addr 是一个结构体,通常只包含一个成员,用于表示 32 位的 IPv4 地址
struct in_addr {
unsigned long s_addr; // IPv4 地址,网络字节序
};
解析 sockaddr_in
sin_family:
这是一个地址族字段,通常设置为 AF_INET(在 <sys/socket.h> 中定义)以表示这是一个 IPv4 地址。
sin_port:
这是一个 16 位的端口号字段,以网络字节序(大端字节序)存储。在使用之前,你可能需要使用 htons() 函数(host to network short)将其从主机字节序转换为网络字节序,反之亦然,使用 ntohs() 函数。
sin_addr:
这是一个 in_addr 结构体,用于存储 32 位的 IPv4 地址。in_addr 结构体通常只包含一个 unsigned long 类型的 s_addr 字段。你可以使用 inet_pton() 函数(presentation to network)将点分十进制表示的 IP 地址(如 “192.168.1.1”)转换为网络字节序的 in_addr 结构体,或使用 inet_ntoa() 函数(但请注意,inet_ntoa() 不是线程安全的)将其转换回点分十进制表示。
sin_zero:
这是一个填充字段,用于确保 sockaddr_in 结构体的大小与 sockaddr 结构体的大小相同。你应该始终将其设置为 0。
示例
以下是一个简单的示例,展示了如何设置一个 sockaddr_in 结构体:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main() {
struct sockaddr_in server_addr;
// 设置地址族为 IPv4
server_addr.sin_family = AF_INET;
// 设置端口号为 8080(网络字节序)
server_addr.sin_port = htons(8080);
// 设置 IP 地址为 192.168.1.1(网络字节序)
if (inet_pton(AF_INET, "192.168.1.1", &server_addr.sin_addr) <= 0) {
perror("inet_pton");
exit(EXIT_FAILURE);
}
// sin_zero 必须设置为 0
memset(server_addr.sin_zero, 0, sizeof(server_addr.sin_zero));
// 现在你可以使用 server_addr 进行网络编程了
// ...
return 0;
}