是Beej被影射的方法,和AlastairG提到,工作是這樣的:
對於每個併發連接,你保持閱讀,但是,尚未處理的數據的緩衝區。 (這是Beej建議調整爲最大數據包長度兩倍的緩衝區)。顯然,緩衝開始了空:
unsigned char recv_buffer[BUF_SIZE];
size_t recv_len = 0;
每當你的插座是可讀,讀入緩衝區中的剩餘空間,然後立即嘗試過程中,你有什麼:
result = recv(sock, recv_buffer + recv_len, BUF_SIZE - recv_len, 0);
if (result > 0) {
recv_len += result;
process_buffer(recv_buffer, &recv_len);
}
的process_buffer()
將嘗試並將緩衝區中的數據作爲數據包處理。如果緩衝區尚未包含完整的數據包,它只會返回 - 否則,它會處理數據並將其從緩衝區中刪除。因此,對於您的示例協議,它看起來是這樣的:
void process_buffer(unsigned char *buffer, size_t *len)
{
while (*len >= 3) {
/* We have at least 3 bytes, so we have the payload length */
unsigned payload_len = buffer[2];
if (*len < 3 + payload_len) {
/* Too short - haven't recieved whole payload yet */
break;
}
/* OK - execute command */
do_command(buffer[0], buffer[1], payload_len, &buffer[3]);
/* Now shuffle the remaining data in the buffer back to the start */
*len -= 3 + payload_len;
if (*len > 0)
memmove(buffer, buffer + 3 + payload_len, *len);
}
}
(該do_command()
功能會檢查是否有一個有效的標題和指令字節)。
這種技術最終被必要的,因爲任何recv()
可以返回一個短 - 與你的建議的方法,如果你的有效載荷長度爲500會發生什麼,但接下來的recv()
只返回你400個字節?無論如何,你必須保存這400個字節,直到下一次套接字變得可讀。當你處理多個併發客戶端時,每個客戶端只需要一個recv_buffer
和recv_len
,並將它們填充到每個客戶端結構中(這可能也包含其他內容 - 如客戶端套接字,可能是它們的源地址,當前狀態等等。)。
來源
2010-12-03 01:29:52
caf
謝謝AlastairG。我基本上試圖保持它儘可能簡單,只處理最常見的情況,如讀入下一個數據包,或讀取部分數據包。我實際上使用select來監視傳入數據的客戶端套接字(在這種情況下是命令),所以我不認爲這是異步套接字的問題。我的方法是讀取3個字節,然後有效載荷不起作用?我不想實現Beej所說的原因是因爲如果沒有實際的代碼/僞代碼就很難理解。感謝您的建議:) – Jack 2010-12-02 16:14:26
對於處理傳入數據後的寫入/發送,我只是簡單地使用Beej的sendall方法的變體,因爲我知道數據包的長度..顯示在這裏:http:// beej頁面沒有自動跳轉/導向/ bgnet /輸出/ HTML /多頁/ advanced.html#sendall。我更關心閱讀。 – Jack 2010-12-02 16:15:52
我對寫作的評論只是爲了完整 - 以防其他人想知道如何編寫一個非常好的套接字服務器。我要說的一件事是,我已經嘗試編寫小而簡單的套接字服務器,但你很快發現你只需要按照我描述的方式來完成它。但並非總是如此。讀取3個字節可以工作,但爲什麼要這樣做? – AlastairG 2010-12-02 16:33:14