【Linux系統程式設計】libevent庫實現簡易tcp伺服器

杨谖之發表於2024-04-14

libevent庫實現簡易tcp伺服器

流程分析

  1. 建立socket,設定埠複用,繫結四元組,開始監聽。
  2. 初始化event_base結構體。
  3. 編寫監聽事件的回撥函式和客戶端讀事件的回撥函式。
  4. 初始化tcp監聽事件,並加入event_base中。
  5. 開始event事件處理迴圈。
  6. 釋放所有事件佔用資源。
  7. 釋放event_base佔用資源。

服務端原始碼

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <event2/event.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <cerrno>
#include <unistd.h>

void conncb(evutil_socket_t fd, short events, void *arg);
void readcb(evutil_socket_t fd, short events, void *arg);

int main(int argc, char *argv[]) {
    // 建立socket
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    
    // 設定埠複用
    int opt = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 繫結
    sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_addr.s_addr = htonl(INADDR_ANY);
    serv.sin_port = htons(8888);
    serv.sin_family = AF_INET;
    bind(lfd, (sockaddr*)&serv, sizeof(serv));

    // 監聽
    listen(lfd, 1024);
    
    // 建立event_base
    event_base *base = event_base_new();
    if (base == nullptr) {
       perror("event_base_new()"); 
       exit(-1);
    }

    // 建立監聽檔案描述符對應事件
    event *ev = event_new(base, lfd, EV_READ|EV_PERSIST, conncb, base);
    if (ev == nullptr) {
        perror("event_new(lfd)");
        exit(-1);
    }

    // 將新的事件新增到event_base
    event_add(ev, nullptr);

    // 進入事件迴圈
    event_base_dispatch(base);

    // 釋放事件
    event_base_free(base);
    event_free(ev);

    close(lfd);

    return 0;
}

void conncb(evutil_socket_t fd, short events, void *arg) {
    sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);
    int cfd = accept(fd, (sockaddr*)&addr, &addrlen);
    if (cfd == -1) {
        perror("accept");
        exit(-1);
    }
    char ipstr[1024];
    if (nullptr == inet_ntop(AF_INET, &addr.sin_addr.s_addr, ipstr, 1024)) {
        perror("inet_ntop()");
        exit(-1);
    }
    std::cout << "Accept from " << ipstr << ":" << ntohs(addr.sin_port) << " on " << cfd << std::endl;
    
    // 建立客戶端監聽事件並放進event_base
    event *connev = event_new((event_base*)arg, cfd, EV_READ|EV_PERSIST, readcb, connev);
    if (connev == nullptr) {
        perror("event_new(cfd)");
        exit(-1);
    }
    event_add(connev, 0);
}

void readcb(evutil_socket_t fd, short events, void *arg) {
    int n;
    char buffer[1024];
    memset(buffer, 0, sizeof(buffer));   
    n = read(fd, buffer, sizeof(buffer));
    if (n <= 0) {
        close(fd);
        // 將讀事件從event_base移除
        event_del((event*)arg);
    }
    std::cout << "Read from " << fd << ":" << buffer << std::endl;
    write(fd, buffer, n);
}

相關文章