#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#define ZEROPOD(var) memset(&var, 0, sizeof(var))
const int MYPORT = 1234;
const int qsize = 10;
const int epsize= 10;
/** устанавливает неблокирующий режим для указанного сокета */
int setnonblocking(int sock) {
int opts = fcntl(sock,F_GETFL);
if (opts < 0) {
perror("fcntl(F_GETFL)");
return -1;
}
opts = (opts | O_NONBLOCK);
if (fcntl(sock,F_SETFL,opts) < 0) {
perror("fcntl(F_SETFL)");
return -1;
}
return 0;
}
/** печатает адрес в консоль */
void printf_address(const struct sockaddr_in* sa) {
char buff[INET_ADDRSTRLEN+1] = "\0";
const char* p = inet_ntop(AF_INET, &sa->sin_addr, buff, sizeof(buff));
if ( !p ) {
perror("printf_address()");
return;
}
in_port_t port = ntohs(sa->sin_port);
fprintf(stderr, "%s:%d\n", p, port);
}
/** инициализирует серверный сокет */
int initserver(int type, const struct sockaddr* sa, socklen_t alen, int qlen) {
int sock_fd = 0;
int value = 1;
if ( (sock_fd=socket(sa->sa_family, type, 0)) < 0 ) {
return -1;
}
if ( setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0 ) {
close(sock_fd);
return -1;
}
if ( setsockopt(sock_fd, SOL_SOCKET, SO_KEEPALIVE, &value, sizeof(value)) < 0 ) {
close(sock_fd);
return -1;
}
if ( setnonblocking(sock_fd) < 0 ) {
close(sock_fd);
return -1;
}
if ( bind(sock_fd, sa, alen) < 0 ) {
close(sock_fd);
return -1;
}
if ( type == SOCK_STREAM || type == SOCK_SEQPACKET ) {
if ( listen(sock_fd, qlen) < 0 ) {
close(sock_fd);
return -1;
}
}
return sock_fd;
}
/***************************************************************************/
int main() {
/** дескрипторы */
int count_fds = 0;
int base_socket_fd = 0;
int epoll_fd = 0;
/** адреса */
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
socklen_t addrlen = sizeof(sockaddr_in);
ZEROPOD(my_addr);
ZEROPOD(their_addr);
/** epoll */
struct epoll_event epoll_event;
struct epoll_event static_events[epsize];
struct epoll_event* events = &static_events[0];
ZEROPOD(epoll_event);
ZEROPOD(static_events);
/** инициализирую адреса */
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
/** создаю слушающий сокет */
base_socket_fd=initserver(SOCK_STREAM, (struct sockaddr*)&my_addr, addrlen, qsize);
if ( base_socket_fd < 0) {
perror("initserver()");
return EXIT_FAILURE;
}
/** создаю epoll с начальным размером очереди epsize */
epoll_fd = epoll_create(epsize);
if (epoll_fd == -1) {
perror("epoll_create()");
return EXIT_FAILURE;
}
/** создается елемент события для слушающего сокета */
epoll_event.events = EPOLLIN|EPOLLRDHUP|EPOLLET;
epoll_event.data.fd = base_socket_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, base_socket_fd, &epoll_event) == -1) {
perror("epoll_ctl()");
return EXIT_FAILURE;
}
while ( 1 ) {
/** ожидаю события на дескрипторе epoll */
count_fds = epoll_wait(epoll_fd, events, epsize, -1);
if (count_fds == -1) {
perror("epoll_wait()");
return EXIT_FAILURE;
}
/** цикл повторяется "count_fds" раз */
for ( int n = 0; n < count_fds; ++n) {
/* если (events[n].data.fd == base_socket_fd) истинно -
* значит запрос от клиента на подключение
*/
if ( events[n].data.fd == base_socket_fd ) {
addrlen = sizeof(struct sockaddr_in);
/** принимаем запрос на подключение */
int new_socket_fd = accept(base_socket_fd, (struct sockaddr*)&my_addr, &addrlen);
printf_address(&my_addr);
if (new_socket_fd == -1) {
perror("accept()");
return EXIT_FAILURE;
}
if ( setnonblocking(new_socket_fd) < 0 ) {
perror("setnonblocking()");
close(new_socket_fd);
continue;
}
/** указываем какие события на сокете мониторить */
epoll_event.events = EPOLLIN|EPOLLET;
epoll_event.data.fd = new_socket_fd;
/** добавляем в epoll */
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_socket_fd, &epoll_event) == -1) {
perror("epoll_ctl()");
return EXIT_FAILURE;
}
} else {
int32_t sock_fd = events[n].data.fd;
uint32_t event = events[n].events;
/* этот дескриптор уже есть в epoll. */
if ( event & EPOLLIN ) {
/* сокет готов для чтения */
printf("descriptor %d ready for read\n", sock_fd);
char buff[32] = "\0";
int rd = read(sock_fd, &buff, sizeof(buff));
fprintf(stderr, "%s\n", buff);
}
if ( event & EPOLLOUT ) {
/* сокет готов для записи */
printf("descriptor %d ready for write\n", sock_fd);
}
if ( event & EPOLLRDHUP ) {
/* как я понял из документации(http://linux.die.net/man/2/epoll_ctl)
* это событие закрытия удаленного сокета. так?
*/
printf("descriptor %d disconnect\n", sock_fd);
}
if ( event & EPOLLPRI ) {
/* срочные данные готовы для считывания. так? */
printf("descriptor %d ready for read urgent data\n", sock_fd);
}
if ( event & EPOLLERR ) {
/* какая-то ошибка произошла на сокете. так?
* какие ошибки могут происходить? и почему?
*/
printf("on descriptor %d error condition\n", sock_fd);
}
if ( event & EPOLLHUP ) {
/* какое-то событие произошло на сокете. так? */
printf("on descriptor %d ???\n", sock_fd);
}
if ( event & EPOLLET ) {
/* как я понял, эта константа используется для установки
* какого-то правила наблюдения за сокетом. и не понимаю,
* о чем это сыбытие может говорить(если оно происходит).
*/
printf("event & EPOLLET\n");
}
if ( event & EPOLLONESHOT ) {
/* по моему, так же как и в предыдущем блоке, это не событие.
* но суть константы понятна.
*/
printf("event & EPOLLONESHOT\n");
}
close(sock_fd);
}
}
}
close(epoll_fd);
close(base_socket_fd);
return 0;
}