2016-02-23 37 views
6

Próbuję napisać program serwera klienta za pomocą protokołu UDP i czekać i zatrzymać, ale nie mam do tej części, nadal próbuję to sobie wyobrazić jak oba komunikaty (serwer i klient) komunikują się, ponieważ w moim programie klienckim użytkownik musi wprowadzić nazwę serwera lub adres IP i nazwę portu, a następnie wysłać wyrażenie, które serwer powinien obliczyć. Jednak wykopałem kilka tutoriali w Internecie i po odpowiednim kodowaniu (lub tak mi się wydawało) nie mogę sprawić, aby klient komunikował się z serwerem. Poniżej znajduje się mój kod, proszę oświecić mnie, co robię źle, jeśli jest to bind(), ,lub lub wszystkie z nich. Nie widzę, co dokładnie jest nie tak. Wiem, że strona po stronie klienta nie powinna działać w nieskończonej pętli, ale do tej pory chcę, aby programy komunikowały się ze sobą, a potem poleruję mój kod. Dzięki!Programowanie gniazd: serwer klienta UDP w C

kod po stronie klienta:

#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; 
} 

kod po stronie serwera:

#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; 
} 

Program serwer nie wyjście w ogóle, natomiast wyjść klienckich, aż proces zostanie przerwany:

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 

Próbowałem zmienić nazwę serwera na localhost i inne porty, ale bez skutku.

+2

„Nie mogę sprawić, że klient komunikuje się z serwerem.”. Czy możesz być trochę bardziej konkretny? Czy program się zawiesza, czy nie generuje wyjścia, czy generuje błędne wyjście? itd. Uwzględnij także wszelkie ustalenia dotyczące debugowania, które już zostały zebrane. – kaylum

+0

Przechodzenie do edycji danych wyjściowych w pytaniu –

+1

Twój klient musi wysłać do tego samego portu, do którego serwer się zobowiązał. – kaylum

Odpowiedz

12

Podczas tworzenia oprogramowania sieciowego (szczególnie w przypadku korzystania z interfejsu gniazda BSD) ważne jest, aby wszystko było tak proste, jak to tylko możliwe, do momentu nawiązania podstawowej komunikacji. Następnie możesz stopniowo dodawać funkcje, upewniając się, że niczego nie zepsujesz po drodze.

Po stronie klienta, utrzymanie rzeczy proste środki

  • Nie nazywaj bind w kliencie. System operacyjny wybierze odpowiedni interfejs i przypisze mu losowy numer portu, więc nie ma potrzeby instalowania gniazda bind.

  • Użyj zakodowanego na sztywno adresu serwera (na przykład 127.0.0.1). Adres 127.0.0.1 (0x7f000001) to lokalny adres hosta, odpowiedni do wysyłania pakietów do serwera na tym samym komputerze.

  • Użyj zakodowanego na stałe numeru portu (np. 50037). Ephemeral port numbers powinien być większy niż 0xC000 w zapisie szesnastkowym (49152 dziesiętny).

  • Użyj zakodowanej na stałe wiadomości, np. "cześć".

Mając to na uwadze, oto co oprogramowanie klienckie wygląda

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); 
} 

po stronie serwera, utrzymując rzeczy proste środki

  • Bind do INADDR_ANY, czyli niech wybierać OS odpowiedni interfejs.
  • Powiąż z zakodowanym na stałe portem, np. 50037 (musi być tym samym portem, którego używa klient).
  • Nie żądaj informacji o adresie od recvfrom, tj. Podaj NULL, 0 jako dwa ostatnie parametry.

Mając to na uwadze, oto co oprogramowanie serwera wygląda

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

W aplikacjach "zabawkowych" wątpię, by ktokolwiek wpadł na rzęsy, ale ogólnie: nie używaj efemerycznych portów. Jeśli zgłosisz w Google temat, znajdziesz przypadki, w których ludzie to zrobili i mieli problemy lub możesz po prostu skorzystać z ich porad: http://books.google.si/books?id=Pm4RgYV2w4YC&pg=PA705&lpg=PA705&dq=use%20Ephemeral % 20port% 20 dla% 20server & source = bl & ots = qwnYpJGseD & sig = 3EihPoqMeNH06ge5lChaRmi9hFg & hl = en & sa = X & ei = JoZsU4GBJ4iL7Ab-yICYCA & redir_esc = y # v = onepage & q = użyj% 20Ememeral% 20port% 20for% 20server & f = false –