2016-04-29 27 views
-1

我正在創建一個聊天應用程序,其中兩個程序(聊天服務和聊天客戶端)來回發送消息。 我試圖設置它一次發送一個字符。我計算消息應該多長時間,並將聊天服務發送給聊天客戶端。數字傳輸沒有問題,但是當我嘗試讀取/寫入消息時,使用打印語句成功發送了指示for循環應該結束的打印語句,但在發送最後一個字母后,程序只是掛起。連續發送/接收的消息的打印消息從不被觸發。但是,如果Sig INT聊天客戶端同時掛起聊天服務似乎完成其寫入並顯示完成發送消息。我對正在發生的事情感到有點不知所措。從我的打印語句看來,for循環看起來應該滿足條件,但程序似乎都陷入其中。用於讀寫循環的C套接字問題

即時通訊服務: 上面顯示的代碼中,我將「Tom:」作爲主處理程序硬編碼,並將hostmessage的用戶輸入作爲本例中的測試對象。 MONE是程序的

char mone[2]=""; 

//send message to chatclient 
charcount= strlen(hostmessage); 
meslen=charcount+strlen(hosthandle);//total message length 
int number_to_send = meslen; 
int converted_number = htonl(number_to_send); 
write(newsockfd, &converted_number, sizeof(converted_number)); //sends number to chat client 
for (j=0;j<strlen(hosthandle);j++) 
{ 
    mone[0]=hosthandle[j]; 
    n = write(newsockfd, mone, 2); //writes handle to chatclient 
    if (n <= 0) 
    { 
     perror("Message not \n"); 
    } 
    printf("%s %d \n",mone, j); 
} 
for (j=0;j<strlen(hostmessage);j++) 
{ 
    mone[0]=hostmessage[j]; 
    n = write(newsockfd, mone, 2); //writes message to chatclient 
    printf("%s %d \n",mone, j); 
} 
printf("Finished Sending Message"); 

輸出(hostmessage是測試消息大小= 8)

文稿:

Tom: test 
T 0 
o 1 
m 2 
: 3 
t 0 
e 1 
s 2 
t 3 

ChatClient

int received_int = 0; //this section of code receives message length from chatserve 
int return_status = read(sockfd, &received_int, sizeof(received_int)); 
if (return_status > 0) { 
    fprintf(stdout, "Received int = %d\n", ntohl(received_int)); 
    } 
else 
{ 
    printf("did not receive message length"); 
} 
for(j=0;j<received_int;j++) 
{ 
    n=read(sockfd,kone,2); //reads in letter from chat serve 
    if (n <= 0) 
    { 
     perror("Message not recieved\n"); 
    } 
    //printf("%d \n", n); 
    printf("%s %d \n",kone, j); 
} 
printf("Received message \n"); 

輸出程序的在此之後它只是掛起當我覺得這兩個循環應該打到他們的櫃檯。

Received int = 8 
T 0 
o 1 
m 2 
: 3 
t 4 
e 5 
s 6 
t 7 
+1

慣例:未能正確完整地處理read()返回的結果。當傳遞的參數不保證以空字符結尾時,調用期望以空字符結尾的字符數組的函數。 –

+0

則可能會遇到一個問題:爲'(j = 0;Ĵ Aconcagua

回答

1

mone只包含一個元素,但你有寫兩個大字:

n = write(newsockfd, mone, 2); 

需要聲明mone爲:

char mone[] = " "; 

,使其包含一個字符後面尾隨空字節。你的聲明只有最後的空字節。另外

char[] mone = ""; 

甚至沒有有效的語法,我不明白如何編譯程序。

在客戶端中,您還應該檢查read()實際上是否返回2個字節。沒有任何事情可以保證每次撥打read()都會在發件人的相應write()中發送所有內容。允許read()返回任何數字,直到您請求的數量,所以它可能一次返回1個字節,並且您需要再次調用以獲取第二個字節。

int total_needed = 2; 
int total_read = 0; 
while (total_read < total_needed) { 
    n = read(sockfd, mone + total_read, total_needed - total_read); 
    if (n < 0) { 
     perror("Error while reading"); 
     break; 
    } else if (n == 0) { 
     printf("Unexpected EOF\n"); 
     break; 
    } else { 
     total_read += n; 
    } 
} 
+0

如果我們假設至少有TCP套接字... – Aconcagua

+0

@Aconcagua真,數據報套接字保留消息邊界。 – Barmar

+0

char mone [] =「」;你能告訴我一個例子,我將如何檢查看到讀取功能正常。 –

0

老實說,我不明白爲什麼你想要寫上自己的每一個角色。這隻會讓你的生活變得沒有任何好處(至少對我來說是可見的)。如果使用UDP套接字,則應該嘗試將整個消息合併成單個UDP數據包(例如,將轉換後的int和完整數據放入單個緩衝區,並將所有內容發送(寫入)一次)。如果使用TCP,則無論如何您的數據都會合併到一個流中,而客戶端沒有任何可見邊界(將數據封裝到ip數據包中是在套接字實現中完成的)。

不知道你有沒有注意到,你隱含定義的協議已經:首先,你在網絡字節順序的四個字節應遵循的字節數,其次是–送你猜怎麼着–正是這種數量的數據.. 。

如果使用UDP,你將不得不嘗試在一個單一的讀取來獲取整個郵件(如果你提供一個太小緩衝區,該消息的其餘部分被丟棄!)。如果使用TCP,閱讀不一定會返回完整的消息。因此,客戶端將執行以下操作來實現你定義的協議:

  • 從流讀取–四個字節可能,你需要一個以上的電話閱讀!
  • 評估這四個字節的期望字節數。
  • 從流–有一個以上的電話可能再次閱讀閱讀本字節數。

要知道,讀可以阻止;如果您還有其他任務要做,那麼您可以使用select或poll來測試,如果數據完全可用並且只能讀取(或者您有多個線程)。

編輯:你問(希望這不會遲到)–這是我會怎麼做:

int readFromSocket(int fd, char* buffer, unsigned long length) 
{ 
    unsigned int count = 0; 
    char* b = buffer; 

    while(count < length) 
    { 
     int n = read(fd, buffer, sizeof(length) - count); 
     if(n < 0 && errno != EINTR) // EINTR: interrupted due to signal -> can continue 
     { 
      // handle error, possibly: 
      // * closing socket (next messages might be corrupt now!) 
      // * calling exit(-1), abort(), ... 
      // * returning - as here - an error 
      return -1; 
     } 
     count -= n; 
     buffer += n; 
    } 

    return 0; // OK 
} 

int handleNextMessage(int fd) 
{ 
    int result = 0; 
    unsigned int length; 
    char* message; 
    result = readFromSocket(fd, (char*)&length, sizeof(length)); 
    if(result == 0) 
    { 
     length = htonl(length); 
     // possibly check first length for valid range 
     message = malloc(length); 
     result = readFromSocket(fd, message, length); 
     if(result == 0) 
     { 
      // do what ever needs to be done with message 
     } 
     // important, else you get a memory leak: 
     free(message); 
    } 
    if(!result != 0) 
    { 
     // appropriate error handling 
     // all handling proposed within readFromSocket could 
     // alternatively be done here, too... 
     // or outside handleNextMessage 
    } 
    return result; 
} 

而不是使用malloc和free的,你可以考慮使用堆棧的緩衝區:

char message[MAX_MESSAGE_LENGTH]; 

確保服務器不會發送比客戶端可以讀取更長的消息。

一個重要的節點:如果你無法讀取完整的消息,你可能已經閱讀郵件只是部分。如果你只是繼續下去,你可能會冒險閱讀前一封郵件的其餘部分作爲下一封郵件的開頭,並獲取損壞的數據。所以你應該關閉你的插座,並且如果需要–重新打開它,則需要–。

有一個在服務器端留下一個小問題:你打算如何把手從消息再次在客戶端分離(除非手柄具有固定長度)?我建議包括終止0字符(和,如使用TCP,只需編寫一次所有數據):

int isSuccess, result, lenHandle, lenMessage; 
// ... 
lenHandle = strlen(hosthandle) + 1; // +1: for terminating 0 character 
lenMessage = strlen(hostmessage) + 1; 
meslen = lenHandle + lenMessage; 
meslen = htonl(meslen); 
result = write(newsockfd, &meslen, sizeof(meslen); 
isSuccess = 0; 
if(result == sizeof(meslen)) 
{ 
    result = write(newsockfd, hosthandle, lenHandle); 
    if(result == lenHandle) 
    { 
     result = write(newsockfd, hostmessage, lenMessage); 
     isSuccess = result == lenHandle; 
    } 
} 
if(!isSuccess) 
{ 
    // handle errno appropriately 
} 

要知道,寫操作也可導致與EINTR錯誤,在這種情況下,你可能會繼續,所以你可以將每個寫入循環包裝:

do 
{ 
    result = write(/*...*/); 
} 
while(result < 0 && errno == EINTR); 
+0

我正在使用TCP,你介意解釋一下更多的顯示或例子,你的意思是閱讀和評估4字節。 –

+0

按照您的要求添加了示例代碼。 – Aconcagua

+0

非常感謝我明天會測試一下,看看它是如何發展的。如果我只是打印我的消息,將在第二個結果== 0部分正確。 –