2011-03-02 92 views
3

我寫了一些代碼在C中的TCP服務器,回聲無論它得到。問題在於,當我第一次發送數據時會迴應它,並在下次服務器發回我發送的第一個數據包。日誌是這樣的:TCP回聲服務器,只回聲的第一個數據包(C語言)

Client Send : Packet1 
Server reply : Packet1 
Client Send : Packet2 
server reply : Packet1 

服務器代碼如下:

int main(int argc, char** argv) { 
int listenfd,connfd; 
pid_t childpid; 
socklen_t clilen; 
struct sockaddr_in servaddr,cliaddr; 
listenfd = socket(AF_INET,SOCK_STREAM,0); 
printf("Socket listenfd : %d with %d And %d\n",listenfd,AF_INET,SOCK_STREAM); 
bzero(&servaddr, sizeof(servaddr)); 

servaddr.sin_family = AF_INET; 
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
servaddr.sin_port = htons(SERV_PORT); 
printf("Server address: %d\n",servaddr.sin_addr.s_addr); 
bind(listenfd, (SA*) &servaddr, sizeof(servaddr)); 
printf("Listened: %d\n",listenfd); 
listen(listenfd,LISTENQ); 
printf("After Listening: %d\n",listenfd); 
int num=0; 
for(; ;){ 
    clilen=sizeof(cliaddr); 
    connfd = accept(listenfd, (SA*) &cliaddr,&clilen); 
    printf("Client no. %d connected\n",++num); 
    if((childpid=fork())==0){ 
     close(listenfd); 
     echo(connfd); 
     exit(0); 
    printf("Client no. %d Terminated\n",++num); 
    } 
    close(connfd); 
} 
return (EXIT_SUCCESS); 
} 

我的回聲功能:

void echo(int sockfd) { 
ssize_t n; 
char buf[MAXLINE]; 
again: 
while ((n = read(sockfd, buf, MAXLINE)) > 0) 
    writen(sockfd, buf, n); 
if (n < 0 && errno == EINTR) 
    goto again; 
else if (n < 0) 
    printf("read error"); 
} 

客戶端代碼主:

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

sockfd= socket(AF_INET,SOCK_STREAM,0); 
bzero(&servaddr,sizeof(servaddr)); 

servaddr.sin_family = AF_INET; 
servaddr.sin_port= htons(SERV_PORT); 
inet_pton(AF_INET,"0.0.0.0",&servaddr.sin_addr); 
printf("%d , %d \n",sockfd,servaddr.sin_addr.s_addr); 
connect(sockfd, (SA*) &servaddr,sizeof(servaddr)); 
printf("%d\n",sockfd); 
replyBack(stdin,sockfd); 

printf("RETURN\n"); 
return (EXIT_SUCCESS); 
} 

replyBack功能:

void replyBack(FILE *fp, int sockfd) { 
char sendline[MAXLINE], recvline[MAXLINE]; 
printf("ENTER YOUR ECHOED: \n"); 
while (fgets(sendline, MAXLINE, stdin) != NULL) { 
    write(sockfd, sendline, sizeof(sendline)); 

    if (read(sockfd, recvline, MAXLINE) == 0) 
    { 
     printf("str_cli: server terminated prematurely"); 
     exit(-1); 
    } 
    fputs(recvline, stdout); 

} 
} 
+0

你的客戶端和服務器對我來說工作得很好。檢查以確保您所做的任何系統調用('read','write','connect'等)都返回錯誤或小於預期的值。嘗試使用Wireshark來查看實際上通過網絡發送的內容,這會告訴您問題出在客戶端還是服務器上。嘗試使用'netcat'作爲替代客戶端。 – 2011-03-02 18:43:55

+0

THANKs :)我會嘗試使用它們 – 2011-03-02 19:22:44

回答

4

好吧,讓我們來看看你的代碼的這一部分:

printf("After Listening: %d\n",listenfd); 
int num=0; 
for(; ;){ 
    clilen=sizeof(cliaddr); 
    connfd = accept(listenfd, (SA*) &cliaddr,&clilen); 
    printf("Client no. %d connected\n",++num); 
    if((childpid=fork())==0){ 
     close(listenfd); 
     echo(connfd); 
     exit(0); 
    printf("Client no. %d Terminated\n",++num); 
    } 
    close(connfd); 
} 

調用exit退出你的應用程序,所以printf以下它將永遠不會得到執行。次要的,但值得指出。

此外,在「子」過程中,您不應關閉偵聽套接字。它應該工作的唯一套接字是客戶端的連接,所以你應該有一些沿的線條更:

if ((childpid = fork()) == 0) { 
    echo (connfd); 
    close (connfd); 
    printf ("Client no %d terminated.\n", num); /* Don't use the ++ here or your count will be off */ 
    exit (0); 
} 

現在,讓我們看看你的迴音代碼:

void echo(int sockfd) { 
    ssize_t n; 
    char buf[MAXLINE]; 
    again: 
    while ((n = read(sockfd, buf, MAXLINE)) > 0) 
     writen(sockfd, buf, n); 
if (n < 0 && errno == EINTR) 
     goto again; 
else if (n < 0) 
     printf("read error"); 
} 

必須記住的是,調用readwrite可能會阻塞(因爲我沒有看到您將套接字設置爲非阻塞IO),並且write在調用時可能不會發送整個緩衝區,所以您需要在此處查看更多內容。

void 
echo (int sockfd) 
{ 
    ssize_t bytes_in, bytes_out, bytes_remaining; 
    int write_err; 
    char buf[MAXLINE]; 
    char * send_start_pos; 
    while (1) { 
    bytes_in = read (sockfd, buf, MAXLINE); 
    if (bytes_in < 1) { 
     if (errno == EINTR) 
     continue; 
     break; /* other error occurred, or EOF (0 bytes read) */ 
    } 
    bytes_remaining = bytes_in; 
    send_start_pos = buf; 
    write_err = 0; 
    while ((bytes_remaining > 0) && !(write_err)) { 
     bytes_out = write (sockfd, send_start_pos, bytes_remaining); 
     if (bytes_out < 0) { 
     if (errno == EINTR) 
      continue; 
     write_err = 1; 
     break; 
     } 
     bytes_remaining -= bytes_out; 
     send_start_pos += bytes_out; 
    } 
    if (write_err) 
     break; 
    } 
} 

一旦你的echo函數退出,套接字將被關閉回到調用函數。通常,我會建議關閉echo函數中的套接字,除非您事後需要它。我幾乎肯定會建議在發生錯誤時關閉它,但是,這又取決於您。

正如一邊,遠離goto ......它有它的目的,但大多數情況下,寫得很好的代碼很少使用它。

+0

它工作:)非常感謝您的解決方案和你寶貴的建議:D – 2011-03-02 19:01:36

+1

另一個小的評論:你還應該檢查'fork()'是否返回-1,這表明一個錯誤。 – 2011-03-02 19:52:50

+0

@亞當:絕對是。好的做法是檢查-1,0和其他任何東西;沒有專注於此,所以我錯過了那個調整。 – Will 2011-03-02 21:20:22

1

最大的問題是,你認爲TCP就像它是一個數據報協議,事實並非如此。這是一個流協議。

我還沒有把我的手指放在究竟是怎麼回事,但現在我的錢在客戶端打印它已經在緩衝區中的消息(而不是第二次)。向我們展示客戶端代碼。

編輯有很多事情是錯誤的。

首先,對write的單個呼叫可能需要在另一端多次呼叫read。其次,由於TCP是一個流協議,因此您需要確保接收方知道每個邏輯消息有多少字節。您要麼堅持使用固定長度的消息,要麼使用其長度前綴每個消息。您總是通過發送MAXLINE字節來完成前一種操作,但不夠穩定(例如,writen(sockfd, buf, n)n字節寫回,n可能與MAXLINE不同)。

另一編輯來解決評論中提出的一點。 Telnet是一個不好的比喻:在telnet中,有一個字符流向一個方向,而一個字符流向另一個方向。在你的協議中,你發送離散多字節消息(或數據報)。這正是你找到一個數據報協議(UDP)更容易適用於你的問題的原因。

+0

您可以使用telnet作爲客戶端..但是,我會編輯帖子並添加客戶端 – 2011-03-02 18:24:50

+0

在那裏我添加了客戶端.. – 2011-03-02 18:27:57

+0

如果您使用telnet,無所謂作爲客戶,它仍然是一個流。 – nos 2011-03-02 19:13:36