2017-10-17 1 views
0

J'ai essayé d'écrire un serveur HTTP java à partir de zéro pour mon programme de chat qui envoie des données sur les Websockets et tout s'est bien passé jusqu'à ce que je tente d'envoyer de grandes images avec mon programme en utilisant google chrome. J'ai découvert que lorsque Chrome envoie plusieurs images de données à mon serveur, il n'envoie jamais la bonne longueur de données. La première image est toujours correcte, mais toutes les images après l'initiale envoient un nombre significativement plus petit que les données contenues dans l'image. Par exemple, il enverra une trame indiquant qu'il contient 123 octets de données, alors qu'il envoie en réalité plus de 100 kilo-octets. J'ai testé d'autres navigateurs comme Firefox et Safari (sur mon téléphone) et ils ne semblent pas diviser de grandes données dans des cadres comme le fait le chrome, donc ils n'ont pas le même problème. Voici quelques captures d'écran de mes résultats: cadre initial: enter image description hereProblèmes avec l'envoi de données volumineuses sur Websockets avec Chrome

cadres suivants (C'est là où il échoue): enter image description here

Et voici mon code côté serveur qui récupère les données à partir d'un flux d'entrée et écrit dans un fichier:

byte[] headerdata = new byte[12]; 
input.read(headerdata, 0, 2); 
int frame = (headerdata[0] & 0b10000000) & 0xFF; 
long length = (headerdata[1] & 0b01111111) & 0xFF; 
System.out.format("Before: %d, Frame: %d%n", length, frame); 
if(length == 127){ 
    input.read(headerdata, 0, 12); 
    length = ((long)(headerdata[0] << 56) & 0xFFFFFFFFFFFFFFFFL | (long)(headerdata[1] << 48) & 0xFFFFFFFFFFFFFFL | (long)(headerdata[2] << 40) & 0xFFFFFFFFFFFFL | (long)(headerdata[3] << 32) & 0xFFFFFFFFFFL | (headerdata[4] << 24) & 0xFFFFFFFFL | (headerdata[5] << 16) & 0xFFFFFFL | (headerdata[6] << 8) & 0xFFFFL | headerdata[7] & 0xFFL) 
} else if(length == 126){ 
    input.read(headerdata, 6, 6); 
    length = ((headerdata[6] << 8) & 0xFFFF | headerdata[7] & 0xFF); 
}else{ 
    input.read(headerdata, 8, 4); 
} 
System.out.println("After: " + length); 

int count; 
int total = 0; 
byte[] buffer = new byte[16384]; 
boolean done = false; 
while(done == false){ 
    count = input.read(buffer, 0, buffer.length); 
    //System.out.println(count); 
    for(int i = 0; i < count; i++){ 
     buffer[i] = (byte)(buffer[i]^headerdata[(total & 3) + 8]); 
     total++; 
    } 
    fileoutput.write(buffer, 0, count); 
    System.out.format("Count: %d Left: %d%n", count, (length - total)); 
    if(total >= length){ 
     done = true; 
    } 
} 

if(frame == 128) break; 

Toute explication à laquelle je pourrais manquerais de lire la longueur des cadres suivants seraient grandement appréciés (j'ai quelques soupçons de ma propre aussi bien). Merci pour votre temps et vos efforts.

+0

Vous continuez à supposer que 'read()' remplit le tampon, sans vérifier la fin du flux ou une lecture courte. Utilisez 'DataInputStream.readFully()': en fait certaines de ces lectures pourraient être faites avec 'readInt()' et des amis. – EJP

Répondre

-1

J'ai découvert que la mémoire tampon finissait par déborder, ce qui entraînait une mauvaise lecture ou une perte des données, ce qui expliquait pourquoi je recevais des valeurs de longueur incorrectes (merci EJP). Je l'ai corrigé en ajoutant une instruction if qui vérifie si le nombre d'octets lus approche la valeur de longueur de cette image, puis ne lit que ce qui reste à la place de la taille de la mémoire tampon pour l'empêcher de déborder. Voici le code avec la modification:

byte[] headerdata = new byte[12]; 
       input.read(headerdata, 0, 2); 
       int frame = (headerdata[0] & 0b10000000) & 0xFF; 
       long length = (headerdata[1] & 0b01111111) & 0xFF; 
       System.out.format("Before: %d, Frame: %d%n", length, frame); 
       if(length == 127){ 
        input.read(headerdata, 0, 12); 
        length = ((long)(headerdata[0] << 56) & 0xFFFFFFFFFFFFFFFFL | (long)(headerdata[1] << 48) & 0xFFFFFFFFFFFFFFL | (long)(headerdata[2] << 40) & 0xFFFFFFFFFFFFL | (long)(headerdata[3] << 32) & 0xFFFFFFFFFFL | (headerdata[4] << 24) & 0xFFFFFFFFL | (headerdata[5] << 16) & 0xFFFFFFL | (headerdata[6] << 8) & 0xFFFFL | headerdata[7] & 0xFFL); 
       }else if(length == 126){ 
        input.read(headerdata, 6, 6); 
        length = ((headerdata[6] << 8) & 0xFFFF | headerdata[7] & 0xFF); 
       }else{ 
        input.read(headerdata, 8, 4); 
       } 
       System.out.println("After: " + length); 

       int count; 
       int total = 0; 
       byte[] buffer = new byte[16384]; 
       boolean done = false; 
       while(done == false){ 
        if(length - total > buffer.length) count = input.read(buffer, 0, buffer.length); 
        else count = input.read(buffer, 0, (int) (length - total)); 
        //System.out.println(count); 
        for(int i = 0; i < count; i++){ 
         buffer[i] = (byte)(buffer[i]^headerdata[(total & 3) + 8]); 
         total++; 
        } 
        fileoutput.write(buffer, 0, count); 
        //System.out.format("Count: %d Left: %d%n", count, (length - total)); 
        if(total >= length){ 
         done = true; 
        } 
       } 

       if(frame == 128) break; 

J'ai aussi fait des recherches sur les différentes façons de lire des flux d'octets à partir d'une prise et a trouvé qu'il serait également possible d'utiliser DataInputStream.readFully() (Merci EJP).

+0

Vous êtes * toujours * en supposant que les lectures remplissent le tampon et * toujours * en ignorant les lectures en fin de flux et les lectures courtes. Vous devez réparer cela. – EJP

+0

Je suis confus. Le code que j'ai partagé continue à lire jusqu'à ce qu'il lise le nombre exact d'octets de la valeur de longueur envoyée dans le cadre. Mon problème venait d'une lecture excessive, qui a entraîné l'omission de l'en-tête de l'image suivante. Je ne peux pas vérifier la fin du flux car il se bloque en attente, ce que je suppose est parce que le websocket reste ouvert après l'envoi des données, et je ne comprends pas ce que vous entendez par des lectures courtes. – Anonymous