美文网首页
2019-03-24

2019-03-24

作者: nit小星星 | 来源:发表于2019-03-24 07:30 被阅读0次

LIBENENT

#cnclude<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet/h>

#include<errno.h>

#include<unistd.h>// 是 C 和 C++ 程序设计语言中提供对 POSIX 操作系统 API 的访问功能的头文件的名称。该头文件由 POSIX.1 标准(单一UNIX规范的基础)提出,故所有遵循该标准的操作系统和编译器均应提供该头文件(如 Unix 的所有官方版本,包括 Mac OS X、Linux 等)。

#include<stdio.h>

#include<string.h>

#inlcude<stdlib.h>

#include<event2/util.h>

type struct socketaddr SA;

int tcp_connect_to_server(const char * server_ip,int port )

{

int sockfd,save_error;

struct socketaddr_in,server_addr;

truct sockaddr_in

{

struct sockaddr_in

{

short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/

unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/

struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/

unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/

}

memset(&server_addr,0,sizeof(server_addr));

server_addr.sin_family=AF_INET;

server_addr.sin_port=htons(port);

status=inet_aton(server_ip,&server_addr.sin_addr);inet_aton是一个计算机函数,功能是将一个字符串IP地址转换为一个32位的网络序列IP地址。如果这个函数成功,函数的返回值非零,如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以它的值会被忽略。

if( status == 0 ) //the server_ip is not valid value

    {

        errno = EINVAL;

        return -1;

    }

    sockfd=::socket(PF_INET,SOCK_STREAM,0);//参数一表示地址类型,参数而表示面向连接还是面向无连接的套接字,参数三是表示TCP还是udp

    if(sockfd==-1)

    return sockfd;

    其中流式套接字使用SOCKET_STREAM表示

    SOCK_STREAM 是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送

    流时套接字有自己的纠错机制

    特点有

    数据在传输过程中不会丢失,数据按照顺序传输的,数据的发送和接受不是同步的

    只要传送带本身没有问题(不会断网),就能保证数据不丢失;同时,较晚传送的数据不会先到达,较早传送的数据不会晚到达,这就保证了数据是按照顺序传递的。

  为什么流格式套接字可以达到高质量的数据传输呢?这是因为它使用了 TCP 协议(The Transmission Control Protocol,传输控制协议),TCP 协议会控制你的数据按照顺序到达并且没有错误。

  你也许见过 TCP,是因为你经常听说“TCP/IP”。TCP 用来确保数据的正确性,IP(Internet Protocol,网络协议)用来控制数据如何从源头到达目的地,也就是常说的“路由”。

  数据报格式套接字(Datagram Sockets)也叫“无连接的套接字”,在代码中使用 SOCK_DGRAM 表示。

计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。

因为数据报套接字所做的校验工作少,所以在传输效率方面比流格式套接字要高。

有了地址类型和数据传输方式,还不足以决定采用哪种协议吗?为什么还需要第三个参数呢?

正如大家所想,一般情况下有了 af 和 type 两个参数就可以创建套接字了,操作系统会自动推演出协议类型,除非遇到这样的情况:有两种不同的协议支持同一种地址类型和数据传输类型。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。

status=::connect(sockfd,(SA*)&server_addr,sizeof(server_addr));

  if( status == -1 )

    {

        save_errno = errno;

        ::close(sockfd);

        errno = save_errno; //the close may be error

        return -1;

    }

evutil_make_socket_nonblocking(sockfd);

return sockfd;

}

void server_msg_cb(struct bufferevent* bev, void* arg)

{

char msg[1024];

msg[len];

size_t len=bufferevent_read(bev,msg,sizeof(msg));

msg[len]='\0';

printf("recv%s from server\n",msg);

}

void socket_read_cb(int fd, short events, void *arg)

{

    char msg[1024];

    //为了简单起见,不考虑读一半数据的情况

    int len = read(fd, msg, sizeof(msg)-1);

    if( len <= 0 )

    {

        perror("read fail ");

        exit(1);

    }

    msg[len] = '\0';

    printf("recv %s from server\n", msg);

int main()

{

if( argc < 3 )

    {

        printf("please input 2 parameter\n");

        return -1;

    }

    //两个参数依次是服务器端口和IP地址

    int sockfd=tcp_connect_server(argv[1], atoi(argv[2]));

    if( sockfd == -1)

    {

        perror("tcp_connect error ");

        return -1;

    }

    printf("connect to server successful\n");

    struct event_base *base=event_base_new();

    struct event *ev_sockfd=event_new(base,sockfd, EV_READ | EV_PERSIST,socket_read_cb, NULL);

    event_add(ev_sockfd,NULL);

    struct event *ev_cmd=event_new(base, STDIN_FILENO,  EV_READ | EV_PERSIST, cmd_msg_cb, (void*)&sockfd);

    //eventbase是libevent处理事物的框架负责事件的注册,删除,属于reactor模式中的reactor

    //创建一个event_base event_base_new(void)

      struct event_base *

  event_base_new(void)

  {

    struct event_base *base = NULL;

    struct event_config *cfg = event_config_new();

    if (cfg) {

        base = event_base_new_with_config(cfg);

        event_config_free(cfg);

    }

  return base;

}

由上边程序知道,event_base创建的关键所在是event_base_new_config函数,而次函数有用过调用多路复用的机制来初始化event_base实例

event_new创建事件,涉及内存操作

event_add

event_del

event_free释放由new创建的内存

struct event_base *

event_init(void)

{

    struct event_base *base = event_base_new();    //event_init()调用event_base_new()

    if (base != NULL)

        current_base = base;

    return (base);

}

struct event_base *

event_base_new(void)    //初始化libevent的event_base

{

    int i;

    struct event_base *base;

    if ((base = calloc(1, sizeof(struct event_base))) == NULL)    //在堆上分配内存存储event_base,所有字段初始化为0

        event_err(1, "%s: calloc", __func__);

    event_sigcb = NULL;

    event_gotsig = 0;

    detect_monotonic();    //设置use_monotonic变量

    gettime(base, &base->event_tv);    //base->tv_cache.tv_sec非0,则赋给base->event_tv

    min_heap_ctor(&base->timeheap);    //初始化定时事件的小根堆base->timeheap    min_heap.h

    TAILQ_INIT(&base->eventqueue);    //初始化注册事件链表base->eventqueue    sys/queue.h

    base->sig.ev_signal_pair[0] = -1;    //初始化信号base->sig

    base->sig.ev_signal_pair[1] = -1;

    base->evbase = NULL;    //初始化I/O多路复用 base->evbase

    //遍历全局数组eventops[],初始化libevent的I/O多路复用机制

    for (i = 0; eventops[i] && !base->evbase; i++) {    //以NULL标志数组结尾,只选取一个I/O多路复用机制

        base->evsel = eventops[i];    //初始化base->evsel

        base->evbase = base->evsel->init(base);    //初始化base->evbase

    }

    if (base->evbase == NULL)    //没有I/O多路复用

        event_errx(1, "%s: no event mechanism available", __func__);

    if (evutil_getenv("EVENT_SHOW_METHOD")) //调用getenv()获取环境变量EVENT_SHOW_METHOD    evutil.c

        event_msgx("libevent using: %s\n",

              base->evsel->name);

    /* allocate a single active event queue */

    //event_base_new()内调用event_base_priority_init()

    event_base_priority_init(base, 1);    //设置优先级base->nactivequeues;分配数组base->activequeues。数组大小和优先级相同

    return (base);

}

先是初始化event_base然后初始化event,通过初始化event_set()函数实现。

void

event_set(struct event *ev, int fd, short events,

      void (*callback)(int, short, void *), void *arg)

{

    /* Take the current base - caller needs to set the real base later */

    ev->ev_base = current_base;    //设置event属于当前base;current_base通过event_init()设置

    ev->ev_callback = callback;    //设置回调函数

    ev->ev_arg = arg;    //设置回调函数的3个参数

    ev->ev_fd = fd;

    ev->ev_events = events;

    ev->ev_res = 0;

    ev->ev_flags = EVLIST_INIT;    //设置event状态

    ev->ev_ncalls = 0;

    ev->ev_pncalls = NULL;

    min_heap_elem_init(ev);    //初始化event在小根堆中索引为-1    min_heap.h

    /* by default, we put new events into the middle priority */

    if(current_base)

        ev->ev_pri = current_base->nactivequeues/2;    //设置event优先级

}

  event_base()初始化event_base

        event_set()初始化event

        event_base_set()将event绑定到指定的event_base上

        event_add()将event添加到事件链表上,注册事件

        event_base_dispatch()循环、检测、分发事件

        写一个测试程序

        struct event ev;

        struct timeval tv;

        void time(int fd,short event,void *arg)

        {

        printf("amck");

        event_add(&ev,&tv);

        }

        int main()

        {

        struct event_base *base=event_init();

      tv.tv_sec=1;

      tv.tv_usec=0;

      event_set(&ev, -1, 0, timer_cb, NULL);  //初始化event结构中成员

        event_base_set(base, &ev);

        event_add(&ev, &tv);  //将event添加到events事件链表,注册事件

        event_base_dispatch(base);  //循环、分发事件

        return 0;

        }

服务端代码:

#include<stdio.h>

#include<string.h>

#include<errno.h>

#include<unistd.h>

#include<event.h>

void accept_cb(int fd, short events, void* arg)

evutil_socket_t sockfd;//int

struct sockaddr_in client;

socklen_t len=sizeof(client);

sockfd-::accept(fd,(struct sockaddr*&client,&len);

evutil_make_socket_nonblocking(sockfd);

printf("accept a client %d\n", sockfd);

struct event_base * base=(event_base*)arg;

bufferevent * bev=bufferevent_socket_new(base,sockfd BEV_OPT_CLOSE_ON_FREE);//

//bufferevent专门为封装成带有缓冲区的socket套接字。当有数据到来时,我们只需要在回调函数里面通过封装函数bufferevent_read读取数据即可,根本不需要自己处理一些细节,以及缓存的问题。

bufferevent其实也就是在event_base的基础上再进行一层封装,其本质还是离不开event和event_base,从bufferevent的结构体就可以看到这一点。

bufferevent_setcb(bev,socket_read_cb,NULL,event_cb,arg);

bufferevent_ebable(bev,EV_READ|EV_PERSIST);

void socket_read_cb(int fd, short events, void *arg);

typedef struct aockaddr SA

int tcp_server_init(int port, int listen_num)

{

int errno_save;

evutil_sock_t listener;

if(listener==-1)

return =-1;

evutil_make_listen_socket_reuseable(listen);//允许多次绑定同一地址

struct sockaddr_in sin;

sin.sin_family=AF_INET;

sin.sin_addr.s_addr=0;

sin.sin_port=htons(port);

if(::bind(listener,(SA)&sin,sizeof(sin))<0)

goto error;

if(::listen(listener,listen_num)<0)

goto err;

evutil_make_socket_nonblocking(listener);//跨平台统一接口,将套接字设置为非阻塞

return listener;

error:

        errno_save = errno;

        evutil_closesocket(listener);

        errno = errno_save;

        return -1;

}

int main(int argc,char** argv)

{

int listener=tcp_server_init(9999,10);

if( listener == -1 )

    {

        perror(" tcp_server_init error ");

        return -1;

    }

struct event_base *base=event_base_new();

struct event *ev_listen=event_new(base,listener,EV_READ|EV_PRESIST,accept__cb,base);

}

相关文章

网友评论

      本文标题:2019-03-24

      本文链接:https://www.haomeiwen.com/subject/evsyvqtx.html