2009-01-19 33 views
8

Tôi đang làm việc trên hệ thống Linux (máy chủ Ubuntu 7.04 có hạt nhân 2.6.20).chọn trên ổ cắm UDP không kết thúc khi ổ cắm bị đóng - tôi đang làm gì sai?

Tôi có một chương trình có một chuỗi (thread1) đang chờ trên một lựa chọn cho một ổ cắm UDP để có thể đọc được. Tôi đang sử dụng lựa chọn (với ổ cắm của tôi như là readfd duy nhất và duy nhất exceptfd) thay vì chỉ gọi recvfrom vì tôi muốn một thời gian chờ.

Từ một chủ đề khác, tôi tắt máy và đóng ổ cắm. Nếu tôi làm điều này trong khi thread1 bị chặn trong một recvfrom, sau đó recvfrom sẽ chấm dứt ngay lập tức. Nếu tôi làm điều này trong khi thread1 bị chặn trong một lựa chọn với một thời gian chờ, sau đó chọn sẽ không chấm dứt ngay lập tức, nhưng cuối cùng sẽ hết thời gian.

Bất cứ ai có thể cho tôi biết lý do tại sao lựa chọn đó không thoát ngay sau khi đóng socket? Đó không phải là một ngoại lệ sao? Tôi có thể nhìn thấy nơi nó không thể đọc được (rõ ràng), nhưng nó đóng cửa, mà dường như được exeptional.

Đây là việc mở các ổ cắm (tất cả các lỗi xử lý loại bỏ để giữ cho mọi thứ đơn giản):

m_sockfd = socket(PF_INET, SOCK_DGRAM, 0); 
struct sockaddr_in si_me; 
memset((char *) &si_me, 0, sizeof(si_me)); 
si_me.sin_family = AF_INET; 
si_me.sin_port = htons(port); 
si_me.sin_addr.s_addr = htonl(INADDR_ANY); 
if (bind(m_sockfd, (struct sockaddr *)(&si_me), sizeof(si_me)) < 0) 
{ 
// deal with error 
} 

Đây là tuyên bố chọn mà thread1 thực hiện:

struct timeval to; 
to.tv_sec = timeout_ms/1000;// just the seconds portion 
to.tv_usec = (timeout_ms%1000)*1000;// just the milliseconds 
            // converted to microseconds 

// watch our one fd for readability or 
// exceptions. 
fd_set readfds, exceptfds; 
FD_ZERO(&readfds); 
FD_SET(m_sockfd, &readfds); 
FD_ZERO(&exceptfds); 
FD_SET(m_sockfd, &exceptfds); 

int nsel = select(m_sockfd+1, &readfds, NULL, &exceptfds, &to); 

UPDATE: Rõ ràng (như đã nêu dưới đây), đóng ổ cắm không phải là một điều kiện đặc biệt (từ quan điểm của lựa chọn). Tôi nghĩ điều tôi cần biết là: Tại sao? Và, đó là cố ý ?.

Tôi thực sự muốn hiểu suy nghĩ đằng sau hành vi được chọn này bởi vì nó có vẻ phản đối mong đợi của tôi. Vì vậy, tôi rõ ràng cần phải điều chỉnh suy nghĩ của tôi về cách ngăn xếp TCP hoạt động. Xin vui lòng giải thích cho tôi.

Trả lời

4

Có thể bạn nên sử dụng thứ gì đó khác để đánh thức lựa chọn. Có thể là một cái ống hay thứ gì đó như thế.

+0

Đó sẽ là một giải pháp tốt. Có sự lựa chọn chờ đợi trên cả hai ổ cắm và đường ống, và các chủ đề khác sẽ ghi vào đường ống để gây ra sự lựa chọn để trở về. –

+0

Điều đó sẽ hiệu quả và có thể sẽ là những gì tôi kết thúc. Tôi chỉ muốn tránh bất kỳ bộ phận chuyển động nào khác. Cảm ơn! –

4

UDP là giao thức không kết nối. Vì không có kết nối, không ai có thể bị hỏng, vì vậy người tiêu dùng không biết rằng nhà sản xuất sẽ không bao giờ gửi lại.

Bạn có thể làm cho nhà sản xuất gửi thông báo "kết thúc luồng" và yêu cầu người tiêu dùng chấm dứt khi nhận được thông báo.

+1

Tất cả chính xác, đúng sự thật và giải pháp khả thi, nhưng: Việc thu hồi DOES chấm dứt khi tôi đóng socket. Tại sao không chọn? Tôi thực sự bối rối là tại sao đóng socket không tạo ra một ngoại lệ mà kết thúc lựa chọn. –

+0

Tôi tin rằng bạn hiểu nhầm câu hỏi: hai luồng không thao tác hai ổ cắm UDP. Chỉ có ** một ổ cắm UDP **. – curiousguy

2

Tôi nghĩ giải pháp rõ ràng nhất là bị đóng không được coi là một điều kiện đặc biệt. Tôi nghĩ rằng gốc rễ của vấn đề là, rằng bạn không thực sự chấp nhận triết lý của select. Tại sao trên trái đất là bạn không quan tâm xung quanh với các ổ cắm trong thread khác, mà âm thanh như một công thức cho thảm họa.

+0

Điều duy nhất tôi làm với ổ cắm từ một sợi khác là đóng nó, vì mục đích dừng việc chờ đợi. Bằng cách này tôi có thể nhận được chương trình của tôi để thoát TRƯỚC KHI thời gian chờ chọn hết hạn. –

2

Tôi sẽ nói sự khác biệt là recvfrom đang tích cực cố gắng đọc một tin nhắn từ một ổ cắm duy nhất, nơi chọn đang chờ tin nhắn đến, có thể trên nhiều tay cầm và không nhất thiết phải xử lý ổ cắm.

3

Bạn không thể gửi tín hiệu (ví dụ: USR2) đến luồng sẽ làm cho select() trả về bằng EINTR? Sau đó, trong trình xử lý tín hiệu, hãy đặt một lá cờ yêu cầu nó không khởi động lại lựa chọn()?

Điều đó sẽ loại bỏ nhu cầu chờ đợi trên nhiều bộ mô tả tệp và có vẻ sạch hơn rất nhiều so với sử dụng đường ống để tiêu diệt nó.

+0

Có, điều đó sẽ hoạt động, mặc dù trong trường hợp cụ thể này, tôi không nghĩ rằng tôi có thể sử dụng tín hiệu bên trong (một số lớp cơ bản được viết sai trong việc sử dụng thư viện của chúng tôi và/hoặc tín hiệu khối theo cách kỳ lạ). Nếu tôi có thể làm sạch các thư viện cơ bản, tôi có thể thử điều này tại một số điểm. –

0

Mã của bạn về cơ bản bị hỏng.Các biến thể về lỗi này là phổ biến và đã gây ra các lỗi nghiêm trọng với các tác động bảo mật lớn trong quá khứ. Dưới đây là những gì bạn đang thiếu:

Khi bạn đi đến đóng ổ cắm, không có cách nào bạn có thể biết liệu chủ đề khác có bị chặn trong select hoặc sắp bị chặn trong select. Ví dụ: hãy xem xét những điều sau:

  1. Chủ đề gọi tới select nhưng không được lên lịch.
  2. Bạn đóng chốt.
  3. Trong một chủ đề, mã của bạn không biết (có thể đó là một phần của quản lý bộ nhớ nội bộ của nền tảng hoặc nội bộ ghi nhật ký) một thư viện mở một ổ cắm và nhận được cùng một số nhận dạng như ổ cắm bạn đã đóng.
  4. Chuỗi bây giờ đi vào select, nhưng nó là select ing trên ổ cắm được mở bởi thư viện.
  5. Cảnh cáo thiên tai.

Bạn không được cố gắng giải phóng tài nguyên trong khi một chuỗi khác hoặc có thể đang sử dụng tài nguyên đó.

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