> if ( event & EPOLLIN ) {
> /* сокет готов для чтения */
> printf("descriptor %d ready for read\n", sock_fd);
> char buff[32] = "\0"; тут не надо инициализации буфера как "\0" - все равно перезапишется.
> int rd = read(sock_fd, &buff, sizeof(buff));
вызов read принимает вторым аргументом не указатель-на-указатель, а указатель на буфер - он же никуда его двигать не собирается: просто запихнуть в буфер что-то.
тобишь будет строка как:
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);
вот это лишнее - контроль выходного потока нужно ставить если данных в ответе накопилось больше, чем на одну операцию write. Некоторые предпочитают дождаться EBUSY ошибки, прежде чем подписываться на информирование об этом сигнале.
> }
<кусь> - ибо неважно
> if ( event & EPOLLERR ) {
> /* какая-то ошибка произошла на сокете. так?
>
> * какие ошибки могут происходить? и
>почему?
> */
наиболее суперская ошибка - "ошибочная ошибка - никаких ошибок нет", или "превышено число допустимых ошибок", можно поймать в винде - хто его знает какие еще хитроумные сплетения возможны. Но наиболее часто имеется в виду ошибка по потоку, например, если нечего больше мониторить - поток закрылся, разорвалось соединение (характено для SOCK_STREAM - TCP). Или какая-то часть кода не хочет мира во всем мире и злономерено закрыла исходный код дескриптор, посчитав, что номер 666 будет смотреться лучше, чем 13 (close, dup, dup2).
Это же относится к EPOLLHUP и прочих непонятностей - не хочется лезть в хэндбук.