2011-02-04 24 views
14

Khi tôi đóng socket trên một đầu của một kết nối, đầu kia được một lỗi lần thứ hai nó sẽ gửi dữ liệu, nhưng không phải là lần đầu tiên:Python không phát hiện một ổ cắm đóng cửa cho đến thứ hai gửi

import socket 

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
server.bind(("localhost", 12345)) 
server.listen(1) 

client = socket.create_connection(("localhost",12345)) 
sock, addr = server.accept() 
sock.close() 

client.sendall("Hello World!") # no error 
client.sendall("Goodbye World!") # error happens here 

Tôi đã thử đặt TCP_NODELAY, bằng cách sử dụng send thay vì sendall, kiểm tra fileno(), tôi không thể tìm thấy bất kỳ cách nào để gửi thông báo lỗi đầu tiên hoặc thậm chí phát hiện lỗi không thành công. EDIT: gọi sock.shutdown trước sock.close không có tác dụng. EDIT # 2: thậm chí thêm time.sleep sau khi đóng và trước khi viết không quan trọng. EDIT # 3: kiểm tra số byte được trả về bởi send không giúp đỡ, vì nó luôn trả về số byte trong thư.

Vì vậy, giải pháp duy nhất tôi có thể đưa ra nếu tôi muốn phát hiện lỗi là làm theo mỗi sendall với số client.sendall("") sẽ gây ra lỗi. Nhưng điều này có vẻ bị hack. Tôi đang trên một Linux 2.6.x vì vậy ngay cả khi một giải pháp chỉ làm việc cho hệ điều hành đó tôi sẽ được hạnh phúc.

+0

Tôi gặp vấn đề tương tự. Làm cách nào để bạn giải quyết vấn đề trong mã của mình? – HighCommander4

+0

@HighCommander: Tôi đã kết thúc việc thêm một khách hàng.sendall ("") 'sau mỗi' send', không được đảm bảo về mặt kỹ thuật để lỗi, nhưng hoạt động cho chương trình của tôi cho thời gian và chi tiết cụ thể về những gì tôi đang làm. Điều này là không phù hợp và tôi thực sự mong rằng có một cách để truy cập trạng thái socket TCP cơ bản, nhưng có vẻ như không, hoặc trong Python hoặc C. –

+0

có hiệu quả với bạn khi máy khách và máy chủ trên các máy khác nhau không? Nó không cho tôi, sendall() thành công trong trường hợp đó. – HighCommander4

Trả lời

13

này được mong đợi, và làm thế nào các API TCP/IP được thực hiện (vì vậy nó tương tự như trong khá nhiều mọi ngôn ngữ và trên tất cả các hệ điều hành)

Câu chuyện ngắn gọn là, bạn không thể làm bất cứ điều gì để đảm bảo rằng một gửi() gọi trả về một lỗi trực tiếp nếu send() gọi bằng cách nào đó không thể cung cấp dữ liệu đến đầu kia. gửi/ghi cuộc gọi chỉ cung cấp dữ liệu vào ngăn xếp TCP, và nó lên đến ngăn xếp TCP để cung cấp nó khi nó có thể.

TCP cũng chỉ là một giao thức truyền tải, nếu bạn cần biết ứng dụng của bạn "tin nhắn" đã đạt đến đầu kia hay không, bạn cần phải thực hiện chính mình (một số dạng ACK), như một phần của giao thức ứng dụng của bạn không ăn trưa miễn phí. Tuy nhiên, nếu bạn đọc() từ ổ cắm, bạn có thể nhận được thông báo ngay lập tức khi xảy ra lỗi hoặc khi đầu kia đóng socket - bạn thường cần thực hiện điều này trong một số dạng vòng lặp ghép kênh (tức là , sử dụng select/poll hoặc một số thiết bị ghép kênh IO khác). Chỉ cần lưu ý rằng bạn không thể đọc() từ ổ cắm để tìm hiểu xem gửi/viết mới nhất có thành công hay không, Dưới đây là một vài trường hợp tại sao (nhưng đó là trường hợp bạn không nghĩ đến điều đó luôn giúp bạn)

  • một số cuộc gọi write() đã bị giật do tắc nghẽn mạng hoặc do cửa sổ tcp bị đóng (có thể là trình đọc chậm) và đầu kia đóng socket hoặc lỗi mạng cứng, vì vậy bạn có thể ' t cho biết nếu là viết cuối cùng mà đã không nhận được thông qua, hoặc viết một bạn đã làm 30 giây trước đây.
  • Lỗi mạng hoặc tường lửa tự động xóa các gói của bạn (không có trả lời ICMP được tạo), Bạn sẽ phải đợi đến khi TCP hết kết nối để nhận được lỗi có thể mất nhiều giây, thường là vài phút.
  • TCP đang bận làm truyền lại khi bạn gọi send -. Có lẽ những người truyền lại tạo ra một lỗi (thực sự giống như trường hợp đầu tiên)
+0

Cảm ơn câu trả lời chi tiết, nhưng tôi vẫn còn hơi bối rối về điều này. Không phải phía bên kia của một kết nối gửi các gói ACK để xác nhận rằng nó nhận được dữ liệu? Tôi giả định từ câu trả lời của bạn rằng 'gửi' không chờ đợi sự thừa nhận này, nhưng có cách nào khác để chặn (ở cấp độ vận chuyển) cho đến khi chúng tôi biết rằng dữ liệu của chúng tôi đã trải qua? Tôi hiểu rằng nói chung điều này sẽ xảy ra ở cấp ứng dụng, nhưng tôi hiện đang nói một giao thức mà không có sự thừa nhận như vậy, vì vậy tôi tự hỏi những gì các tùy chọn của tôi là ở cấp độ vận chuyển. –

+0

Ngoài ra, không TCP gửi gói FIN khi nó đóng? –

+3

@Eli Courtwright. Vâng. TCP gửi ACK. Tuy nhiên đó là ACK tại TCP (lớp vận chuyển). TCP không biết về thông điệp của bạn, nó chỉ là một luồng byte theo như TCP có liên quan. Một TCP ACK KHÔNG gửi một cuộc gọi send(). Nó có thể ack sự tích lũy của 100 * * tin nhắn của bạn. Hoặc 3 byte tin nhắn của bạn. – nos

1

Theo số docs, hãy thử gọi sock.shutdown() trước khi gọi tới sock.close().

+0

Được bình chọn để đề xuất một điều gì đó mà tôi chưa thử (chúng không đề cập đến việc tắt máy trong socket.close docstring), nhưng tiếc là điều này cũng không có hiệu lực. –

+2

tắt máy() sẽ không đạt được kết quả nào (đóng trong trường hợp này). Và ngay cả khi nó đã xảy ra - nếu có sự chậm trễ mạng nhỏ, đầu kia sẽ không nhận được yêu cầu tắt máy ngay lập tức, lệnh send() đầu tiên vẫn sẽ thành công và bạn lại gặp phải vấn đề tương tự. – nos

Các vấn đề liên quan