2016-02-23 25 views
6

Sto provando a scrivere un programma server client usando UDP, e wait-and-stop, ma non ho ottenuto quella parte, sto ancora cercando di capirlo come i due processi (server e client) comunicano, perché sul mio programma client, l'utente deve inserire il nome del server o l'indirizzo IP e il nome di una porta, e quindi inviare un'espressione che il server dovrebbe calcolare. Tuttavia, ho scavato alcuni tutorial in internet e dopo aver codificato di conseguenza (o ho pensato così) non posso far comunicare il client con il server. Di seguito è riportato il mio codice, per favore chiariscimi cosa sto facendo male, se è il bind(), sendto(), recvfrom() o socket(), o tutti loro. Non riesco a vedere cosa sia esattamente sbagliato. So che il lato client non dovrebbe funzionare su un ciclo infinito, ma finora voglio far comunicare i programmi tra loro, dopodiché pulirò il mio codice. Grazie!Programmazione socket: UDP Client-Server in C

codice lato client:

#include <stdio.h>  // Default System Calls 
#include <stdlib.h>  // Needed for OS X 
#include <string.h>  // Needed for Strlen 
#include <sys/socket.h> // Needed for socket creating and binding 
#include <netinet/in.h> // Needed to use struct sockaddr_in 
#include <time.h>  // To control the timeout mechanism 

#define EXPR_SIZE 1024 
#define BUFLEN  512 
#define TRUE  1 
#define FALSE  0 
#define SERVERLEN 1024 

int main(int argc, char **argv){ 

    long portNum;   // Since it's possible to input a value bigger 
          // than 65535 we'll be using long to 
          // avoid overflows 
    char expr[EXPR_SIZE]; 
    char server[SERVERLEN]; 
    int fd;     // file descriptor for the connected socket 
    int buf[512]; 
    struct hostent *h;   // information of the host 
    unsigned int addrLen;  // address length after getting the port number 
    struct sockaddr_in myaddr; // address of the client 
    struct sockaddr_in servaddr; // server's address 
    unsigned int exprLen; 
    socklen_t slen = sizeof(servaddr); 

    printf("Enter server name or IP address:"); 
    scanf("%s",server); 
    printf("Enter port:"); 
    scanf("%ld",&portNum); 
    if ((portNum < 0) || (portNum > 65535)) { 
     printf("Invalid port number. Terminating."); 
     return 0; 
    } 
    printf("Enter expression:"); 
    scanf("%s",expr); 

    if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ 
     perror("cannot create socket"); 
     return 0; 
    } 

    memset((char *)&myaddr, 0, sizeof(myaddr)); 
    myaddr.sin_family = AF_INET; 
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    myaddr.sin_port = htons(0); 

    if(bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0){ 
     perror("cannot bind"); 
     return 0; 
    } 

    /* 
    // Discovering the port number the OS allocated 
    addrLen = sizeof(myaddr); 
    if(getsockname(fd, (struct sockaddr *)&myaddr, &addrLen) < 0){ 
    perror("cannot getsockname"); 
    return 0; 
    } 
    printf("local port number = %d\n", ntohs(myaddr.sin_port)); 
    */ 

    memset((char*)&servaddr, 0, sizeof(servaddr)); 
    servaddr.sin_family = AF_INET; 
    servaddr.sin_port = htonl(portNum); 

    exprLen = sizeof(expr); 


    while(TRUE){ 
     printf("Sending message to %s port %ld\n",server, portNum); 
     if (sendto(fd, expr, strlen(expr), 0, (struct sockaddr *)&servaddr, slen) < 0) { 
      perror("cannot sendto()"); 
     } 
     printf("Success\n"); 

    } 


    return 0; 
} 

codice lato server:

#include <stdio.h>  // Default System Calls 
#include <stdlib.h>  // Needed for OS X 
#include <string.h>  // Needed for Strlen 
#include <sys/socket.h> // Needed for socket creating and binding 
#include <netinet/in.h> // Needed to use struct sockaddr_in 
#include <time.h>  // To control the timeout mechanism 

#define EXPR_SIZE 1024 
#define BUFLEN  512 
#define TRUE  1 
#define SERVERLEN 1024 

int main(int argc, char **argv){ 

    struct sockaddr_in myaddr; // address of the server 
    struct sockaddr_in claddr; // address of the client 
    char buf[BUFLEN]; 
    int fd; 
    long recvlen; 
    socklen_t clientlen; 



    if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ 
     perror("cannot create socket"); 
     return 0; 
    } 

    memset((char *)&myaddr, 0, sizeof(myaddr)); 
    myaddr.sin_family = AF_INET; 
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    myaddr.sin_port = htons(0); 

    if(bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0){ 
     perror("cannot bind"); 
     return 0; 
    } 
    clientlen = sizeof(claddr); 

    while (TRUE) { 
     recvlen = recvfrom(fd, buf, BUFLEN, 0, (struct sockaddr *)&claddr, &clientlen); 
     if (recvlen < 0) { 
      perror("cannot recvfrom()"); 
      return 0; 
     } 
     printf("Received %ld bytes\n",recvlen); 
     buf[recvlen] = 0; 
     printf("Received message: \"%s\"\n",buf); 

    } 

    return 0; 
} 

Il programma server non uscita nulla, mentre le uscite del cliente fino a quando il processo viene interrotto:

Enter server name or IP address:127.0.0.1 
Enter port:30 
Enter expression:2+2 
Sending message to 127.0.0.1 port 30 
cannot sendto(): Can't assign requested address 

Ho provato a cambiare il nome del server in localhost e altre porte, ma inutilmente.

+2

"Non può fare il cliente comunicare con il server.". Potresti essere un po 'specifico? Il programma si blocca, non produce output, genera output errato? ecc. Comprende anche tutti i risultati di debug che hai già raccolto. – kaylum

+0

Passare alla modifica dell'output nella domanda –

+1

Il client deve inviare alla stessa porta a cui è collegato il server. – kaylum

risposta

12

Durante lo sviluppo di software di rete (specialmente quando si utilizza l'interfaccia socket BSD), è importante mantenere le cose il più semplice possibile finché non si sono stabilite le comunicazioni di base. Quindi è possibile aggiungere in modo incrementale funzionalità, assicurandosi che non si rompa nulla lungo la strada.

Sul lato client, mantenere le cose semplici mezzi

  • non chiamare bind nel client. Il sistema operativo sceglierà un'interfaccia appropriata e assegnerà un numero di porta casuale, quindi non è necessario il socket bind.

  • Utilizzare un indirizzo del server hardcoded (ad esempio 127.0.0.1). L'indirizzo 127.0.0.1 (0x7f000001) è l'indirizzo host locale, adatto per l'invio di pacchetti a un server sulla stessa macchina.

  • Utilizzare un numero di porta hardcoded (ad esempio 50037). Ephemeral port numbers deve essere maggiore di 0xC000 esadecimale (49152 decimale).

  • Utilizzare un messaggio codificato, ad es. "Ciao".

Con questo in mente, ecco cosa il software client si presenta come

int main(void) 
{ 
    int fd; 
    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 
     perror("socket failed"); 
     return 1; 
    } 

    struct sockaddr_in serveraddr; 
    memset(&serveraddr, 0, sizeof(serveraddr)); 
    serveraddr.sin_family = AF_INET; 
    serveraddr.sin_port = htons(50037);    
    serveraddr.sin_addr.s_addr = htonl(0x7f000001); 

    for (int i = 0; i < 4; i++) { 
     if (sendto(fd, "hello", 5, 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { 
      perror("sendto failed"); 
      break; 
     } 
     printf("message sent\n"); 
    } 

    close(fd); 
} 

Sul lato server, mantenere le cose semplici mezzi

  • Bind a INADDR_ANY, cioè lasciare che il pick OS un'interfaccia appropriata.
  • Associare a una porta hardcoded, ad es. 50037 (deve essere la stessa porta utilizzata dal client).
  • Non richiedere le informazioni sull'indirizzo recvfrom, ovvero passare NULL, 0 come gli ultimi due parametri.

Con questo in mente, qui è quello che il software del server sembra

int main(void) 
{ 
    int fd; 
    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 
     perror("socket failed"); 
     return 1; 
    } 

    struct sockaddr_in serveraddr; 
    memset(&serveraddr, 0, sizeof(serveraddr)); 
    serveraddr.sin_family = AF_INET; 
    serveraddr.sin_port = htons(50037); 
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 

    if (bind(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) { 
     perror("bind failed"); 
     return 1; 
    } 

    char buffer[200]; 
    for (int i = 0; i < 4; i++) { 
     int length = recvfrom(fd, buffer, sizeof(buffer) - 1, 0, NULL, 0); 
     if (length < 0) { 
      perror("recvfrom failed"); 
      break; 
     } 
     buffer[length] = '\0'; 
     printf("%d bytes: '%s'\n", length, buffer); 
    } 

    close(fd); 
} 
+0

Nelle applicazioni "giocattolo" dubito che qualcuno possa battere ciglio, ma in generale: non usare porte effimere. Se cerchi su Google l'argomento, troverai casi in cui le persone lo hanno fatto e hanno avuto problemi, oppure puoi semplicemente dare il loro consiglio: http://books.google.si/books?id=Pm4RgYV2w4YC&pg=PA705&lpg=PA705&dq=use%20Ephemeral % 20port% 20for% 20server & source = bl & OTS = qwnYpJGseD & sig = 3EihPoqMeNH06ge5lChaRmi9hFg & hl = it & sa = X & ei = JoZsU4GBJ4iL7Ab-yICYCA & redir_esc = y # v = onepage & q = utilizzare% 20Ephemeral% 20port% 20for% 20server & f = false –