2013-01-24 33 views
5

我正在使用完成端口在Windows NT中編寫一個tcp服務器來利用異步I/O。 我有一個TcpSocket類,一個TcpServer類和一些(虛函數)回調函數,以在I/O操作完成時調用,例如onRead()用於讀取完成時。我還爲連接建立時的onOpen()和連接關閉時的onEof()等等。 我總是有一個未決的讀取套接字,所以如果套接字有效地獲取數據(讀取將完成大小> 0)它調用onRead(),而不是如果客戶端關閉套接字從客戶端(讀取將以size == 0完成)它調用onEof(),並且服務器知道客戶端何時用closesocket(server_socket)關閉套接字;從它的側面。可以將臭名昭着的ERROR_NETNAME_DELETED錯誤視爲錯誤嗎?

所有工作正常,但我注意到一件事:

當我打電話關閉套接字(client_socket);在連接的服務器端端點上,而不是客戶端端(不管是否設置了linger {true,0}),掛起的讀操作將被錯誤地完成,即,讀大小不僅僅是= = 0,但GetLastError()返回一個錯誤:64或'ERROR_NETNAME_DELETED'。我在網上搜索了很多這方面的內容,但沒有發現任何有趣的內容。

然後我問自己:但這是一個真正的錯誤?我的意思是,這真的可以被認爲是一個錯誤?

問題是,在服務器端,當我closesocket(client_socket)時會調用onError()回調函數;而不是onEof()。所以我認爲這:

如果我收到這個'ERROR_NETNAME_DELETED'「錯誤」,請調用onEof()而不是onError()? 會引入一些錯誤或未定義的行爲? 讓我問這個問題的另一個重要的一點是:

當我收到這個讀取完成與「ERROR_NETNAME_DELETED」,我已經檢查重疊 結構,特別是其中含有NTSTATUS錯誤overlapped->內部參數代碼 的底層驅動程序。如果我們看到NTSTATUS錯誤代碼列表[http://www.tenox.tc/links/ntstatus.html] 我們可以清楚地看到'ERROR_NETNAME_DELETED'由NTSTATUS 0xC000013B生成,這是一個錯誤,但它被稱爲'STATUS_LOCAL_DISCONNECT'。那麼,它看起來不像一個錯誤的名稱。看起來更像是`ERROR_IO_PENDING',這是一個錯誤,也是一個正確行爲的狀態。

那麼如何檢查OVERLAPPED結構的內部參數,當這是=='STATUS_LOCAL_DISCONNECT'對onEof()回調執行調用?會搞砸了嗎?另外,我不得不說從服務器端,如果我調用 closesocket(client_socket)之前調用DisconnectEx();我不會收到那個錯誤。但是我不想調用DisconnectEx()?例如。當服務器關閉並且不想等待所有的DisconnectEx()完成時,只想關閉所有連接的客戶端。

+1

@Hans,我認爲他在描述他是如何遇到這個錯誤方面做得非常出色。 –

回答

3

這完全取決於你如何對待錯誤條件。在你的情況下,這個錯誤條件是完全可以預料的,並且將它視爲預期條件是完全安全的。

這種性質的另一個例子是當你調用一個API函數,但不知道要提供多大的緩衝區。所以你提供了一個你希望足夠大的緩衝區。但是如果API調用失敗,則檢查最後一個錯誤是否爲ERROR_INSUFFICIENT_BUFFER。這是一個預期的錯誤情況。然後您可以使用更大的緩衝區再次嘗試。

+0

我同意你的意見。唯一的缺點是我可以想象的是,如果在Windows中爲其他事物生成錯誤ERROR_NETNAME_DELETED,然後在出現真正的錯誤情況時,將調用onEof()回調而不是onError()。所以也許我可以在OVERLAPPED結構中檢查NTSTATUS。 –

+0

你不打算從'OVERLAPPED'結構中讀取'NTSTATUS'值。它是內部的,並且可能會發生變化。文件很清楚。 –

+0

是的,沒錯,內部成員並不意味着被使用,所以也許我會依賴GetLastError() –

2

如何處理錯誤條件取決於您,但問題是代碼中存在潛在問題(從邏輯錯誤到未定義行爲)的跡象。

最重要的一點是你不應該在closesocket之後觸摸SOCKET句柄。你在EOF上做什麼?當我們檢測到EOF時,在我們這邊合乎邏輯地closesocket,但這是你在ERROR_NETNAME_DELETED處理程序中無法做到的,因爲closesocket已經發生並且句柄無效。

這也是有利可圖之前closesocket想象如果未決讀取完成後會發生什麼(提供真實的數據),以及您的應用程序closesocket後立即檢測到它。您處理傳入數據,並且...您是否使用相同的套接字句柄向客戶端發送答案?你會安排下一次閱讀該句柄嗎?這將是全部錯誤,並且將不會有ERROR_NETNAME_DELETED告訴你。

如果待定讀取在EOF的非常不幸的時刻完成,在closesocket之前會發生什麼?如果您的常規OnEof回調被觸發,並且回調確實爲closesocket,那麼它將再次出錯。

如果closesocket在一個線程中完成,而另一個線程等待I/O完成,那麼您描述的問題可能暗示更嚴重的問題。您確定另一個線程在第一個線程調用closesocket時未調用WSARecv/ReadFile?這是未定義的行爲,儘管winsock使它看起來好像在大多數時間都有效。

總之,代碼處理完成(或失敗)讀取不能正確,如果它不知道套接字句柄是無用的,因爲它已關閉。在closesocket之後,等待掛起的I/O完成是有用的,因爲如果你不這樣,你不能重用OVERLAPPED結構;但是在處理這種類型的完成方面沒有任何意義,就好像它在正常操作期間發生的那樣,並且套接字仍處於打開狀態(錯誤/狀態代碼無關緊要)。

+0

你有一個很好的觀點。那麼,基本上我有onEof()來運行一些清理(例如dealloc內存等),但我應該有效的回調2:onEof()只有當另一端關閉連接時調用,onClose () - 以這種方式,當接收到onEof()時,另一部分可以按照你的建議調用closesocket()。所以,如果我得到了它,如果掛起讀取在closesocket()之前完成並且應用程序檢測到它們之後,則存在ERROR_NETNAME_DELETED來指示這種情況,是它的目的嗎? –

+0

不,「之前完成/」在「場景沒有」ERROR_NETNAME_DELETED「之後檢測到」,這是一個例子,事情可能以*未檢測*的方式*邏輯*錯誤。這也是一個例子,說明爲什麼在'ERROR_NETNAME_DELETED'回調中進行清理可能是錯誤的:在成功讀取這個「不幸」的時機的時候,沒有什麼可處理的(因爲不能重新安排'WSARecv * '在封閉的插座上)。 –

0

你打電話的方法不對。您應該撥打WSAGetLastError()。 Winsock API調用後GetLastError()的結果是沒有意義的。

+0

其實我打電話給WSAGetLastError()。此外,錯誤值也是一樣的。我提到過GetLastError(),因爲我使用I/O完成端口也用於非網絡I/O,但我認爲這種錯誤僅適用於聯網I/O。 –

+0

@MarcoPagliaricci所以實際上你的問題應該這樣說。 – EJP

相關問題