2013-01-24 61 views
5

Tôi đang viết một máy chủ tcp trong Windows NT bằng cách sử dụng các cổng hoàn thành để khai thác I/O không đồng bộ. Tôi có một lớp TcpSocket, một lớp TcpServer và một số hàm gọi lại (ảo) để gọi khi một hoạt động I/O được hoàn thành, ví dụ: onRead() cho khi đọc xong. Tôi cũng onOpen() cho khi kết nối được thiết lập và onEof() cho khi kết nối được đóng lại, và như vậy. Tôi luôn đọc đọc cho socket, vì vậy nếu socket có hiệu quả dữ liệu (đọc sẽ được hoàn thành với kích thước> 0) nó gọi onRead(), thay vào đó nếu client đóng socket từ phía máy khách (đọc sẽ được hoàn thành với kích thước == 0) nó gọi onEof(), và máy chủ nhận thức được khi khách hàng đóng socket bằng closesocket (server_socket); từ phía của nó.lỗi "ERROR_NETNAME_DELETED" khét tiếng có được coi là lỗi không?

Tất cả các công trình một cách duyên dáng, nhưng tôi đã nhận thấy một điều:

khi tôi gọi closesocket (client_socket); trên điểm cuối bên của máy chủ của kết nối, thay vì phía máy khách, (với thiết lập nán {true, 0} hoặc không), đọc đang chờ xử lý sẽ được hoàn thành là sai, nghĩa là kích thước đọc sẽ không chỉ = = 0, nhưng cũng GetLastError() trả về lỗi: 64 hoặc 'ERROR_NETNAME_DELETED'. Tôi đã tìm kiếm nhiều về điều này trên web, nhưng không tìm thấy gì thú vị.

Sau đó tôi tự hỏi: nhưng đây có phải là lỗi thực sự không? Ý tôi là, điều này có thực sự được coi là lỗi không?

Vấn đề ở phía máy chủ, hàm gọi lại onError() sẽ được gọi khi tôi đóng sổ (client_socket); thay vì onEof(). Vì vậy, tôi nghĩ rằng điều này:

Điều gì xảy ra nếu tôi, khi nhận được lỗi 'ERROR_NETNAME_DELETED' "này, hãy gọi onEof() thay vì onError()? Điều đó sẽ giới thiệu một số lỗi hoặc hành vi không xác định? Một điểm quan trọng mà làm cho tôi hỏi câu hỏi này là:

Khi tôi đã nhận được này đọc xong với 'ERROR_NETNAME_DELETED', tôi đã kiểm tra chồng chéo cấu trúc, đặc biệt là overlapped-> tham số nội bộ có chứa lỗi NTSTATUS mã số của trình điều khiển cơ bản. Nếu chúng tôi thấy danh sách các mã lỗi NTSTATUS [http://www.tenox.tc/links/ntstatus.html] , chúng tôi có thể thấy rõ rằng 'ERROR_NETNAME_DELETED' được tạo bởi NTSTATUS 0xC000013B, đây là lỗi, nhưng được gọi là 'STATUS_LOCAL_DISCONNECT'. Vâng, nó không giống như một tên cho một lỗi. Nó có vẻ giống như `ERROR_IO_PENDING 'mà là một lỗi, nhưng cũng là một trạng thái cho một hành vi chính xác.

Vì vậy, điều gì về việc kiểm tra tham số bên trong của cấu trúc OVERLAPPED, và khi nào đây là == đến 'STATUS_LOCAL_DISCONNECT' một cuộc gọi đến hàm gọi lại onEof() được thực hiện? Điều gì sẽ rối tung lên?

Ngoài ra, tôi phải nói rằng từ phía máy chủ, nếu tôi gọi DisconnectEx() trước khi gọi closesocket (client_socket); Tôi sẽ không nhận được lỗi đó. Nhưng những gì về tôi không muốn gọi DisconnectEx()? Ví dụ. khi máy chủ tắt và không muốn đợi tất cả các kết thúc DisconnectEx(), nhưng chỉ muốn đóng tất cả kết nối của máy khách.

+1

@Hans, tôi nghĩ anh ấy đã làm rất tốt khi mô tả cách anh ấy gặp phải lỗi đó. –

Trả lời

3

Nó hoàn toàn tùy thuộc vào bạn cách bạn xử lý một điều kiện lỗi. Trong trường hợp của bạn, điều kiện lỗi này hoàn toàn được mong đợi, và nó hoàn toàn an toàn để bạn có thể coi nó là một điều kiện mong đợi.

Một ví dụ khác về bản chất này là khi bạn gọi hàm API nhưng không biết bộ đệm cung cấp bao nhiêu. Vì vậy, bạn cung cấp một bộ đệm mà bạn hy vọng sẽ đủ lớn.Nhưng nếu cuộc gọi API không thành công, thì bạn kiểm tra xem lỗi cuối cùng có phải là ERROR_INSUFFICIENT_BUFFER hay không. Đó là một điều kiện lỗi dự kiến. Sau đó bạn có thể thử lại với bộ đệm lớn hơn.

+0

Tôi đồng ý với bạn. Hạn chế duy nhất tôi có thể tưởng tượng, là nếu lỗi ERROR_NETNAME_DELETED được tạo ra cho những thứ khác trong Windows, và sau đó khi có một điều kiện lỗi thực, hàm gọi lại onEof() sẽ được gọi thay vì onError(). Vì vậy, có lẽ tôi chỉ có thể kiểm tra NTSTATUS trong cấu trúc OVERLAPPED. –

+0

Bạn không có nghĩa là đọc giá trị 'NTSTATUS' từ cấu trúc' OVERLAPPED'. Đó là nội bộ và có thể thay đổi. Tài liệu rõ ràng về điều đó. –

+0

Vâng, đúng vậy, các thành viên nội bộ không có nghĩa là để được sử dụng, vì vậy có lẽ tôi sẽ dựa vào GetLastError() –

2

Tùy thuộc vào bạn cách xử lý tình trạng lỗi, nhưng câu hỏi là dấu hiệu của các vấn đề tiềm ẩn trong mã của bạn (từ lỗi logic đến hành vi không xác định).

Điểm quan trọng nhất là bạn không nên chạm vào SOCKET handle sau closesocket. Bạn làm gì do trên EOF? Nó sẽ là hợp lý để closesocket về phía chúng tôi khi chúng tôi phát hiện EOF, nhưng đó là những gì bạn không thể làm trong xử lý ERROR_NETNAME_DELETED, bởi vì closesocket đã xảy ra và xử lý không hợp lệ.

Nó cũng có lợi nhuận để tưởng tượng những gì sẽ xảy ra nếu đang chờ đọc xong (với dữ liệu thực tế có sẵn) ngay trướcclosesocket, và ứng dụng của bạn phát hiện nó ngay sau khiclosesocket. Bạn xử lý dữ liệu đến và ... Bạn có gửi câu trả lời cho khách hàng bằng cách sử dụng cùng một socket handle không? Bạn có lên kế hoạch đọc tiếp theo trên tay cầm đó không? Nó sẽ là tất cả sai, và sẽ không có ERROR_NETNAME_DELETED để cho bạn biết về nó.

Điều gì sẽ xảy ra nếu chờ đọc hoàn tất với EOF trong khoảnh khắc rất đáng tiếc đó, ngay trước closesocket? Nếu cuộc gọi lại OnEof thông thường của bạn bị kích hoạt và cuộc gọi lại đó sẽ thực hiện closesocket, nó sẽ lại bị lỗi.

Vấn đề bạn mô tả có thể gợi ý về vấn đề nghiêm trọng hơn nếu closesocket được thực hiện trong một chuỗi, trong khi một luồng khác chờ hoàn thành I/O. Bạn có chắc chắn rằng một chuỗi khác không gọi số WSARecv/ReadFile trong khi chuỗi đầu tiên đang gọi closesocket? Đó là hành vi không xác định, mặc dù winsock làm cho nó trông như thể nó làm việc hầu hết thời gian.

Để tóm tắt, việc xử lý mã hoàn thành (hoặc không đọc) lần đọc không thể chính xác nếu không biết xử lý ổ cắm là vô ích vì đã bị đóng. Sau closesocket, sẽ rất hữu ích khi chờ kết thúc I/O đang chờ xử lý vì bạn không thể sử dụng lại cấu trúc OVERLAPPED nếu bạn không; nhưng không có điểm nào trong việc xử lý loại hoàn thành này như thể nó đã xảy ra trong quá trình hoạt động bình thường, với ổ cắm vẫn đang mở (lỗi/mã trạng thái không liên quan).

+0

Bạn có một điểm thực sự tốt. Vâng, về cơ bản tôi có onEof() để chạy một cái gì đó để làm sạch (ví dụ như bộ nhớ dealloc và vv), nhưng tôi có hiệu quả nên có 2 callbacks cho rằng: onEof() được gọi chỉ khi phía bên kia đóng kết nối, và onClose() - theo cách này khi nhận được onEof(), phần kia có thể gọi closesocket() như bạn đã đề xuất.Vì vậy, nếu tôi nhận được nó, nếu chờ đọc đọc hoàn thành trước khi closesocket() và ứng dụng phát hiện chúng sau, có ERROR_NETNAME_DELETED để báo hiệu kịch bản này, và nó là mục đích của nó? –

+0

Không, trường hợp "hoàn thành trước"/"được phát hiện sau" không có 'ERROR_NETNAME_DELETED', đây là ví dụ mà mọi thứ có thể đi * logic * sai theo cách * không bị phát hiện *. Nó cũng là một minh hoạ tại sao có thể sai khi dọn dẹp trong gọi lại 'ERROR_NETNAME_DELETED': với thời gian" không may "để đọc thành công, không có gì để xử lý (và có * sẽ không có gì để xử lý vì bạn không thể lên lịch lại' WSARecv 'trên ổ cắm kín). –

0

Bạn đang gọi sai phương pháp. Bạn nên gọi số WSAGetLastError(). Kết quả của GetLastError() sau khi một cuộc gọi API Winsock là vô nghĩa.

+0

Thực ra tôi đang gọi WSAGetLastError(). Và cũng với điều đó, giá trị lỗi là như nhau. Tôi đã đề cập đến GetLastError() vì tôi sử dụng cổng hoàn thành I/O cũng cho I/O không mạng, nhưng tôi nghĩ rằng lỗi chỉ có ý nghĩa đối với I/O được kết nối mạng. –

+0

@MarcoPagliaricci Vì vậy, thực sự câu hỏi của bạn nên nói như vậy. – EJP