2017-03-20 2 views
1

Je m'intéresse aux principes de base des serveurs Web, comme Apache ou Nginx, alors maintenant je développe mon propre serveur.Est-il possible d'écrire directement à partir du fichier sur le socket?

Lorsque mon serveur reçoit une requête, il recherche un fichier (par exemple, index.html), s'il existe - il lit tout le contenu dans le tampon (content) et l'écrit sur le support après. Voici un code simplifié:

int return_file(char* content, char* fullPath) { 
    file = open(fullPath, O_RDONLY); 
    if (file > 0) { // File was found, OK 
     while ((nread = read(file, content, 2048)) > 0) {} 
     close(file); 
     return 200; 
    } 
} 

La question est assez simple: est-il possible d'éviter d'utiliser le tampon et écrire le contenu de fichiers directement sur la prise?

Merci pour tous les conseils :)

+1

Je ne l'ai jamais utilisé donc je ne veux pas écrire une réponse, mais jetez un oeil à [ 'splice'] (https://linux.die.net/ man/2/épissure). L'idée ici est de faire un transfert de zéro copie, ce qui vous permettra au moins de gagner du temps en copiant les données du fichier dans l'espace utilisateur, puis de redescendre à l'espace du noyau avant de l'atteindre. Vous pouvez google plus sur les concepts de zéro-copie, et il y a un échange informatif d'email de Linus quelque part en parlant de 'splice'. Je parle ici de Linux, je ne suis pas sûr de l'équivalent de Microsoft ou des autres. – yano

+1

@yano 'splice' est très cool et général mais dans le cas spécifique d'envoyer un fichier sur un socket,' sendfile' est plus facile et légèrement plus efficace, je crois. – rici

+1

@rici ah oui, je suis d'accord ,, plus facile certainement.Je suppose que 'sendFile' utilise' splice' sous le capot (pour Linux de toute façon), mais je ne sais pas. – yano

Répondre

4

Il n'existe aucun appel système normalisé pouvant écrire directement à partir d'un fichier sur un socket.

Toutefois, certains systèmes d'exploitation fournissent un tel appel. Par exemple, FreeBSD et Linux implémentent un appel système appelé sendfile, mais les détails précis diffèrent entre les deux systèmes. (Dans les deux cas, vous avez besoin du descripteur de fichier sous-jacent pour le fichier, pas le pointeur FILE*, bien que sur ces deux plates-formes, vous pouvez utiliser fileno() pour extraire le fd du FILE*.)

Pour plus d'informations:

+0

"Non portable" - 'fileno' existe dans POSIX.1-2001! –

+0

@ Antti c'est portable entre implémentations posix, certainement. Mais il n'est pas portable pour une implémentation non-posix. – rici

+0

mais alors, les sockets ne le sont pas non plus :) OP utilise presque certainement une plateforme POSIX pour écrire ce code. –

3

Ce que vous pouvez faire est d'écrire le « morceau » vous avez lu immédiatement au client. Pour écrire le contenu, vous DEVEZ le lire, donc vous ne pouvez pas l'éviter, mais vous pouvez utiliser un tampon plus petit et écrire le contenu au fur et à mesure que vous le lisez, éliminant ainsi la nécessité de lire tout le fichier dans la mémoire .

Par exemple, vous pourriez

unsigned char byte; 
// FIXME: store the return value to allow 
//  choosing the right action on error. 
// 
//  Note that `0' is not really an error. 
while (read(file, &byte, 1) > 0) { 
    if (write(client, &byte, 1) <= 0) { 
     // Handle error. 
    } 
} 

mais, unsigned char byte; pourrait être unsigned char byte[A_REASONABLE_BUFFER_SIZE]; qui serait mieux, et vous ne avez pas besoin de stocker tout le contenu dans la mémoire. }

0

Non, ce n'est pas le cas. Il doit y avoir un stockage intermédiaire que vous utilisez pour lire/écrire les données.

Il existe un cas de bordure: lorsque vous utilisez des fichiers mappés en mémoire, la région du fichier mappé peut être utilisée pour écrire dans le socket. Mais en interne, le système effectue de toute façon une opération de lecture en mémoire tampon.