Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 05 Jan 2015 16:41:52 +0100
From:      Pieter de Goeje <pieter@degoeje.nl>
To:        =?windows-1252?Q?Victor_Fran=E7a?= <victorfranlopes@outlook.com>,  "freebsd-hackers@freebsd.org" <freebsd-hackers@freebsd.org>
Subject:   Re: kqueue nonblocking socket timeout with siege
Message-ID:  <54AAB0C0.20908@degoeje.nl>
In-Reply-To: <COL127-W1755795B8BA5646BB11280B75A0@phx.gbl>
References:  <COL127-W1755795B8BA5646BB11280B75A0@phx.gbl>

next in thread | previous in thread | raw e-mail | index | archive | help
Victor França schreef op 2015-01-03 om 16:37:
> I'm trying to create a server that works with multiple connections using kqueue.
> After much research, I developed my code to test the technology.
> So I started testing with the siege, however, after an average of 300 requests, the server begins to become unstable, sometimes the siege points timeout, sometimes it can get the answer.
>
> I 'm using the g ++ to compile.
>
> Here is my code.

It looks to me like your code overwrites the kevent changeset each time 
it accepts a new client or writes data without pushing the previous 
changeset out to the kernel if multiple events are received from a 
single kevent() call.
I would keep a list of changes and commit them to the kernel whenever 
the list is full or kevent() is called in the main loop, whichever comes 
first. Pseudo code:

my_ev_set(...) {
   if changeset is full, call kevent(q, changeset, changeset_count, ...);
   EV_SET(&changeset[changeset_count++], ...);
}

Use my_ev_set() whenever you'd call EV_SET() normally. You can then 
remove all manual calls to kevent() (except the one in the main loop).
The added benefit of this approach is that you minimize the number of 
system calls.

Some other things I noticed:
- The code doesn't account for partial reads & writes.
- You don't need to set EV_ENABLE when first adding a filter, this is 
the default.
- The code doesn't check for EV_EOF and fflags (which contains the 
error) in the EV_READ case.
- EV_DISABLE doesn't actually remove the event, use EV_DELETE if you 
don't want to trigger the event in the first place.
- It isn't necessary to EV_DELETE or EV_DISABLE events on a socket prior 
to close()ing it.

Hope this helps,
Pieter


>
> #include <iostream>
> #include <netinet/in.h>
> #include <netinet/tcp.h>
> #include <sys/socket.h>
> #include <cstring>
> #include <cerrno>
> #include <cstdlib>
> #include <unistd.h>
> #include <sys/time.h>
> #include <sys/types.h>
> #include <sys/event.h>
> #include <sstream>
>
> using namespace std;
>
> struct client_s {
>      int fd;
>      int type;
>      socklen_t addrlen;
>      struct sockaddr addr;
>      int bufflen;
> };
>
> int main (int argc, char *argv[]) {
>
>      int portN, sockFD, sockOPT, eveCT, optRET, bindRET, listenRET, kernelQUE, nev, connectionFlags, numBT;
>      struct sockaddr_in sockADDR;
>      struct kevent events[2];
>      struct kevent changes[2];
>
>      if (argc < 2) {
>          cerr << "Argument required: [port]" << endl;
>          return -1;
>      }
>
>      portN = atoi(argv[1]);
>
>      sockFD = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
>
>      if (sockFD < 0) {
>          cerr << "Error while opening socket: " << strerror(errno) << endl;
>          return -1;
>      } else
>          clog << "Socket openend. Nonblock socket defined." << endl;
>
>      sockOPT = 1;
>
>      optRET = setsockopt(sockFD, SOL_SOCKET, SO_REUSEADDR, &sockOPT, sizeof(sockOPT)); // Avoid TIME_WAIT
>      if (optRET < 0) {
>          cerr << "Error while setting flag SO_REUSEADDR: " << strerror(errno) << endl;
>          return -1;
>      } else
>          clog << "SO_REUSEADDR flag ok." << endl;
>
>      optRET = setsockopt(sockFD, IPPROTO_TCP, TCP_NODELAY, &sockOPT, sizeof(sockOPT)); // Avoid socket buffer
>      if (optRET < 0) {
>          cerr << "Error while setting flag TCP_NODELAY: " << strerror(errno) << endl;
>          return -1;
>      } else
>          clog << "TCP_NODELAY flag ok." << endl;
>
>      memset(&sockADDR, 0, sizeof(struct sockaddr_in));
>
>      sockADDR.sin_family = AF_INET;
>      sockADDR.sin_port = htons(portN);
>      sockADDR.sin_addr.s_addr = INADDR_ANY;
>
>      bindRET = bind(sockFD, (struct sockaddr*)&sockADDR, sizeof(sockADDR));
>
>      if (bindRET < 0) {
>          cerr << "Error while binding socket: " << strerror(errno) << endl;
>          return -1;
>      } else
>          clog << "Socket binded." << endl;
>
>      listenRET = listen(sockFD, 1000);
>
>      if (listenRET < 0) {
>          cerr << "Error while start listening the port: " << strerror(errno) << endl;
>          return -1;
>      } else
>          clog << "Socket is listening the port " << argv[1] << endl;
>
>      kernelQUE = kqueue();
>
>      if (kernelQUE < 0) {
>          cerr << "Error on creating kqueue." << endl;
>          return -1;
>      } else
>          clog << "Starting kernel queue." << endl;
>
>      memset(events, 0, sizeof(events));
>      memset(changes, 0, sizeof(changes));
>
>      EV_SET(&changes[0], sockFD, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
>
>      eveCT = 1;
>      for (;;) {
>
>          nev = kevent(kernelQUE, changes, eveCT, events, eveCT, NULL);
>
>          if (nev < 0) {
>              cerr << "Error resolving event." << endl;
>              return -1;
>          }
>
>          for (int i = 0;i <= nev; i++) {
>              struct client_s * client = static_cast<client_s *>(malloc(sizeof(struct client_s)));
>
>              if (events[i].ident == sockFD && events[i].filter == EVFILT_READ) {
>                  client->fd = accept4(sockFD, &client->addr, &client->addrlen, SOCK_NONBLOCK);
>
>                  if (client->fd < 0) {
>                      cerr << "Error while accepting new connection." << strerror(errno) << endl;
>                      free(client);
>                  } else {
>                      client->type = 2;
>                      client->bufflen = 0;
>                      EV_SET(&changes[1], client->fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, client);
>                      eveCT=2;
>                  }
>              }
>
>              if (events[i].filter == EVFILT_READ && events[i].udata && events[i].data > 0) {
>                  client = static_cast<client_s *>(events[i].udata);
>                  if (client->type == 2) {
>
>                      client->type++;
>                      char *buffer = new char[events[i].data];
>                      client->bufflen = events[i].data;
>                      numBT = recv(client->fd, buffer, events[i].data, 0);
>                      delete[] buffer;
>                      if (numBT == events[i].data) {
>                          EV_SET(&changes[1], client->fd, EVFILT_READ, EV_DISABLE, 0, 0, 0);
>                          kevent(kernelQUE, &changes[1], 1, NULL, 0, 0);
>                          EV_SET(&changes[1], client->fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, client);
>                      } else
>                          cerr << "Error while reading." << strerror(errno) << endl;
>                  }
>              } else if (events[i].filter == EVFILT_WRITE && events[i].udata) {
>                  client = static_cast<client_s *>(events[i].udata);
>                  if (client->type == 3) {
>                      string query("HTTP/1.1 200 OK\r\n\r\nKernel events, baby!";);
>                      numBT = send(client->fd, query.c_str(), query.size(), 0);
>                      if (numBT == query.size()) {
>                          EV_SET(&changes[1], client->fd, EVFILT_WRITE, EV_DISABLE, 0, 0, 0);
>                          kevent(kernelQUE, &changes[1], 1, NULL, 0, 0);
>                          memset(&changes[1], 0, sizeof(struct kevent));
>                          shutdown(client->fd, SHUT_RDWR);
>                          close(client->fd);
>                          free(client);
>                          eveCT=1;
>                      } else
>                          cerr << "Error while writing." << strerror(errno) << endl;
>                  }
>              }
>          }
>      }
>
>      shutdown(sockFD, SHUT_RDWR);
>      close(sockFD);
>
>      return 0;
> } 		 	   		
> _______________________________________________
> freebsd-hackers@freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
> To unsubscribe, send any mail to "freebsd-hackers-unsubscribe@freebsd.org"




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?54AAB0C0.20908>