最近在学习libevent,顺便写了几个例子。按照Unix网络编程里的经典例子,从echo server开始。所谓echo server(回显服务器), 就是服务器将客户端发送过来的信息原封不动的发送回给客户端。
以下是结合libevent的初级回显服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <event2/event.h>
#define MAX_BUF_SIZE 1024
int n_send(int fd, void *data, int len, int flag)
{
int nsent = 0;
int nleft = len;
void *ptr = data;
while (nleft > 0)
{
if ((nsent = send(fd, ptr, nleft, flag)) <= 0)
{
if (errno == EINTR)
{
continue;
}
else if (errno == EAGAIN)
{
usleep(100);
continue;
}
else
{
fprintf(stderr, "send() error: errno %d --- %s\r\n",
errno, strerror(errno));
break;
}
nleft -= nsent;
ptr += nsent;
}
return (len - nleft);
}
void do_read_cb(int fd, short events, void *arg)
{
char buf[MAX_BUF_SIZE];
struct event *ev = (struct event *)arg;
int len = 0;
again:
len = recv(fd, buf, sizeof(buf) - 1, 0);
if (len < 0)
{
if (errno == EINTR)
{
goto again;
}
fprintf(stderr, "some error happened while receiving data, errno: %d--- %s\r\n",
errno, strerror(errno));
event_free(ev);
close(fd);
return;
}
else if (len == 0)
{
fprintf(stderr, "socket closed by peer, fd = %d...\r\n", fd);
event_free(ev);
close(fd);
return;
}
n_send(fd, buf, len, 0);
}
void do_accept_cb(int fd, short events, void *arg)
{
evutil_socket_t sock;
struct sockaddr_in client;
socklen_t len = sizeof(client);
sock = accept(fd, (struct sockaddr *)&client, &len);
if (sock < 0)
{
fprintf(stderr, "accept() error: errno %d --- %s\r\n",
errno, strerror(errno));
return;
}
evutil_make_socket_nonblocking(sock);
printf("A new client connected from %s on port %d, socket %d\r\n",
inet_ntoa(client.sin_addr),
ntohs(client.sin_port), sock);
struct event_base *base = (struct event_base *)arg;
struct event *ev = event_new(NULL, -1, 0, NULL, NULL);
event_assign(ev, base, sock, EV_READ | EV_PERSIST, do_read_cb, (void *)ev);
event_add(ev, NULL);
}
int init_echo_server(int port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
fprintf(stderr, "socket() error: errno %d --- %s\r\n",
errno, strerror(errno));
return -1;
}
evutil_make_listen_socket_reuseable(sock);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(port);
if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
fprintf(stderr, "bind() error: errno %d --- %s\r\n",
errno, strerror(errno));
goto error;
}
if (listen(sock, 32) < 0)
{
fprintf(stderr, "listen() error: errno %d --- %s\r\n",
errno, strerror(errno));
goto error;
}
struct event_base *base = event_base_new();
struct event *ev_listen = event_new(base, sock, EV_READ | EV_PERSIST,
do_accept_cb, base);
event_add(ev_listen, NULL);
event_base_dispatch(base);
event_base_free(base);
return 0;
error:
close(sock);
return -1;
}
int main(int argc, char **argv)
{
int port = 9999;
if (argc >= 2)
{
port = atoi(argv[1]);
if (port < 0 || port > 65535)
{
fprintf(stderr, "Invalid port ...\r\n");
return -1;
}
}
init_echo_server(port);
return 0;
}
至于客户端代码,大家可以参考Unix网络编程。下面给出一个示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <event.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/util.h>
#include <event2/listener.h>
int is_valid_ip_addr(const char *ip)
{
if (ip == NULL || strlen(ip) < 7)
{
return -1;
}
int len = strlen(ip);
const char *start = ip;
const char *end = ip + len - 1;
int ret = 0;
int dot_count = 0;
int cur_val = 0;
int val = -1;
while (end > start && isspace(*end))
{
--end;
}
while (start < end && isspace(*start))
{
++start;
}
if (start >= end || isdigit(*start) == 0 || isdigit(*end) == 0)
{
return -1;
}
while (start <= end)
{
while (start <= end && '0' <= *start && *start <= '9')
{
cur_val = 10 * cur_val + (*start - '0');
if (cur_val < 0 || cur_val > 255)
{
ret = -1;
break;
}
val = cur_val;
++start;
}
if (start <= end)
{
if (*start == '.' && val != -1)
{
++start;
++dot_count;
cur_val = 0;
val = -1;
}
else
{
ret = -1;
break;
}
}
else
{
break;
}
}
if (ret == 0 && dot_count != 3)
{
ret = -1;
}
return ret;
}
int connect_with_timeout(int sock, struct sockaddr *sa, socklen_t sa_len,
int seconds)
{
int flags, n, error;
socklen_t len;
fd_set rset, wset;
struct timeval tv;
flags = fcntl(sock, F_GETFL, 0);
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
{
fprintf(stderr, "fcntl() error while set socket nonblocking: %d --- %s\r\n",
errno, strerror(errno));
close(sock);
return -1;
}
error = 0;
if ((n = connect(sock, sa, sa_len)) < 0)
{
if (errno != EINPROGRESS)
{
fprintf(stderr, "connect() error: %d --- %s\r\n",
errno, strerror(errno));
close(sock);
return -1;
}
}
if (n == 0)
{
goto done;
}
if (seconds <= 1)
{
seconds = 1;
}
FD_ZERO(&rset);
FD_SET(sock, &rset);
wset = rset;
tv.tv_sec = seconds;
tv.tv_usec = 0;
if ((n = select(sock + 1, &rset, &wset, NULL, &tv)) == 0)
{
close(sock);
fprintf(stderr, "connect to server timeout ...\r\n");
return -1;
}
if (FD_ISSET(sock, &rset) || FD_ISSET(sock, &wset))
{
len = sizeof(error);
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
{
close(sock);
return -1;
}
}
else
{
fprintf(stderr, "select() error: fd is not set\r\n");
close(sock);
return -1;
}
done:
if (fcntl(sock, F_SETFL, flags) < 0)
{
fprintf(stderr, "fcntl() error while set flags back: %d --- %s\r\n",
error, strerror(errno));
close(sock);
return -1;
}
if (error)
{
close(sock);
errno = error;
return -1;
}
return sock;
}
int create_tcp_connect_socket(char *ip_str, int port, int timeout)
{
int sock;
struct sockaddr_in server_addr;
uint32_t ip;
if (ip_str == NULL || is_valid_ip_addr(ip_str) != 0)
{
printf("Invalid ip address ...\r\n");
return -1;
}
if (port < 0 || port > 65535)
{
printf("Invalid port number ...\r\n");
return -1;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(ip_str);
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
fprintf(stderr, "socket() error: %d --- %s\r\n", errno, strerror(errno));
return -1;
}
return connect_with_timeout(sock, (struct sockaddr *)&server_addr,
sizeof(server_addr), timeout);
}
#define MAX_BUF_SIZE 1024
void do_read_cb(int fd, short events, void *arg)
{
char buf[MAX_BUF_SIZE];
int n;
again:
n = recv(fd, buf, sizeof(buf) - 1, 0);
if (n < 0)
{
if (errno == EINTR)
{
goto again;
}
fprintf(stderr, "recv() error: errno %d --- %s\r\n",
errno, strerror(errno));
return;
}
else if (n == 0)
{
printf("connection closed by peer.\r\n");
return;
}
buf[n] = '\0';
printf("%s", buf);
}
int n_send(int fd, void *data, int len, int flag)
{
int nsent = 0;
int nleft = len;
void *ptr = data;
while (nleft > 0)
{
if ((nsent = send(fd, ptr, nleft, flag)) <= 0)
{
if (errno == EINTR)
{
continue;
}
else if (errno == EAGAIN)
{
usleep(100);
continue;
}
else
{
fprintf(stderr, "send() error: errno %d --- %s\r\n",
errno, strerror(errno));
break;
}
}
nleft -= nsent;
ptr += nsent;
}
return (len - nleft);
}
void console_read_cb(int fd, short events, void *arg)
{
char buf[MAX_BUF_SIZE];
int len = read(fd, buf, sizeof(buf));
if (len <= 0)
{
fprintf(stderr, "read() error: errno %d --- %s\r\n",
errno, strerror(errno));
return;
}
int sock = (*(int *)arg);
n_send(sock, buf, len, 0);
}
int main(int argc, char **argv)
{
if (argc < 3)
{
printf("usage: %s <ip> <port>\r\n", argv[0]);
return -1;
}
int sock = create_tcp_connect_socket(argv[1], atoi(argv[2]), 3);
if (sock < 0)
{
printf("cregate connect socket failed ...\r\n");
return -1;
}
evutil_make_socket_nonblocking(sock);
struct event_base *base = event_base_new();
struct event *ev_sock = event_new(base, sock, EV_READ | EV_PERSIST,
do_read_cb, base);
event_add(ev_sock, NULL);
struct event *ev_console = event_new(base, STDIN_FILENO,
EV_READ | EV_PERSIST, console_read_cb, (void *)&sock);
event_add(ev_console, NULL);
event_base_dispatch(base);
event_base_free(base);
return 0;
}