2014-11-20 4 views
1

클라이언트 프로그램에서 수정할 수있는 정수를 저장하는 TCP 서버 프로그램을 작성해야합니다. 은행 계좌와 비슷해야합니다. 한 가지를 제외하고는 모두 잘 작동합니다.sprintf/snprintf가 버퍼에 올바르게 쓰지 못했습니다.

클라이언트가 처음 서버에 연결하면 환영 메시지 (서버는 반복적이어야하므로 한 번에 하나의 클라이언트 만 처리해야합니다)가 대기합니다. 서버는 항상 환영 메시지의 처음 몇 글자 만 보냅니다. 다른 모든 메시지는 완전하고 올바르게 전송됩니다.

49 번째 줄에서 환영 메시지는 먼저 char 배열로 복사 된 다음 소켓에 기록됩니다. 첫 번째 1-5 자만 보내집니다 (새 클라이언트가 연결될 때마다 다름). char 배열에 메시지를 복사 한 다음 소켓에 쓰는 sprintf()를 사용하는 다른 곳에서는 모든것이 원하는대로 작동합니다. snprintf()를 사용해 보았지만 작동하지 않습니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까? : 나는 서버에 명령을 입력 시작할 수, 그 후

Connected! 
Waiting for welcome message... 
We 

: D

그래서 이것은 클라이언트 측의 예를 출력 할 것이다. 그러나 전체 환영 메시지는 두 글자 뒤에 나옵니다. 그러나 위에서 말했듯이, 때로는 그것의 단 한 문자, 때로는 다섯 문자 : D. 어쨌든

, 여기에 (: D 내가하지 않도록해야 다른 오류 또는 물건이있는 경우, 말해 주시기 바랍니다) : 내 코드의

클라이언트 :

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <netdb.h> 
#define BufferSize 99999 

void error(const char *msg) { 
    fprintf(stderr, "%s\n", msg); 
    exit(EXIT_FAILURE); 
} 

int main(int argc, char *argv[]) { 
    int sockfd, portno, n; 
    struct sockaddr_in serv_addr; 
    struct hostent *server; 

    char msg[BufferSize], data[BufferSize]; 
    if (argc < 3) error("usage: <hostname> <port>\n"); 
    server = gethostbyname(argv[1]); 
    if (server == NULL) error("Host not found!"); 
    portno = atoi(argv[2]); 

    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd < 0) error("socket() error"); 

    bzero((char *) &serv_addr, sizeof (serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    bcopy((char *) server->h_addr, (char *) &serv_addr.sin_addr.s_addr, server->h_length); 
    serv_addr.sin_port = htons(portno); 

    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) error("connect() error"); 

    printf("Connected!\nWaiting for welcome message...\n"); 
    memset(msg, 0, BufferSize); 
    n = read(sockfd, msg, BufferSize - 1); 
    if (n < 0) error("read() error"); 
    printf("%s\n", msg); 

    memset(data, 0, BufferSize); 
    while (fgets(data, BufferSize, stdin) != NULL) { 
     data[strlen(data) - 1] = '\0'; //remove trailing newline char 
     n = write(sockfd, data, strlen(data) + 1); 
     if (n < 0) error("write() error"); 

     if (strcmp(data, "exit") == 0) break; 

     memset(msg, 0, BufferSize); 
     n = read(sockfd, msg, BufferSize - 1); 
     if (n < 0) error("read() error"); 
     if (n==0) error("Server shut down..."); 
     printf("%s\n", msg); 
     memset(data, 0, BufferSize); 
    } 

    close(sockfd); 
    return 0; 
} 

서버 :

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <limits.h> 

#define BufferSize 99999 
#define ClientWaiting 100 

void error(const char *msg) { 
    fprintf(stderr, "%s\n", msg); 
    exit(EXIT_FAILURE); 
} 

int main(int argc, char *argv[]) { 
    int sockfd, newsockfd, portno, n, amount, balance, balOld; 
    socklen_t clilen; 
    char msg[BufferSize], data[BufferSize], *splitBuf[2]; 
    struct sockaddr_in serv_addr, cli_addr; 

    balance = 0; 

    if (argc < 2) error("usage: <port>"); 
    portno = atoi(argv[1]); 

    sockfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (sockfd < 0) error("socket() error"); 

    memset(&serv_addr, 0, sizeof (serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr = INADDR_ANY; 
    serv_addr.sin_port = htons(portno); 

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) error("bind() error"); 

    listen(sockfd, ClientWaiting); 

    while (1) { 
     printf("Waiting for new client...\n"); 
     clilen = sizeof (cli_addr); 
     newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); 
     if (newsockfd < 0) error("accept() error"); 
     printf("New connection accepted...\n"); 

     memset(data, 0, BufferSize); 
     sprintf(data, "Welcome!\nPlease use the following commands:\n<put, get> <positive integer>\nBalance: %d€", balance); 
     n = write(newsockfd, data, strlen(msg) + 1); 
     if (n < 0) error("write() error"); 

     while (1) { 
      splitBuf[0] = NULL; 
      splitBuf[1] = NULL; 
      memset(data, 0, BufferSize); 
      memset(msg, 0, BufferSize); 
      n = read(newsockfd, msg, BufferSize - 1); 
      if (n < 0) { 
       fprintf(stderr, "read() error\n"); 
       break; 
      } 
      if (n == 0) { 
       printf("Client disconnected...\n"); 
       break; 
      } 

      printf("Message received: %s\n", msg); 

      if (strcmp(msg, "exit") == 0) break; 

      splitBuf[0] = strtok(msg, " "); 
      splitBuf[1] = strtok(NULL, " "); 
      if (splitBuf[1] == NULL) { 
       strcpy(data, "Please use the following commands:\n<put, get> <positive integer>"); 
      } else { 
       amount = atoi(splitBuf[1]); 
       if (amount <= 0) { 
        strcpy(data, "Please use the following commands:\n<put, get> <positive integer>"); 
       } else if (strcmp(splitBuf[0], "put") == 0) { 
        balOld = balance; 
        balance += amount; 
        if (balance < balOld) { 
         balance = INT_MAX; 
         sprintf(data, "Warning! Overflow!\nBalance: %d€", balance); 
        } else { 
         sprintf(data, "Balance: %d€", balance); 
        } 
        printf("New balance: %d€\n", balance); 
       } else if (strcmp(splitBuf[0], "get") == 0) { 
        balOld = balance; 
        balance -= amount; 
        if (balance > balOld) { 
         balance = INT_MIN; 
         sprintf(data, "Warning! Underflow!\nBalance: %d€", balance); 
        } else { 
         sprintf(data, "Balance: %d€", balance); 
        } 
        printf("New balance: %d€\n", balance); 
       } 
      } 

      n = write(newsockfd, data, strlen(data) + 1); 
      if (n < 0) error("write() error"); 
     } 
     close(newsockfd); 
    } 

    close(newsockfd); 
    close(sockfd); 
    return 0; 
} 
+2

[snprintf (3)] (http://man7.org/linux/man-pages/man3/snprintf.3.html) (또는 Linux의 경우 'glibc', [asprintf (3) ] (http://man7.org/linux/man-pages/man3/asprintf.3.html)). 그것은 문서화 된대로 작동합니다. 종종 결과를 사용해야합니다. 'gcc -Wall -Wextra -g' (모든 경고와 디버그 정보)로 두 코드를 컴파일하십시오. 그리고 ** 디버거 **를 사용하십시오 (예 : 두 개의 터미널에서 클라이언트 디버깅 용, 하나는 서버 디버깅 용). 모든 함수를 실패에 대해 테스트하십시오 (실패시'perror' 사용). –

+0

흠, 잘 snprintf와 나는 똑같은 문제가 : 그것은 환영 메시지와 함께 제대로 작동하지 않지만, 괜찮아요 내가 다른 메시지와 함께 똑같은 방식으로 문자 배열에 복사합니다. 그리고 크기가 고정 된 char 배열이므로 asprintf()는 실제로 적용되지 않습니다. 맞습니까? : D – user2336377

+2

환영 메시지를 쓸 때,'data'의 내용을 쓰고 있지만'strlen (msg) + 1'을 사용하여 길이를 얻습니다 ... 틀린 것 같습니다. – Dmitri

답변

1

귀하의 문제는 귀하의 첫 번째 read() 호출에 있다고 생각합니다. 모든 환영 메시지를 받기를 기대하면서 독신으로 읽습니다. 그러나 이것이 TCP가 작동하는 방식이 아닙니다. TCP는 스트림 데이터를 자체 내부 규칙에 따라 패킷에 저장하고 수신 시스템은 원하는 모든 양으로 스트림 데이터를 사용할 수 있습니다.

서버가 단일 읽기에서 쓴 모든 데이터를 가져올 수는 없습니다.

사실 서버가 엉망입니다. 당신이 말하는 모든 것을 쓸 수있는 전화를 기대할 수는 없습니다. 소켓에 대한 운영 체제의 송신 버퍼가 가득 찼거나 신호 호출에 인터럽트가있을 수 있습니다.

전체 라인을 수신하거나 전체 버퍼를 보낼 때까지 계속되는 읽기 및 쓰기 루프를 처리하려면 보내기 및 받기 버퍼와 함수가 필요합니다.

+0

흠, 우리 대학에서 배웠던 방법입니다. D 정확히 어떻게 그렇게할까요? 내 메시지가 완전히 전송되었는지 어떻게 알 수 있습니까? 내 메시지 길이가 같아 질 때까지 write()를 반복하고 리턴 값을 확인해야합니까? 그리고 상대방이 전체 메시지를 받았는지 확인하는 방법은 무엇입니까? null-terminator를 얻을 때까지 read()를 반복 할 수 있을까요? – user2336377

+1

@ user2336377 : read() 및 write()에서 문서를 읽습니다. 그들은받은 바이트 수를 반환합니다. 이 번호는 함수 호출에서 요청한 번호와 항상 일치하지 않을 수도 있습니다. TCP 스트림은 줄 바꿈 문자를 찾거나, messag 앞에 바이트 카운트를 보내거나 닫을 소켓을 찾아야합니다. 아니 널 때까지 반복 할 수 없습니다. –

2

이 예제는 SO에서 디버깅하기에는 너무 길다. (우리는 컴파일러와 디버거가 아닙니다.)

첫 번째 단계는 프로그램을 더 작고 이해하기 쉽게 분해하여 독립적으로 디버깅 할 수 있어야합니다.것을 수행하는 몇 가지 방법이 있습니다

  • 보다 쉽게 ​​이해하고 독립적으로 검증 기능 예를 들어

에 코드를 깨는 당신의 주장

  • 를 테스트 체크 포인트를 추가, 나는이 보면 :

     splitBuf[0] = strtok(msg, " "); 
         splitBuf[1] = strtok(NULL, " "); 
    

    예상 검색어에 splitBuf가 포함되어 있습니까? (NULL 유효한 매개 변수는 strtok를 할 수 있습니까?)

    을 나는 두 가지 추천 : 그 DEBUG가 정의되어 있는지 확인하거나 추가 -DDEBUG = 1 사용법 #include

    # Are my assumptions met? 
    assert(splitBuf[0]!=null); 
    assert(splitBuf[1]!=null); 
    #ifdef DEBUG 
         printf("splitBuf[0]=%s\n", splitBuf[0]); 
         printf("splitBuf[1]=%s\n", splitBuf[1]); 
    #endif 
    

    컴파일 :

    #define DEBUG 
    

    파일의 맨 위에 있습니다.

    둘째, 작은 문제를 해결하고 프로그래밍 작업을 수행 한 다음 문제를 독립적으로 해결하고 테스트 해보십시오. 이제 네트워크에서 메시지를 분석하고 균형을 추출해야한다고 가정 해 봅시다, 당신은 작성할 수 있습니다 : 이제 테스트 쓸 수

    int parseBalance(char const* serverMessage) { 
        ... 
        return balance; 
    } 
    

    :

    void tests() 
    { 
        // test parseBalance 
        assert(100 == parseBalance("100")) 
        ... more tests 
    } 
    

    최소한, 당신은 테스트를 호출 할 수있다() 프로그램 시작시 자체 테스트를 수행하십시오 (더 나은 접근을 위해 "단위 테스트"에서 읽음).

    이 방법으로 프로그래밍 할 경우 : 당신이 붙어 있고 SO, 당신은 단지 최소한의 기능과 테스트 케이스를 게시 할 필요 게시 할 경우

    • 종종 문제가 더 쉽게
    • 명백해질 것이다.
  • 관련 문제