td;lr: essaie d'écho "Hello World" à un client HTTP mais rencontre des problèmes avec la fermeture trop précoce du socket et des erreurs de lecture mystérieuses de l'outil de benchmark wrk.
J'essaie de créer un serveur HTTP simple "Hello World" avec la bibliothèque de boucle d'événements picoev mais la connexion client/peer se ferme trop rapidement et l'outil de benchmark wrk renvoie des erreurs de lecture pour une raison quelconque dont je ne suis pas conscient. Voici le code que j'utilise :
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "picoev.h"
#define HOST 0 /* 0x7f000001 pour localhost */
#define PORT 8080
#define MAX_FDS 1024 * 128
#define TIMEOUT_SECS 10
char buf[1024];
ssize_t response;
int listen_sock;
static void close_conn(picoev_loop* loop, int fd)
{
picoev_del(loop, fd);
close(fd);
}
static void write_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
{
// vérifie si ni les événements ni les délais ne sont présents
if ((events & PICOEV_TIMEOUT) != 0) {
/* délai */
close_conn(loop, fd);
} else if ((events & PICOEV_READ) != 0) {
/* mise à jour du délai, et lecture */
picoev_set_timeout(loop, fd, TIMEOUT_SECS);
ret = read(fd, buf, sizeof(buf));
if (ret == 0 | ret == -1) {
close_conn(loop, fd);
}
else {
write(fd, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\nConnection: close\r\n\r\nHello, world!", ret);
close_conn(loop, fd);
}
}
}
static void accept_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
{
int newfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (newfd != -1) {
picoev_add(loop, newfd, PICOEV_READ, TIMEOUT_SECS, write_callback, NULL);
}
}
int main(void)
{
picoev_loop* loop;
/* écouter sur le port */
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, 1, sizeof(1));
struct sockaddr_in listen_addr;
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons(PORT);
listen_addr.sin_addr.s_addr = htonl(HOST);
bind(listen_sock, (struct sockaddr*)&listen_addr, sizeof(listen_addr));
listen(listen_sock, 1000000);
/* initialiser picoev */
picoev_init(MAX_FDS);
/* créer une boucle */
loop = picoev_create_loop(60);
/* ajouter le socket d'écoute */
picoev_add(loop, listen_sock, PICOEV_READ, 1, accept_callback, NULL);
/* boucle */
while (1) {
// Appel asynchrone de Picoev pour écrire etc...
picoev_loop_once(loop, 10);
}
/* nettoyer */
picoev_destroy_loop(loop);
picoev_deinit();
return 0;
}
Le lancement avec curl http://0.0.0.0:8080/ -v
renvoie :
* Essai de 0.0.0.0...
* TCP_NODELAY activé
* Connecté à 0.0.0.0 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: 0.0.0.0:8080
> User-Agent: curl/7.52.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html
< Content-Length: 13
* transfert fermé avec 13 octets restants à lire
* Curl_http_done: appel prématuré == 1
* arrêt du flux de mise en pause!
* Fermeture de la connexion 0
curl: (18) transfert fermé avec 13 octets restants à lire
ou le suivant après avoir essayé de benchmarker des milliers de connexions concurrentes quelques fois après l'autre :
* Essai de 0.0.0.0...
* TCP_NODELAY activé
* échec de la connexion au port 8080 de 0.0.0.0 : Connection refused
* Échec de la connexion au port 8080 de 0.0.0.0 : Connection refused
* Fermeture de la connexion 0
curl: (7) Échec de la connexion au port 8080 de 0.0.0.0 : Connection refused
et wrk -t1 -c400 http://0.0.0.0:8080/
renvoie toutes les erreurs en lecture:
Exécution d'un test de 10s @ http://0.0.0.0:8080/
1 thread et 400 connexions
Statistiques du thread Moyenne Ecart-type Max +/- Ecart-type
Latence 0,00us 0,00us 0,00us -nan%
Req/Sec 0,00 0,00 0,00 -nan%
0 requêtes en 10,08s, 9,05MB lues
Erreurs de socket : connect 0, lecture 249652, écriture 0, expiration 0
Requêtes/sec: 0,00
Transfert/sec: 0,90MB
Je ne comprends pas si le problème vient de la fermeture trop précoce du socket, de la réponse (ret) incorrecte, des fd zombie non tués ou d'une combinaison de ceux-ci. En essayant de faire des traces du programme, je n'obtiens aucune information précieuse sur l'emplacement du problème, juste beaucoup de epoll_wait. J'ai déjà essayé de nombreuses variations de réponses HTTP en vain et comme vous pouvez le voir, j'essaie de tuer tout fd zombie ou en erreur dès que nécessaire mais soit je le fais mal, soit le problème réside ailleurs. Est-ce que quelqu'un pourrait m'aider à localiser le problème là où il se trouve ?