利用libevent写一个简单的tcp服务器

作者: 白云飞 分类: linux,网络知识 发布时间: 2018-12-26 16:31 阅读:

libevent 这个包很出名,很多优秀的开源项目也利用这个库,来做事件驱动库。它拥有优秀的跨平台特性,你可以在一个平台写代码,在各种平台都可以跑。官网地址:http://libevent.org/

我们下载一个新的版本 libevent-2.1.8-stable.tar.gz ,并进行编译安装。

cd /usr/local/src
wget https://github.com/libevent/libevent/releases/download/release-2.1.8-stable/libevent-2.1.8-stable.tar.gz
tar zxf libevent-2.1.8-stable.tar.gz
cd libevent-2.1.8-stable
./configure --prefix=/usr/local
make && make install

好了,等待编译安装完成之后,我们就可以使用 libevent搞点事情了。直奔主题,写一个 tcp 服务器。服务端:event_server.c

#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <unistd.h>
#include <event.h>

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

int tcp_server_init(int port, int listen_num);

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_PERSIST, accept_cb, base);

    event_add(ev_listen, NULL);

    event_base_dispatch(base);

    return 0;
}

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

    evutil_socket_t sockfd;

    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 = (struct event_base *)arg;

    struct event *ev = event_new(NULL, -1, 0, NULL, NULL);
    event_assign(ev, base, sockfd, EV_READ | EV_PERSIST, socket_read_cb, (void *)ev);

    event_add(ev, NULL);
}

void socket_read_cb(int fd, short events, void *arg)
{
    char msg[4096];
    struct event *ev = (struct event *)arg;
    int len = read(fd, msg, sizeof(msg) - 1);

    if(len <= 0) {

        printf("some error happen when read\n");
        event_free(ev);
        close(fd);
        return ;
    }

    msg[len] = '\0';
    printf("recv the client msg: %s", msg);

    char reply_msg[4096] = "I have recviecd the msg: ";
    strcat(reply_msg + strlen(reply_msg), msg);

    write(fd, reply_msg, strlen(reply_msg));
}

typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num)
{
    int errno_save;
    evutil_socket_t listener;

    listener = socket(AF_INET, SOCK_STREAM, 0);
    if(listener == -1) {
        return -1;
    }

    evutil_make_listen_socket_reuseable(listener);

    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 error;
    }

    evutil_make_socket_nonblocking(listener);

    return listener;

error:
    errno_save = errno;
    evutil_closesocket(listener);
    errno = errno_save;

    return -1;
}

客户端: event_cli.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <event.h>
#include <event2/util.h>

int tcp_connect_server(const char *server_ip, int port);

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

int main(int argc, char *argv[])
{
    if(argc < 3) {
        printf("please input 2 parameter\n");
        return -1;
    }

    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);

    event_add(ev_cmd, NULL);

    // 事件轮询
    event_base_dispatch(base);

    printf("finished \n");

    return 0;
}

void cmd_msg_cb(int fd, short events, void *arg)
{
    char msg[1024];

    int ret = read(fd, msg, sizeof(msg));
    if(ret <= 0) {
        perror("read fail");
        exit(1);
    }

    int sockfd = *((int*)arg);

    // 终端的信息,发送给服务器
    write(sockfd, msg, ret);
}

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);
}

typedef struct sockaddr SA;
int tcp_connect_server(const char *server_ip, int port)
{
    int sockfd, status, save_errno;
    struct sockaddr_in server_addr;

    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);

    if(0 == status) {
        errno = EINVAL;
        return -1;
    }

    sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if(sockfd == -1) {
        return sockfd;
    }

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

    if(status == -1) {
        save_errno = errno;
        close(sockfd);
        errno = save_errno;

        return -1;
    }

    evutil_make_socket_nonblocking(sockfd);

    return sockfd;
}

编译好执行。先执行server代码,然后执行客户端代码。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!