epoll networking code review

L

lovecreatesbeauty

1. How does the code handle EPOLLIN, EPOLLOUT, EPOLLERR?

Is it correct?

for (iefd = 0; iefd != nefd; iefd++){
if (evp[iefd].data.fd == sfd) {
...
} else {

if ((evp[iefd].events & EPOLLIN) == EPOLLIN){
...
}

if ((evp[iefd].events & EPOLLOUT) == EPOLLOUT){
...
}

if ((evp[iefd].events & EPOLLERR) == EPOLLERR ||
(evp[iefd].events & EPOLLHUP) == EPOLLHUP ||
((evp[iefd].events & EPOLLIN) != EPOLLIN && (evp[iefd].events & EPOLLOUT) != EPOLLOUT))
{
...
}

2. Do I need to use epoll functions on client side which calls connect()?

I see some select() example which applies select functions on both server and client sides.


Thank you for your time!


////////////////////////////////////////////////////////////////////////////////
// server.c
// (e-mail address removed)
////////////////////////////////////////////////////////////////////////////////

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <stdlib.h>

#define PORTNO "12345"
#define BACKLOG 5
#define MAXEVENTS 10

int setnonblocking(int fd)
{
int flags;

flags = fcntl(fd, F_GETFL, 0);
if (flags == -1){
perror ("fcntl");
return -1;
}
flags |= O_NONBLOCK;
flags = fcntl(fd, F_SETFL, flags);
if (flags == -1){
perror ("fcntl");
return -1;
}
return 0;
}

int main(void)
{
struct addrinfo hints, *result, *rp;
struct sockaddr addr;
socklen_t addrlen;
int sfd, ifd;
int optval;
int rt, errsv = 0;
char host[NI_MAXHOST], service[NI_MAXSERV];
char buf[128] = {'\0'};
struct epoll_event ev, *evp;
int efd, iefd, nefd;
char hostnm[20] = {'\0'};
time_t tm;

if (gethostname(hostnm, sizeof hostnm - 1) == -1){
perror("gethostname");
return -1;
}

memset(&hints, 0, sizeof hints);
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;

rt = getaddrinfo(NULL, PORTNO, &hints, &result);
errsv = errno;
if (rt){
printf("%s: %s\n", "getaddrinfo gai_strerror", gai_strerror(rt));
if (errsv)
perror("getaddrinfo");
return rt;
}

optval = 1;
for (rp = result; rp; rp = rp->ai_next){
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1)
continue;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) == -1){
perror("setsockopt");
return -1;
}
if (!bind(sfd, rp->ai_addr, rp->ai_addrlen))
break;
close(sfd);
}
if (!rp){
printf("Could not bind socket to any address\n");
return !rp;
}

if (listen(sfd, BACKLOG) == -1){
perror("listen");
return -1;
}
freeaddrinfo(result);

efd = epoll_create1(0);
if (efd == -1){
perror("epoll_create");
return -1;
}
ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
ev.data.fd = sfd;
if (epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &ev) == -1){
perror("epoll_ctl");
return -1;
}
errsv = 0;
evp = calloc(MAXEVENTS, sizeof ev);
errsv = errno;
if (!evp){
printf("%s: %s\n", "calloc failed ", errsv ? strerror(errsv) : "");
return !evp;
}

for (;;){
nefd = epoll_wait(efd, evp, MAXEVENTS, -1);
if (nefd == -1){
perror("epoll_wait");
return -1;
}

for (iefd = 0; iefd != nefd; iefd++){
if (evp[iefd].data.fd == sfd) {
addrlen = sizeof addr;
ifd = accept(sfd, &addr, &addrlen);
if (ifd == -1){
perror("accept");
return -1;
}
if (!getnameinfo(&addr, addrlen, host, NI_MAXHOST, service, NI_MAXSERV, 0))
printf("Connection from %s %s\n", host, service);
else
printf("Unknown %s %s\n", host, service);
if (setnonblocking(ifd)){
printf("setnonblocking failed\n");
return -1;
}
ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
ev.data.fd = ifd;
if (epoll_ctl(efd, EPOLL_CTL_ADD, ifd, &ev) == -1){
perror("epoll_ctl");
return -1;
}
} else {
if ((evp[iefd].events & EPOLLIN) == EPOLLIN){
memset(buf, 0, sizeof buf);

//partial read or recv? not handled
rt = recv(evp[iefd].data.fd, buf, sizeof buf - 1, 0); //MSG_DONTWAIT
errsv = errno;
if (rt == -1){
printf("%s: %s\n", "recv", strerror(errsv));
if (errsv == ECONNRESET || errsv == ENOTCONN){
close(evp[iefd].data.fd);
continue;
}
}
if (rt == 0){
printf("%s: %s\n", "recv", "Peer has performed an orderly shutdown");
close(evp[iefd].data.fd);
continue;
}
if (strlen(buf))
printf("recv: client time: %d %s", strlen(buf), buf);
}

if ((evp[iefd].events & EPOLLOUT) == EPOLLOUT){
time(&tm);
snprintf(buf, sizeof buf, "%s %s", hostnm, ctime(&tm));

//partial write or send? not handled
rt = send(evp[iefd].data.fd, buf, strlen(buf), 0); //MSG_DONTWAIT
errsv = errno;
if (rt == -1){
printf("%s: %s\n", "send", strerror(errsv));
if (errsv == ECONNRESET || errsv == ENOTCONN || errsv == EPIPE || errsv == EBADF){
close(evp[iefd].data.fd);
continue;
}
}
}

if ((evp[iefd].events & EPOLLERR) == EPOLLERR ||
(evp[iefd].events & EPOLLHUP) == EPOLLHUP ||
((evp[iefd].events & EPOLLIN) != EPOLLIN && (evp[iefd].events & EPOLLOUT) != EPOLLOUT))
{
printf ("Unknown event: %d\n", evp[iefd].events);
close (evp[iefd].data.fd);
continue;
}
}
}
}

// not reachable
free(evp);
close(sfd);
return 0;
}






////////////////////////////////////////////////////////////////////////////////
// client.c
// (e-mail address removed)
////////////////////////////////////////////////////////////////////////////////

#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#define PORTNO "12345"

int main(int argc, char *argv[])
{
struct addrinfo hints, *result, *rp;
int fd;
int rt;
int errsv = 0;
char buf[128] = {'\0'};
time_t tm;
char hostnm[20] = {'\0'};

if (argc < 2){
printf("%s <host>\n", argv[0]);
return -1;
}
if (gethostname(hostnm, sizeof hostnm - 1) == -1){
perror("gethostname");
return -1;
}

memset(&hints, 0, sizeof hints);
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;

rt = getaddrinfo(argv[1], PORTNO, &hints, &result);
errsv = errno;
if (rt){
printf("%s: %s\n", "getaddrinfo gai_strerror", gai_strerror(rt));
if (errsv)
perror("getaddrinfo");
return rt;
}
for (rp = result; rp; rp = rp->ai_next){
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd == -1)
continue;
if (connect(fd, rp->ai_addr, rp->ai_addrlen) != -1)
break;
close(fd);
}
if (!rp){
printf("Could not connect socket to any address\n");
return !rp;
}
freeaddrinfo(result);

for (;;){

sleep(1); //Block revc / send used - no MSG_DONTWAIT
//temporary for demo only
//Normal code does not require this sleep.

// send
time(&tm);
snprintf(buf, sizeof buf, "%s %s", hostnm, ctime(&tm));
rt = send(fd, buf, strlen(buf), 0); //MSG_DONTWAIT
errsv = errno;
if (rt == -1){
printf("%s: %s\n", "send", strerror(errsv));
if (errsv == ECONNRESET || errsv == ENOTCONN || errsv == EPIPE){
close(fd);
break;
}
}

// recv
memset(buf, 0, sizeof buf);
rt = recv(fd, buf, sizeof buf - 1, 0); //MSG_DONTWAIT
errsv = errno;
if (rt == -1){
printf("%s: %s\n", "recv", strerror(errsv));
if (errsv == ECONNRESET || errsv == ENOTCONN){
close(fd);
break;
}
}
if (rt == 0){
close(fd);
printf("%s: %s\n", "recv", "Peer has performed an orderly shutdown");
break;
}
if (strlen(buf))
printf("recv: server time: %d %s", strlen(buf), buf);
}
close(fd);
return 0;
}
 
L

lovecreatesbeauty

1. How does the code handle EPOLLIN, EPOLLOUT, EPOLLERR?

The following is my little general automatical Makefile used to compile the code.

The server and client code should be compiled seperately.

You just put a copy of Makefile with each code and it just compiles automatically.


# Visual Stupid Makefile (e-mail address removed)
# Layout: [include/] src/
# All .c and Makefile in src, .h can be in include. No other sub-directories.
# Project related: CPPFLAGS
# .cpp, CXX, CXXFLAGS for C++

OUT = main.out
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
CC = gcc
CXX = g++
CFLAGS = -std=c99 -Wall -W
CXXFLAGS = -std=c++0x -Wall -W
CPPFLAGS = -I ../include
LDFLAGS =

CFLAGS += -g
#CFLAGS += -DNDEBUG
CFLAGS += -D_POSIX_SOURCE -D_BSD_SOURCE
LDFLAGS += -lpthread

$(OUT): $(OBJS)
$(CC) $(LDFLAGS) $^ -o $@

include $(SRCS:.c=.d)

%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$

..PHONY : clean
clean:
rm *.d *.d.* $(OBJS) $(OUT)
 
B

Ben Bacarisse

lovecreatesbeauty said:
1. How does the code handle EPOLLIN, EPOLLOUT, EPOLLERR?

The people over in comp.unix.programmer know more about this stuff. I'd
ask there.

<snip>
 
B

Ben Bacarisse

lovecreatesbeauty said:
Would you please review my code if I post it there :)

I'm not an expert on the use of these functions -- I know hardly more
more than a reading of the manual would give. The point of posting
where the experts hang out is to get expert advice.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,709
Latest member
AustinMudi

Latest Threads

Top