简介
函数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:事件集合
evconnlistener_cb:cb是call back的缩写,就是回调函数定义如下:
typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
ptr:传递给cb的参数
flags:属性标志位,可取值9个(掰手指头数的),下面是9个取值的宏定义,在evconnlistener_new_bind函数源码中也可以看出来。
/** Flag: Indicates that we should not make incoming sockets nonblocking * before passing them to the callback. */ #define LEV_OPT_LEAVE_SOCKETS_BLOCKING (1u<<0) /** Flag: Indicates that freeing the listener should close the underlying * socket. */ #define LEV_OPT_CLOSE_ON_FREE (1u<<1) /** Flag: Indicates that we should set the close-on-exec flag, if possible */ #define LEV_OPT_CLOSE_ON_EXEC (1u<<2) /** Flag: Indicates that we should disable the timeout (if any) between when * this socket is closed and when we can listen again on the same port. */ #define LEV_OPT_REUSEABLE (1u<<3) /** Flag: Indicates that the listener should be locked so it's safe to use * from multiple threadcs at once. */ #define LEV_OPT_THREADSAFE (1u<<4) /** Flag: Indicates that the listener should be created in disabled * state. Use evconnlistener_enable() to enable it later. */ #define LEV_OPT_DISABLED (1u<<5) /** Flag: Indicates that the listener should defer accept() until data is * available, if possible. Ignored on platforms that do not support this. * * This option can help performance for protocols where the client transmits * immediately after connecting. Do not use this option if your protocol * _doesn't_ start out with the client transmitting data, since in that case * this option will sometimes cause the kernel to never tell you about the * connection. * * This option is only supported by evconnlistener_new_bind(): it can't * work with evconnlistener_new_fd(), since the listener needs to be told * to use the option before it is actually bound. */ #define LEV_OPT_DEFERRED_ACCEPT (1u<<6) /** Flag: Indicates that we ask to allow multiple servers (processes or * threads) to bind to the same port if they each set the option. * * SO_REUSEPORT is what most people would expect SO_REUSEADDR to be, however * SO_REUSEPORT does not imply SO_REUSEADDR. * * This is only available on Linux and kernel 3.9+ */ #define LEV_OPT_REUSEABLE_PORT (1u<<7) /** Flag: Indicates that the listener wants to work only in IPv6 socket. * * According to RFC3493 and most Linux distributions, default value is to * work in IPv4-mapped mode. If there is a requirement to bind same port * on same ip addresses but different handlers for both IPv4 and IPv6, * it is required to set IPV6_V6ONLY socket option to be sure that the * code works as expected without affected by bindv6only sysctl setting in * system. * * This socket option also supported by Windows. */ #define LEV_OPT_BIND_IPV6ONLY (1u<<8)
backlog:设置监听队列的大小,同listen函数的第二个参数。
int listen(int s, int backlog);
sa:IP和端口,同bind函数的第二个参数
socklen:结构体长度,同bind函数的第三个参数
int bind(int sockfd, struct sockaddr *sa, socklen_t socklen)
返回值:struct evconnlistener *
从源码简单分析可知 evconnlistener_new_bind内部实现了创建socket,绑定IP地址和端口,最后监听socket。
/**
Allocate a new evconnlistener object to listen for incoming TCP connections
on a given address.
@param base The event base to associate the listener with.
@param cb A callback to be invoked when a new connection arrives. If the
callback is NULL, the listener will be treated as disabled until the
callback is set.
@param ptr A user-supplied pointer to give to the callback.
@param flags Any number of LEV_OPT_* flags
@param backlog Passed to the listen() call to determine the length of the
acceptable connection backlog. Set to -1 for a reasonable default.
@param addr The address to listen for connections on.
@param socklen The length of the address.
*/
struct evconnlistener *
evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,
void *ptr, unsigned flags, int backlog, const struct sockaddr *sa,
int socklen)
{
struct evconnlistener *listener;
evutil_socket_t fd;
int on = 1;
int family = sa ? sa->sa_family : AF_UNSPEC;
int socktype = SOCK_STREAM | EVUTIL_SOCK_NONBLOCK;
if (backlog == 0)
return NULL;
if (flags & LEV_OPT_CLOSE_ON_EXEC)
socktype |= EVUTIL_SOCK_CLOEXEC;
fd = evutil_socket_(family, socktype, 0);
if (fd == -1)
return NULL;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on))<0)
goto err;
if (flags & LEV_OPT_REUSEABLE) {
if (evutil_make_listen_socket_reuseable(fd) < 0)
goto err;
}
if (flags & LEV_OPT_REUSEABLE_PORT) {
if (evutil_make_listen_socket_reuseable_port(fd) < 0)
goto err;
}
if (flags & LEV_OPT_DEFERRED_ACCEPT) {
if (evutil_make_tcp_listen_socket_deferred(fd) < 0)
goto err;
}
if (flags & LEV_OPT_BIND_IPV6ONLY) {
if (evutil_make_listen_socket_ipv6only(fd) < 0)
goto err;
}
if (sa) {
if (bind(fd, sa, socklen)<0)
goto err;
}
listener = evconnlistener_new(base, cb, ptr, flags, backlog, fd);
if (!listener)
goto err;
return listener;
err:
evutil_closesocket(fd);
return NULL;
}
实例:监听服务器
#include <sys/types.h>
#include <event2/event-config.h>
#include <event2/listener.h>
#include <stdio.h>
#include <event.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%d $$ " format "\n" \
,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif
/*
*/
void server_evconnlistener_cb(struct evconnlistener *listener, evutil_socket_t new_client, struct sockaddr *addr, int socklen, void *arg)
{
struct sockaddr_in *paddr = (struct sockaddr_in *)addr;
DEBUG_INFO("a new client fd = %d",new_client);
//char *inet_ntoa(struct in_addr in);
DEBUG_INFO("ip = %s,port = %d",inet_ntoa(paddr->sin_addr),ntohs(paddr->sin_port));
}
int main(int argc, char **argv){
struct event_base *base;
struct evconnlistener *listener;
struct sockaddr_in addr;
base = event_base_new();
if(base == NULL){
DEBUG_INFO("Couldn't create event_base");
exit(-1);
}
addr.sin_family = AF_INET;
// addr.sin_addr.s_addr = INADDR_ANY;
// addr.sin_addr.s_addr = inet_addr("0.0.0.0");
// addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_addr.s_addr = inet_addr("192.168.0.11");
addr.sin_port = htons(6666);
listener = evconnlistener_new_bind(
base,
server_evconnlistener_cb,
NULL,//传给cb的参数,本例中就是server_evconnlistener_cb
LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
5,//listen的第二个参数
(struct sockaddr*)&addr,//bind的第二个参数
sizeof(addr) //bind的第三个参数
);
if(listener == NULL){
DEBUG_INFO("evconnlistener_new_bind error");
exit(-1);
}
//开始监听集合中的事件
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
DEBUG_INFO("bye bye");
return 0;
}
测试:
运行程序,使用网络调试助手连接服务器的6666端口
程序输出信息:
实验分析:
从调试信息可知,输出信息中包含新客户端的文件描述符,客户端的IP地址和客户端的端口号。