简介
函数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 connectionson 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 thecallback is NULL, the listener will be treated as disabled until thecallback 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 theacceptable 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_cbLEV_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地址和客户端的端口号。