2011-03-20 29 views
6

Tôi đã đọc tài liệu ít nhất 10 lần và cũng đã đọc khoảng 10 đoạn mã hoặc chương trình đầy đủ nơi các ổ cắm không chặn được sử dụng để gửi dữ liệu. Vấn đề là một số hướng dẫn hoặc là cho người mới bắt đầu (Beejs f.i.) hoặc khá cẩu thả trong các giả định của họ; và những người không phức tạp là những ví dụ mã chuyên biệt không giải thích lý do tại sao họ làm những gì họ làm. Ngay cả cơ sở tri thức SO cũng không bao quát toàn bộ gam màu của hành vi send, theo ý kiến ​​của tôi. Những gì tôi sau đây là chi tiết về f.e:Ai đó có thể cho tôi giải thích tốt về hành vi 'gửi' cho các ổ cắm không chặn không?

  • Mã trả về 0 cho biết chính xác và có giá trị khi kiểm tra errno sau đó hoặc nên bỏ kết nối mà không cần điều tra thêm?
  • Việc nhận được lệnh bảo đảm giá trị trả lại âm có kết thúc không tốt hoặc chỉ trừ khi errnoEWOULDBLOCK, EAGAIN hoặc EINTR (... người khác)?
  • Có đáng để kiểm tra errno khi giá trị trả lại là > 0? Rõ ràng, giá trị cho biết số lượng dữ liệu "đã gửi" (trong dấu ngoặc kép vì nó là một quá trình dài thực sự, phải), nhưng vì ổ cắm không bị chặn, có nghĩa là người dùng có thể thực hiện cuộc gọi khác ngay lập tức hoặc tùy thuộc vào errno một lần nữa , người ta có nên đợi cho dịp gửi tiếp theo (sử dụng select/poll/epoll) không?
  • Về cơ bản, bạn có kiểm tra giá trị trả về trước và chỉ sau đó giá trị errno? Hoặc có thể send đặt errno trên mỗi cuộc gọi, giá trị trả lại bất kể? Điều đó sẽ làm cho việc kiểm tra lỗi dễ dàng hơn một chút ...
  • Nếu một người nhận được EINTR, hành vi mạnh mẽ, tốt cho một chương trình sẽ là gì? Chỉ cần ghi lại tiểu bang và thử lại vào dịp gửi tiếp theo, như với EWOULDBLOCKEAGAIN?
  • Bạn có kiểm tra cả haiEWOULDBLOCKEAGAIN? Liệu chúng ta có thể tin tưởng cả hai có cùng giá trị, hay nó phụ thuộc vào việc thực hiện?
  • send trả lại EMSGSIZE cho ổ cắm luồng không? Nếu không, thì không có kích thước bộ đệm quá lớn, phải không?
  • Bản thân giá trị trả lại có thể bằng một trong các mã lỗi đã biết không?

Nếu bạn có thể cung cấp ví dụ về mã gửi không chặn mạnh mẽ, nó sẽ được đánh giá tuyệt đối.

Trả lời

9

Rất nhiều câu hỏi ở đây:

  • gì Mã trả về 0 chỉ ra một cách chính xác, và nó có giá trị kiểm tra sau đó errno hoặc nên người ta chỉ loại bỏ các kết nối mà không tiếp tục điều tra?

Trên một hệ thống POSIX, gửi (2) không bao giờ có thể trở lại 0, trừ khi bạn gọi nó với một arg chiều dài 0. Kiểm tra các tài liệu cho hệ thống cụ thể của bạn để chắc chắn rằng nó theo POSIX đặc tả

  • Không nhận được lệnh trả về giá trị âm sẽ đóng kết nối bị hỏng hay chỉ trừ khi errno là EWOULDBLOCK, EAGAIN hoặc EINTR (... người khác)?

Không, giá trị trả về -1 (giá trị trả về âm duy nhất) có nghĩa là không có dữ liệu nào được gửi. Bạn cần kiểm tra errno để xem TẠI SAO - xem send (2) người đàn ông để có danh sách đầy đủ của tất cả các giá trị errno càng tốt và ý nghĩa của chúng

  • Có giá trị kiểm tra errno khi giá trị trả về là> 0? Rõ ràng, giá trị cho biết số lượng dữ liệu "đã gửi" (trong dấu ngoặc kép vì nó là một quá trình dài thực sự, đúng), nhưng vì socket không bị chặn, có nghĩa là người ta có thể đưa ra một cuộc gọi khác ngay lập tức, hoặc, tùy thuộc vào errno một lần nữa , người ta có nên đợi cho dịp gửi tiếp theo (sử dụng select/poll/epoll) không?

Nếu gửi trở lại thành công (> 0), sau đó errno sẽ không thay đổi và sẽ chứa bất cứ điều gì nó đã có trước (mà có lẽ là một lỗi từ một số cuộc gọi hệ thống trước đó).

  • Về cơ bản, bạn có kiểm tra giá trị trả về trước và chỉ sau đó giá trị errno không? Hoặc có thể gửi bộ errno trên mỗi cuộc gọi, giá trị trả lại bất kể? Điều đó sẽ giúp kiểm tra lỗi dễ dàng hơn một chút ...

Kiểm tra giá trị trả về trước và sau đó không nếu giá trị trả về là -1. Nếu bạn thực sự muốn, bạn có thể đặt errno 0 trước khi cuộc gọi và sau đó kiểm tra xem nó sau đó

  • Nếu ai được EINTR, những gì sẽ là một tốt, hành vi mạnh mẽ cho một chương trình để mất? Chỉ cần ghi lại trạng thái và thử lại vào dịp gửi tiếp theo, như với EWOULDBLOCK và EAGAIN?

Vâng, đơn giản nhất là vô hiệu hóa gián đoạn cuộc gọi hệ thống, trong trường hợp đó bạn sẽ không bao giờ nhận được EINTR. Xử lý nó giống như EWOULDBLOCK/EAGAIN cũng tốt.

  • Bạn có kiểm tra cả EWOULDBLOCK và EAGAIN không? Liệu chúng ta có thể tin tưởng cả hai có cùng giá trị, hay nó phụ thuộc vào việc thực hiện?

Tùy thuộc vào việc triển khai, mặc dù nhìn chung chúng giống nhau. Đôi khi có weirdness với SysV vs chế độ BSD thi đua mà có thể làm cho họ khác nhau và một trong hai có thể xảy ra

  • Liệu gửi trở lại EMSGSIZE cho ổ cắm dòng? Nếu không, thì không có kích thước bộ đệm quá lớn, phải không?

socket Suối không có tin nhắn nguyên tử và EMSGSIZE chỉ dành cho thông điệp nguyên tử, vì vậy không, ổ cắm dòng không thể trở về EMSGSIZE

  • có thể trả lại giá trị bản thân bằng một trong hai của các mã lỗi đã biết?

Mã lỗi duy nhất là -1. Thành công là số byte được viết, vì vậy nếu bạn có thể viết 2^32-1 byte trên máy 32 bit (hoặc 2^64-1 trên máy 64 bit), nó sẽ là một vấn đề, nhưng bạn không thể viết rằng nhiều byte (và bạn thường sẽ nhận được một EINVAL hoặc EFAULT nếu bạn cố gắng).

+1

gửi (2) có thể trả về 0 nếu số không được chuyển cho 'len'. Đối với một giao thức datagram như UDP, điều này thậm chí sẽ dẫn đến một gói zero-byte được gửi đi. Ngoài ra, errno không được bảo đảm không thay đổi trong suốt cuộc gọi thư viện thành công. – Anomie

+0

@Anomie: phần đầu tiên là đủ, nhưng trong lần thứ hai, POSIX đảm bảo rằng một số cuộc gọi thư viện sẽ không sửa đổi errno nếu không có lỗi, và 'send' là một trong những cuộc gọi đó. –

+0

Bạn thấy điều đó ở đâu? POSIX.1-2008 trực tuyến [tại đây] (http://pubs.opengroup.org/onlinepubs/9699919799/). [Trang trên errno] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/errno.html) nói "Cài đặt errno sau khi cuộc gọi thành công đến một hàm không được chỉ định trừ khi mô tả của hàm đó chỉ định rằng errno sẽ không được sửa đổi ", và [trang gửi] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html) dường như không nói bất kỳ điều gì như vậy. – Anomie

3

Tôi sẽ cố gắng trả lời câu hỏi của bạn.

  • Giá trị trả về 0 từ send cho biết rằng 0 byte đã được gửi. Lỗi được biểu thị bằng giá trị trả về là -1. Nếu bạn gọi là send với chiều dài là 0, bạn nên chờ trả lại 0. Trong khi một ổ cắm không chặn phải trả về -1 với một sai số của EAGAIN hoặc EWOULDBLOCK nếu nó sẽ chặn, tôi sẽ không quá ngạc nhiên nếu một số thực hiện trả về 0 byte bằng văn bản để thay thế.
  • EWOULDBLOCK, EAGAIN và EINTR là các lỗi mà bạn nên thử lại, không đóng kết nối khi nhận được một trong số đó. Các lỗi khác chỉ ra một vấn đề có thể dẫn đến kết quả.
  • Không, đừng kiểm tra errno sau cuộc gọi thư viện thành công (trừ khi tài liệu nêu rõ rằng bạn có thể làm điều này vì một lý do nào đó; Tôi không biết bất kỳ hành vi nào làm điều này). Lưu ý rằng errno có thể không thay đổi trong suốt cuộc gọi thư viện thành công, vì cuộc gọi có thể đã thực hiện các cuộc gọi khác trả lại lỗi đã được mong đợi và xử lý đúng cách (ví dụ: cuộc gọi có thể cố gắng chỉ định tệp, hoàn toàn mong đợi rằng nó có thể không tồn tại; sau đó sẽ là ENOENT mặc dù không có lỗi thực sự). Nếu gửi trả về một viết ngắn, bạn có thể thử lại (và có thể nhận được EWOULDBLOCK/EAGAIN) hoặc bạn có thể đợi select tiếp theo.
  • Có, hãy kiểm tra giá trị trả lại trước. errno cho bạn biết không có gì hữu ích nếu cuộc gọi thành công.
  • Trên EINTR, bạn có thể thử lại ngay lập tức hoặc bạn có thể đợi lần sau thông qua vòng lặp select.
  • Bạn phải kiểm tra cả EAGAIN và EWOULDBLOCK; Tôi cho rằng bạn có thể làm #if EAGAIN == EWOULDBLOCK nếu hiệu suất đặc biệt quan trọng (nhưng hãy nhớ, hồ sơ sau đó tối ưu hóa).
  • Tất cả sẽ phụ thuộc vào giao thức cơ bản, nhưng nói chung tôi sẽ mong đợi một giao thức truyền trực tuyến không có thông điệp nguyên tử (trừ khi có lẽ khi sử dụng MSG_OOB). Đối với TCP, bất kỳ kích thước bộ đệm nào cũng được.
  • Chắc chắn là có thể cho giá trị trả về bằng bất kỳ hằng số errno nào, nhưng nó không có nghĩa là gì cả. Ví dụ, trên hệ thống của tôi nếu 11 byte được viết thì giá trị trả về sẽ bằng EAGAIN.

HTH.

2

Trên EINTR và system call:

  • nếu bạn đang sử dụng glibc, bạn không cần phải lo lắng về điều đó, ít nhất là trong bối cảnh các cuộc gọi hệ thống. Tôi nhận được điều đó từ Glibc FAQ, grep cho "Tại sao không có tín hiệu ngắt cuộc gọi hệ thống nữa?"

  • nếu bạn đang sử dụng LINUX, thì có thể bạn không phải lo lắng về ngữ nghĩa kỳ lạ của cuộc gọi hệ thống connect(), mà David Madore rants khoảng here. Nếu không, hãy chuẩn bị cho hành vi khác với hành vi usuall cho cuộc gọi kết nối không đồng bộ().

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