2009-07-11 8 views
2

J'essaie d'envoyer une chaîne à une application serveur en utilisant C, mais j'ai rencontré un problème. Je suis relativement nouveau dans la programmation réseau, et je pense que mon code aboie dans le mauvais arbre.Envoi de la longueur du tampon et de la mémoire tampon sur la socket dans c

Le message est censé être la longueur du message + message et est décompressé de l'autre côté par un serveur python en tant que tel (BUF étant les données entrantes brutes):

msg_len_bytes = buf[0:4] 
msg_len = struct.unpack("!L", msg_len_bytes)[0] 

! signifie l'ordre des octets du réseau et L signifie non signé long.

Il est assez simple d'envoyer une chaîne régulière. envoyer (chaussette, message, strlen (message), 0);

Mais en ajoutant la longueur du message, je n'arrive pas à comprendre. Voici le code pour mon client jusqu'à présent:

struct msgstruct { 
     uint32_t length; 
     char send_data[4096]; 
}; 

int main() 

{ 
    int sock; 
    struct msgstruct message; 
    char data[4096] = "<MOP><test/></MOP>"; 

    for (int i = 0; i < strlen(data); i++) { 
     message.send_data[i] = data[1]; 
    } 

    struct hostent *host; 
    struct sockaddr_in server_addr; 

    unsigned long buflen = sizeof(message.send_data); 
    uint32_t bufsend = htonl(buflen); 

    message.length = bufsend; 

    host = gethostbyname("127.0.0.1"); 

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 
     perror("Socket"); 
     exit(1); 
    } 

    server_addr.sin_family = AF_INET;  
    server_addr.sin_port = htons(12998); 
    server_addr.sin_addr = *((struct in_addr *)host->h_addr); 
    bzero(&(server_addr.sin_zero),8); 


    if (connect(sock, (struct sockaddr *)&server_addr, 
       sizeof(struct sockaddr)) == -1) { 
     perror("Connect"); 
     exit(1); 
    } 

    if(send(sock, message.length + message.send_data, sizeof(message), 0) == -1){ 
     printf("\nSocket error."); 
     exit(1); 
    } 
    return 0; 
} 

J'ai essayé quelques variations, mais je finis toujours avec une erreur de socket. Est-ce parce que je mélange des types dans le second argument pour envoyer? Je reçois des erreurs de compilation si j'essaye d'envoyer la structure.

+1

Si vous êtes satisfait des réponses fournies, s'il vous plaît choisir un (moi je l'espère!) Comme « réponse correcte » avec la coche. Cela vaut 15 rep. pointe vers moi, et rend la question plus utile dans les futures références. – abelenky

Répondre

4

L'erreur est dans ce code:

send(sock, message.length + message.send_data, sizeof(message), 0) 

Le prototype est envoyer:

ssize_t send(int s, const void *buf, size_t len, int flags); 

Notez que le paramètre 2 est un pointeur. Dans votre code, vous l'avez donné en tant que longueur (Type uint32_t) ajouté à un tampon (Type char *). Cet ajout entraînera un char *, (pointeur vers un caractère) mais un pointeur vers une zone de mémoire imprévisible et insignifiante.

Pour obtenir un pointeur vers la mémoire tampon, vous voulez:

send(sock, &message, sizeof(message), 0) 

Notez que prendre l'adresse d'un struct n'est pas portable ou toujours conseillé, en raison de problèmes de remplissage. Mais sur une architecture 32 bits typique, cela devrait être bon.

Cela enverra des données à partir de la structure du message, mais en envoyant 4100 (4096 + 4) octets !. Je ne pense pas que vous ayez l'intention d'envoyer autant. Le 3ème paramètre indique le nombre d'octets à envoyer, et doit être réglé sur:

sizeof(uint32_t) + strlen(data); // 4-byte Integer + Length of the data "<MOP><test/></MOP>" 

Notez que cela ne pas comprennent un Null-Terminator pour les données, mais que votre initiale en boucle n'a pas copier un null-Terminator soit

(Si vous voulez que le null terminateur, faites votre première boucle pour aller à strlen (données) +1 et utiliser strlen (données) +1 dans d'autres endroits ainsi que).

Idéalement, vous devriez mettre en cache strlen (données) dans une variable locale, et ne pas l'appeler autant. (vous appelez aussi strlen à plusieurs reprises dans la boucle initiale inital).

Votre déclaration finale ressemblera:

if(send(sock, &message, sizeof(uint32_t)+strlen(data), 0) == -1){ 
    printf("\nSocket error."); 
    exit(1); 
} 

essayer, et laissez-moi savoir comment ça se passe.

+0

Hmm, quand je fais cela, il semble envoyer une grande quantité d'ordures au serveur (et conduit mon ordinateur PC détraqué). –

+0

Ce qui se dit aussi. Vous avez une donnée [1] où vous devriez avoir des données [i]! – abelenky

+0

désolé, a commenté sur le mauvais message –

0

La boucle qui copie les données dans l'instance message est cassée, elle a un 1 où elle doit avoir un i.

Le second argument à send() doit être un pointeur vers le premier octet à envoyer. Vous lui donnez plutôt la somme d'un nombre big-endian (qui, si votre plate-forme n'est pas big-endian, sera très mauvais) et une adresse de base de tableau aléatoire. C'est faux.

Vous devez:

if(send(sock, &message, sizeof message.length + strlen(data), 0) == -1) { 

Il y a aussi une autre confusion, vous semblez « vouloir » envoyer toujours le plein 4K, plutôt que la quantité nécessaire.

5

Vous pouvez utiliser 2 ultérieure envoie:

send(sock, &message.length, sizeof(message.length), 0); 
send(sock, message.send_data, message.length*sizeof(char), 0); 

Ou mieux préparer tampon avec 4 premiers octets que la longueur du message:

char buff[MAX_BUFF] = ""; 
int len_disp = sizeof(message.length); 
memcpy(buff, &message.length, len_disp); 
memcpy(&buff[len_disp], &message.length, message.length*sizeof(char)); 
send(sock, buff, message.length*sizeof(char) + len_disp, 0); 

EDIT: Pour les petits messages commenter - désactiver l'algorithme de Nagle.

BOOL bNagleEnabled = FALSE; 
if(setsockopt(sAccept, IPPROTO_TCP, TCP_NODELAY, (char *)&bNagleEnabled, sizeof(BOOL))) 
{ 
    ReportError("Setting TCP_NODELAY socket option failed"); 
    return -2; 
} 
+1

+1 sur l'utilisation d'envois séparés pour la longueur et les données. Ceci est particulièrement important si le compilateur a l'impression d'insérer des données entre la longueur et le tampon. –

+0

Y at-il vraiment aucune bibliothèque qui fait cela pour vous? Sinon, j'en fais un sauf avec une quantité de longueur variable comme en-tête de taille ... – sudo

0

Fondamentalement tout devrait tourner tampon et envoyer/recevoir par socket. Donc, en général ce que je fais:

//Send 
memcpy(buffer, &structure_u_defined, sizeof(structure_u_defined)); 
send(&sock, buffer,...);// lol I am using a public pc 

//receive 
recv(&sock, buffer,...); 
memcpy(&structure_u_defined_for_receive, buffer, sizeof(buffer)); 
Questions connexes