2012-08-26 45 views
0

我一直在努力學習unix網絡編程,所以我試圖編寫一個客戶端/服務器程序,其中客戶端發送消息和服務器返回轉換爲大寫字母的郵件。unix tcp客戶端/服務器怪異行爲

當我運行服務器並將客戶端連接到我自己的機器併發送一些測試字符串時,它可以正常工作,直到我以前輸入的內容寫入屏幕。

我懷疑它與緩衝區有關。這裏的樣品運行後,我啓動服務器:

CAN @ Ubuntu的:〜$ CD桌面

CAN @ Ubuntu的:〜/桌面$ ./tcpuppcli 127.0.0.1

輸入字符串呼應:測試

回波響應:TEST

輸入字符串呼應:字符串2

回波響應:STRING2

輸入字符串呼應:STRING3

回波響應:STRING3

輸入字符串呼應:aaaaaaaaafsfagd

回波響應:AAAAAAAAAFSFAGD

輸入字符串呼應:gdsgsg

回聲響應:GDSGSG

AAFS FAGD ----------->!這是來自先前輸入的字符的奇怪行!

輸入字符串呼應:^ C

CAN @ Ubuntu的:〜/桌面$

服務器和客戶端的代碼如下:

// methods with prefix w_ are just error checking wrappers from UNP by Stevens  
// server 
#include "socketwrap.h" // headers necessary and constants like MAXLINE 
#include <ctype.h> 

void sigchld_handler(int sig); 
void str_upper(int connfd); 
void toUpperCase(char buffer[], int length); 

int main(int argc, char** argv) 
{ 
    int listenfd; 
    int connfd; 
    pid_t childpid; 
    struct sockaddr_in serverAddress; 
    struct sockaddr_in clientAddress; 
    socklen_t length; 
    struct sigaction sa; 

    // Create the socket 
    listenfd = w_socket(AF_INET, SOCK_STREAM, 0); 

    // Clear the serverAddress structure 
    bzero(&serverAddress, sizeof(serverAddress)); 
    // Set up the serverAddress structure 
    serverAddress.sin_family = AF_INET; 
    serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); 
    serverAddress.sin_port = htons(11979); 

    // Bind the socket to a well-defined port 
    w_bind(listenfd, (struct sockaddr*) &serverAddress, sizeof(serverAddress)); 

    // Start listening for connections 
    w_listen(listenfd, BACKLOG); 

    // Handle any zombie children by using a signal handler 
    sa.sa_handler = sigchld_handler; 
    sigemptyset(&sa.sa_mask); 
    sa.sa_flags = SA_RESTART; 
    if(sigaction(SIGCHLD, &sa, NULL) == -1) 
    { 
    perror("signal error"); 
    exit(1); 
    } 

    printf("Waiting for connections...\n"); 

    while(1) 
    { 
     length = sizeof(clientAddress); 
     connfd = w_accept(listenfd, (struct sockaddr*) &clientAddress, &length); 
     if(connfd < 0) 
     { 
      if(errno == EINTR) 
      { 
      continue; // back to while 
      } 
      else 
      { 
      perror("accept error"); 
      exit(1); 
      } 
     } 

     printf("Obtained connection...\n"); 
     childpid = fork(); 
     if (childpid == 0) /* child process */ 
     { 
     w_close(listenfd); /* close listening socket */ 
     str_upper(connfd); // process the request 
     exit(0); 
     } 

    w_close(connfd); /* parent closes connected socket */ 
    } 

} 

void sigchld_handler(int sig) 
{ 
    while(waitpid(-1, NULL, WNOHANG) > 0); 
} 

void str_upper(int connfd) 
{ 
    char buffer[MAXLINE]; 
    while(read(connfd, buffer, MAXLINE - 1) > 0) 
    { 
    toUpperCase(buffer, strlen(buffer)); 
    write(connfd, buffer, strlen(buffer)); 
    } 
} 

void toUpperCase(char buffer[], int length) 
{ 
    int i; 
    for(i = 0; i < length - 1; i++) 
    { 
     buffer[i] = toupper(buffer[i]); 
    } 
} 

// client 
#include "socketwrap.h" 

void str_cli(int connfd); 

int main(int argc, char** argv) 
{ 
    int sockfd; 
    struct sockaddr_in serverAddress; 

    if(argc != 2) 
    { 
     printf("Invalid argument count\n"); 
     printf("Correct usage: tcpcli4 <IPaddress>\n"); 
     exit(1); 
    } 


    sockfd = w_socket(AF_INET, SOCK_STREAM, 0); 
    bzero(&serverAddress, sizeof(serverAddress)); 
    serverAddress.sin_family = AF_INET; 
    serverAddress.sin_port = htons(11979); 
    if(inet_pton(AF_INET, argv[1], &serverAddress.sin_addr) <= 0) 
    { 
     perror("inet_pton error"); 
     exit(1); 
    } 

    w_connect(sockfd, (struct sockaddr*) &serverAddress, sizeof(serverAddress)); 

    str_cli(sockfd); 
    exit(0); 
} 

void str_cli(int connfd) 
{ 
    char buffer[MAXLINE]; 

    printf("Enter the string to echo: "); 
    while(fgets(buffer, MAXLINE, stdin) > 0) 
    { 

    // Send string to echo server, and retrieve response 
    write(connfd, buffer, strlen(buffer)); 
    read(connfd, buffer, MAXLINE - 1); 

    // Output echoed string 
    printf("Echo response: %s\n", buffer); 
    printf("Enter the string to echo: "); 
    } 
} 

如果您需要更多信息或有什麼不清楚的請通知我

感謝所有回覆

+0

嗯。您是否在任何時候給予更長的輸入,然後輸入更短的時間呢? – aschepler

+0

嘗試過,是的它沒有問題,如果輸入變得越來越長,但長,然後較短,這樣做。 – john27

回答

2

read()不會將一個'\0'終止符添加到字符串中,因此您的strlen()調用的結果沒有明確定義。您需要使用返回值read()(將其存儲在一個變量中,不要僅僅測試> 0然後將其丟棄)來知道該字符串有多長。

它似乎在一段時間內正常工作,但這只是'\0' s,它在未初始化時處於緩衝區。在讀取循環之前嘗試使用memset(buffer, 'X', sizeof buffer),您將獲得較早的失敗。

+0

將read()的結果存儲在一個int變量中並將它傳遞給服務器部分中的write()解決了該問題。我仍然想知道爲什麼它只發生在比以前短的投入上。它與'\ 0'和strlen()有什麼關係? – john27

+0

由於之前的輸入仍在緩衝區中。 –

0

還有\0問題所指出的艾倫·庫裏,不要忘了,TCP是消息導向的運輸,這是導向。

是絕對沒有保證一端單一write將導致在其他單一read。實際上在某些情況下,保證不會發生

你應該在你的數據包中加入某種形式的結構或分界來表示下一塊數據將要運行多長時間。在這種情況下,好的一個是包含字符串長度的32位整數,打包爲4個字節。網絡協議通常是以「big endian」格式(又名「網絡訂單」)發送此類東西,因此您將使用htonl()轉換髮送時的值,並在收到時轉換ntohl()

在發送端,您可以使用writev()將長度值和數據本身組合成一個系統調用。在接收端,只需使用read()來獲取長度,然後再使用第二個read()來讀取那麼多數據。

而且,不要忘記,任何read()write()調用可以發送較少的數據則實際上是指定的。每個這樣的操作應該被包裝在一個循環中,該循環會重複調用該函數,直到處理完適當數量的字節。

+0

所以我可以用一個字符串和一個int長度的結構,填寫結構並傳遞它?對不起,如果聽起來很愚蠢,我有點困惑,是的,我打算使用unix網絡編程手冊中的wrapper函數來進行read()和write()操作。 – john27