Note
利用Libevent搭建TCP服务器,可以接受TCP客户端的连接,并可进行数据读写
服务端代码
注意:编译的时候要加libevent库, gcc Libevent服务器.c -o Libevent服务器 -levent
/**********************************************************************************************
常规创建服务器:
1、创建socket
2、bind 绑定socket
3、listen 监听
4、accept 连接
------------------------------------------------
Libevent创建服务器:
1、evconnlistener_new_bind()函数封装了创建、绑定、监听、连接等功能:
**
Allocate a new evconnlistener object to listen for incoming TCP connections
on a given address. //创建监听对象,在指定的地址上监听接下来的TCP连接
@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.
**
EVENT2_EXPORT_SYMBOL struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
const struct sockaddr *sa, int socklen);
2、unsigned flags参数:
** 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)
3、回调函数原型:
**
A callback that we invoke when a listener has a new connection.
@param listener The evconnlistener
@param fd The new file descriptor
@param addr The source address of the connection
@param socklen The length of addr
@param user_arg the pointer passed to evconnlistener_new()
*
typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
4、bufferevent_socket_new函数
**
Create a new socket bufferevent over an existing socket.
@param base the event base to associate with the new bufferevent.
@param fd the file descriptor from which data is read and written to.
This file descriptor is not allowed to be a pipe(2).
It is safe to set the fd to -1, so long as you later
set it with bufferevent_setfd or bufferevent_socket_connect().
@param options Zero or more BEV_OPT_* flags
@return a pointer to a newly allocated bufferevent struct, or NULL if an
error occurred
@see bufferevent_free()
*
EVENT2_EXPORT_SYMBOL
struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options);
//属性设置
** Options that can be specified when creating a bufferevent *
enum bufferevent_options {
** If set, we close the underlying file
* descriptor/bufferevent/whatever when this bufferevent is freed. *
BEV_OPT_CLOSE_ON_FREE = (1<<0),
** If set, and threading is enabled, operations on this bufferevent
* are protected by a lock *
BEV_OPT_THREADSAFE = (1<<1),
** If set, callbacks are run deferred in the event loop. *
BEV_OPT_DEFER_CALLBACKS = (1<<2),
** If set, callbacks are executed without locks being held on the
* bufferevent. This option currently requires that
* BEV_OPT_DEFER_CALLBACKS also be set; a future version of Libevent
* might remove the requirement.*
BEV_OPT_UNLOCK_CALLBACKS = (1<<3)
};
5、给bufferevent设置回调函数 bufferevent_setcb
**
Changes the callbacks for a bufferevent.
@param bufev the bufferevent object for which to change callbacks
@param readcb callback to invoke when there is data to be read, or NULL if
no callback is desired
@param writecb callback to invoke when the file descriptor is ready for
writing, or NULL if no callback is desired
@param eventcb callback to invoke when there is an event on the file
descriptor
@param cbarg an argument that will be supplied to each of the callbacks
(readcb, writecb, and errorcb)
@see bufferevent_new()
*
EVENT2_EXPORT_SYMBOL
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg);
6、使能bufferevent对象bufferevent_enable函数
**
Enable a bufferevent.
@param bufev the bufferevent to be enabled
@param event any combination of EV_READ | EV_WRITE.
@return 0 if successful, or -1 if an error occurred
@see bufferevent_disable()
*
EVENT2_EXPORT_SYMBOL int bufferevent_enable(struct bufferevent *bufev, short event);
7、回调函数read_cb原型:
**
A read or write callback for a bufferevent.
The read callback is triggered when new data arrives in the input
buffer and the amount of readable data exceed the low watermark
which is 0 by default.
The write callback is triggered if the write buffer has been
exhausted or fell below its low watermark.
@param bev the bufferevent that triggered the callback
@param ctx the user-specified context for this bufferevent
*
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
8、bufferevent_read函数原型:从bufferevent对象中读取数据
**
Read data from a bufferevent buffer.
The bufferevent_read() function is used to read data from the input buffer.
@param bufev the bufferevent to be read from
@param data pointer to a buffer that will store the data
@param size the size of the data buffer, in bytes
@return the amount of data read, in bytes.
*
EVENT2_EXPORT_SYMBOL
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
9、回调函数bufferevent_event_cb原型:如果不是读\写事件之外的事件
**
An event/error callback for a bufferevent.
The event callback is triggered if either an EOF condition or another
unrecoverable error was encountered.
For bufferevents with deferred callbacks, this is a bitwise OR of all errors
that have happened on the bufferevent since the last callback invocation.
@param bev the bufferevent for which the error condition was reached
@param what a conjunction of flags: BEV_EVENT_READING or BEV_EVENT_WRITING
to indicate if the error was encountered on the read or write path,
and one of the following flags: BEV_EVENT_EOF, BEV_EVENT_ERROR,
BEV_EVENT_TIMEOUT, BEV_EVENT_CONNECTED.
@param ctx the user-specified context for this bufferevent
*
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx);
************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <event.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <event2/listener.h>
#include <string.h>
//bufferevent对象读\写之外的事件,
void event_cb(struct bufferevent *bev, short what, void *ctx)
{
printf("异常发生!%x \n", what);
if(what & BEV_EVENT_EOF) //以 ctrl+C 为例,当客户端断开,便会触发该回调类型
{
//printf("客户端 %d 下线\n", *(int*)ctx); //容易传局部参数,不建议传
printf("客户端下线\n");
//释放bufferevent对象
bufferevent_free(bev);
}
else
{
printf("未知错误!\n");
}
}
//回调函数read_cb实现,读取数据。参数:bufferevent对象、回调函数入参(可将fd传入,知道当前的连接fd是谁)
void read_cb(struct bufferevent *bev, void *ctx)
{
//int fd = *(int*)ctx; //容易传局部参数,不建议
//读取数据
char buf[128] = {0};
size_t ret = bufferevent_read(bev, buf, sizeof(buf));
if(0 > ret)
{
printf("bufferevent_read error\n");
}
else
{
//printf("read from %d %s \n", fd, buf);
printf("read %s \n", buf);
}
}
void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *arg)
{
printf("服务器接收 %d 的连接\n", fd);
struct event_base *base = arg;
//针对已经存在的socket创建bufferevent对象。实际上是将TCP连接socket与bufferevent绑定起来建立连接
//参数:事件集合(从主函数传递过来)、fd(代表TCP连接,一个客户端一个连接,fd不同)、属性设置BEV_OPT_CLOSE_ON_FREE(如果释放bufferevent对象则关闭连接)
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if(NULL == bev)
{
printf("bufferevent_socket_new error!\n");
exit(1);
}
//给bufferevent设置回调函数
//参数:bufferevent对象、读事件回调函数、写事件回调函数、其他事件回调函数、回调函数入参(无参数用NULL,将fd地址传给回调函数)
bufferevent_setcb(bev, read_cb, NULL, event_cb, &fd);
//使能bufferevent对象,使bufferevent对象生效,读功能。
bufferevent_enable(bev, EV_READ);
}
int main()
{
//创建一个事件集合
struct event_base *base = event_base_new();
if(NULL == base)
{
printf("event_base_new error! \n");
exit(1);
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = 8000;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//创建socket、绑定、监听、接受连接;创建监听对象,在指定的地址上监听接下来的TCP连接
//参数:监听集合、回调函数(连接到来会触发调用)、回调参数、属性设置(释放监听对象关闭socket|端口重复使用、接收队列长度、绑定接收地址、地址长度
struct evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 10, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(NULL == listener)
{
printf("evconnlistener_new_bind error! \n");
exit(1);
}
//监听集合中的事件
event_base_dispatch(base);
//释放两个对象,base对象和监听对象
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
测试客户端
- NOTE: 运行客户端显示“connect error!
- Connection refused ” 待解决。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#define IP "127.0.0.1"
#define PORT 8000
int main()
{
int ret = -1;
char buf[128] = {0};
int fd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == fd)
{
printf("create socket error! \n");
exit(1);
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; //设置tcp协议族
server_addr.sin_port = htons(PORT); //设置端口号
server_addr.sin_addr.s_addr = inet_addr(IP); //设置连接服务器ip地址
ret = connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(-1 == ret)
{
perror("connect\n");
// printf("connect error! ret = %d \n",ret);
close(fd);
exit(1);
}
while(1)
{
printf("Input: \n");
scanf("%s", buf);
ret = send(fd, buf, sizeof(buf), 0);
if(-1 == ret)
{
printf("send error! \n");
close(fd);
exit(1);
}
}
close(fd);
return 0;
}


