一、概述
sol_socket是一種基於事件驅動的網絡I/O庫,它能夠在不同的平台上提供高性能、高可靠性的網絡通信。sol_socket是一個輕型的庫,它的核心是使用非阻塞I/O和事件通知處理I/O讀寫事件。sol_socket封裝了socket系統調用和網絡事件處理模型,提供了方便的編程接口。
二、基本使用
在使用sol_socket前,需要先建立一個event_base(事件處理器)對象、一個或多個event(事件)對象。
struct event_base* base = event_base_new();
struct event* ev = event_new(base, sock_fd, EV_READ|EV_PERSIST, sock_accept_cb, NULL);
以上代碼建立了一個event_base對象和一個event對象。其中sock_fd是一個已經建立連接的socket文件描述符,EV_READ是事件類型,表示該事件是一個可讀事件,EV_PERSIST表示事件觸發後不自動刪除。
接下來需要設置回調函數,當事件觸發後會調用該回調函數:
void sock_accept_cb(int fd, short events, void* arg){
//處理事件的函數
}
以上代碼是一個簡單的回調函數,用於處理socket連接。
最後需要將事件添加到事件處理器的監聽隊列中:
event_add(ev, NULL);
以上代碼實現了將事件添加到監聽隊列中。
三、事件類型
sol_socket定義了五種事件類型:
- EV_TIMEOUT:超時事件
- EV_READ:讀事件
- EV_WRITE:寫事件
- EV_SIGNAL:信號事件
- EV_PERSIST:事件持久化(只有添加的事件才會在事件處理完畢後從監聽隊列中刪除,其它因為一次性事件(如超時事件)只會被處理一次)
四、常用函數介紹
1、event_base_new()
該函數用於建立一個事件處理器對象,返回該對象指針。
struct event_base* base = event_base_new();
2、event_new()
該函數用於建立一個事件對象,返回該對象指針。其中sock_fd是一個已經建立連接的socket文件描述符,EV_READ是事件類型,表示該事件是一個可讀事件,EV_PERSIST表示事件觸發後不自動刪除。
struct event* ev = event_new(base, sock_fd, EV_READ|EV_PERSIST, sock_accept_cb, NULL);
3、event_add()
該函數用於將事件添加到事件處理器的監聽隊列中。
event_add(ev, NULL);
4、event_del()
該函數用於將事件從事件處理器的監聽隊列中刪除。
event_del(ev);
5、event_priority_set()
該函數用於設置事件優先級,優先級越高的事件會優先被處理。默認優先級為0。正數優先級越高。
event_priority_set(ev, 1);
6、event_active()
該函數用於激活事件,並觸發事件回調函數。
event_active(ev, EV_READ, 1);
五、完整示例代碼
下面是一個簡單的使用sol_socket庫實現的基於TCP的echo服務器:
static void on_read(int fd, short event, void* arg);
static void on_accept(int fd, short event, void* arg);
int main(int argc, char **argv)
{
int listen_fd;
struct sockaddr_in address;
int reuseaddr_on = 1;
struct event_base *base;
struct event *listener_event;
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Couldn't create server socket");
return 1;
}
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8888);
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on,sizeof(reuseaddr_on)) == -1) {
perror("Setting SO_REUSEADDR failed");
return 1;
}
if (bind(listen_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("Could not bind socket");
return 1;
}
if (listen(listen_fd, 10) < 0) {
perror("Could not open socket for listening");
return 1;
}
base = event_base_new();
if (!base) {
perror("Could not initialize libevent");
return 1;
}
listener_event = event_new(base, listen_fd, EV_READ | EV_PERSIST, on_accept, (void*)base);
event_add(listener_event, NULL);
event_base_dispatch(base);
return 0;
}
static void on_accept(int fd, short event, void* arg){
struct event_base *base = arg;
int client_fd;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
client_fd = accept(fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd < 0) {
perror("accept failed");
return;
}
printf("Accepted connection from %s:%d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
struct event *client_event = event_new(base, client_fd, EV_READ | EV_PERSIST, on_read, (void*)&client_fd);
event_add(client_event, NULL);
}
static void on_read(int fd, short event, void* arg){
char recvbuf[1024] = {0};
int client_fd = *(int*)arg;
int recvlen = recv(client_fd, recvbuf, sizeof(recvbuf), 0);
if (recvlen <= 0) {
printf("Client disconnected.\n");
close(client_fd);
event_del((struct event*)arg);
event_free((struct event*)arg);
return;
}
printf("Received data from client: %s", recvbuf);
send(client_fd, recvbuf, strlen(recvbuf), 0);
}
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/240325.html