2011-08-30 37 views
0

我是一個新手在C中進行遊戲,我的小項目是編寫一個簡單的SOCKS4代理。感謝這裏的幫助,我到目前爲止使用了非阻塞套接字和poll()作爲我的例程。然而,在這一點上,我似乎有2個問題:代理中的非阻塞套接字和輪詢()怪癖 - 新手

  1. 傳出插槽dstSocket如果輸入插座rcvSocket被關閉,反之亦然不會被關閉。我不檢查這個循環,但我不知道如何。我嘗試POLLHUP作爲啓示,但似乎並沒有這樣做。普通的檢查似乎是recv()是否返回0,但是它對非阻塞套接字是否也有效?如果是這樣,那麼如何才能與revents一起工作,我似乎無法弄清楚我會把它放在哪裏,因爲如果POLLIN | POLLPRI被設置在我看來recv()永遠不應該返回0?另外我不明白POLLIN和POLLPRI之間的確切區別是什麼,在這兩種情況下,在我看來只是一個檢查「數據可供閱讀」?

  2. 代理似乎適用於我使用netcat測試的連接。但是,如果我使用瀏覽器,它說(當我的目標網站)是否我想保存「二進制數據」。我在wireshark中檢查了數據,從服務器收到的數據看起來正確地按字節轉發到客戶端。如果有人也許有一個想法,爲什麼,可能與這一計劃發生這將是很好:)

附相關的代碼(注意編程新手):

fds[1].fd = dstSocket; 
fds[0].fd = rcvSocket; 
fds[1].events = POLLIN | POLLPRI | POLLHUP; 
fds[0].events = POLLIN | POLLPRI | POLLHUP; 

timer = poll(fds, 2, timeout_msecs); /* i dont use this yet */ 

fcntl(rcvSocket, F_SETFL, O_NONBLOCK); 
fcntl(dstSocket, F_SETFL, O_NONBLOCK); 

while (1 == 1) 
{ 
    if (fds[0].revents & POLLIN | POLLPRI) 
    { 
     recvMsgSize = recv(rcvSocket, rcvBuffer, RCVBUFSIZE, 0); 
     if (recvMsgSize > 0) {send(dstSocket, rcvBuffer, recvMsgSize, 0);} 
    } 
    if (fds[1].revents & POLLIN | POLLPRI) 
    { 
     sndMsgSize = recv(dstSocket, sndBuffer, RCVBUFSIZE, 0); 
     if (sndMsgSize > 0) { send(rcvSocket, sndBuffer, sndMsgSize, 0);} 
    }   

    if ((fds[0].revents & POLLHUP) || (fds[1].revents & POLLHUP)) 
    { 
     close(rcvSocket); 
     close(dstSocket); 
    } 
} 
+0

雖然這裏可能不是真正的問題,但最終還是需要處理:使用非阻塞套接字send()可能不會發送請求它發送的所有字節。另外,您是否配置瀏覽器將您的程序用作SOCKS代理?如果是這樣,您需要實現SOCKS協議以使瀏覽器正常工作。 – nos

+0

2.注意。注意|和&運營商運營商優先'fds [1] .revents&POLLIN | POLLPRI'與'(fds [1] .revents&POLLIN)|相同POLLPRI',你需要'fds [1] .revents&(POLLIN | POLLPRI)'(或者按照caf – nos

+0

的建議放棄POLLPRI顯然是一個firefox錯誤,chrome和其他襪子應用程序工作。 t知道這一點,謝謝。 – user912877

回答

1

recv()返回0乾淨的遙控器上關閉 - 即使對於非阻塞套接字也是如此。在這種情況下,將返回POLLIN - 遠程端關閉套接字的通知被視爲「可讀」事件。

對於SOCKS/HTTP連接,您不需要使用POLLPRI - 它表示TCP「緊急數據」,這些協議沒有使用這些數據(或者確實使用得很多)。


除了您的直接問題,你需要做更多來實現可靠的代理:

  1. 你需要在每個循環中調用poll(),不只是一次。你寫它的方式是繁忙循環,這通常不被認爲是可以接受的做法。
  2. 您應該將SIGPIPE的配置設置爲signal(SIGPIPE, SIG_IGN);忽略。這使您可以正常處理寫入失敗。
  3. 您應該檢查send()的結果。請注意,它可以寫入的數量少於您請求的數量 - 在這種情況下,您必須保持未發送的數據緩衝,返回到poll()並嘗試在套接字上產生POLLOUT時再次發送剩餘數據。您只想要求POLLOUT如果尚未發送的數據剩餘,所以您需要確保.events在每個poll()調用之前設置正確。
  4. 您應該檢查errno如果recv()send()收益小於0 EINTREWOULDBLOCK應該被忽略的值;任何其他錯誤應視爲連接失敗。
  5. 當一個套接字關閉時,您不應該立即關閉兩個方向 - 您必須支持非對稱關閉。這意味着當您看到fds[0]已被遠端關閉時,應該撥打shutdown(fds[1], SHUT_WR);,反之亦然;只有當兩個都已關閉(或發生連接失敗)時,纔可以在兩個文件描述符上調用close()並完成。
+0

感謝您的信息,我添加如果(recvMsgSize == 0)中斷;和sndMsgSize相同,現在像連接關閉的魅力。 – user912877

+0

非常感謝輸入,我很明顯意識到我的「代理」並不完全是產品價值,它只是一個項目,讓我亂搞學習C.我會盡力實現你所說的,但我想我將不得不做一些閱讀首先,好的一面是,瀏覽器沒有與socks代理一起工作,看起來像是一個Firefox的bug,因爲Chrome和任何其他襪子可以感知應用程序的作品。 – user912877

+0

@ user912877:我認爲Firefox很可能並不適用這是因爲點5. – caf