2010-07-26 34 views
6

Trong chương trình của tôi có một luồng (chuỗi nhận) chịu trách nhiệm nhận yêu cầu từ một cổng TCP và có nhiều luồng (chuỗi công việc) chịu trách nhiệm xử lý nhận yêu cầu. Khi yêu cầu được xử lý, tôi cần gửi câu trả lời qua TCP.Làm thế nào để hủy chờ trong select() trên Windows

Và đây là một câu hỏi. Tôi muốn gửi dữ liệu TCP trong cùng một luồng mà tôi sử dụng để nhận dữ liệu. Chủ đề này sau khi nhận dữ liệu thường đợi dữ liệu mới trong select(). Vì vậy, một khi một thread công nhân xử lý xong một yêu cầu và đặt một câu trả lời trong hàng đợi đầu ra, nó phải báo hiệu luồng nhận rằng có dữ liệu để gửi. Vấn đề là tôi không biết làm thế nào để hủy bỏ chờ đợi trong select() để có được ra khỏi chờ đợi và gọi send().

Hoặc tôi có nên sử dụng một luồng khác chỉ để gửi dữ liệu qua TCP không?

Cập nhật

MSalters, Artyom cảm ơn bạn cho bạn câu trả lời!

Biến đổi, đã đọc câu trả lời của bạn Tôi tìm thấy trang web này: Winsock 2 I/O Methods và đọc khoảng WSAWaitForMultipleEvents(). Chương trình của tôi trong thực tế phải làm việc cả trên HP-UX và Windows cuối cùng tôi đã quyết định sử dụng cách tiếp cận đã được đề xuất bởi Artyom.

+0

Tại sao bạn sử dụng 'select()' để viết? Thông thường, 'select()' chỉ đợi dữ liệu nhận được, và bạn có thể gửi dữ liệu 'send()' bằng cùng một socket (s) trong khi đó. – ereOn

+0

Không, tôi không sử dụng chọn để viết. Tôi đã nói vậy sao? –

+0

@skwllsp: Tôi giả sử nó vì bạn dường như muốn hủy 'select()' để ghi vào ổ cắm của bạn, điều này không cần thiết. Nhưng có lẽ tôi hiểu lầm toàn bộ điều đó, tiếng anh của tôi không chính xác là bản địa. – ereOn

Trả lời

13

Bạn cần phải sử dụng một cái gì đó tương tự như lừa ống an toàn, nhưng trong trường hợp của bạn, bạn cần phải sử dụng một cặp cổng TCP được kết nối.

  1. Tạo một cặp ổ cắm.
  2. Hãy thêm một trong số đó vào và chọn trên đó
  3. Thông báo bằng cách ghi vào ổ cắm khác từ các chủ đề khác.
  4. Chọn được ngay lập tức đánh thức-up là một trong những ổ cắm có thể đọc, đọc tất cả các dữ liệu trong ổ cắm đặc biệt này và rà soát tất cả dữ liệu trong hàng đợi để gửi/recv

Làm thế nào để tạo ra cặp ổ cắm trong môi trường Windows ?

inline void pair(SOCKET fds[2]) 
{ 
    struct sockaddr_in inaddr; 
    struct sockaddr addr; 
    SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP); 
    memset(&inaddr, 0, sizeof(inaddr)); 
    memset(&addr, 0, sizeof(addr)); 
    inaddr.sin_family = AF_INET; 
    inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 
    inaddr.sin_port = 0; 
    int yes=1; 
    setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes)); 
    bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr)); 
    listen(lst,1); 
    int len=sizeof(inaddr); 
    getsockname(lst, &addr,&len); 
    fds[0]=::socket(AF_INET, SOCK_STREAM,0); 
    connect(fds[0],&addr,len); 
    fds[1]=accept(lst,0,0); 
    closesocket(lst); 
} 

Tất nhiên, một số kiểm tra sẽ được thêm vào cho giá trị trả lại.

+0

'(char *) & yes, sizeof (yes)' ?? mới đối với tôi :) – sarnold

+0

@sarnold Không giống như POSIX setsockopt, cửa sổ sẽ nhận 'char const *' làm giá trị thay vì 'void const *' như trên nền tảng POSIX. – Artyom

+2

Bạn không thực sự cần một cặp ổ cắm, bạn có thể sử dụng một ổ cắm UDP duy nhất, "kết nối" với chính nó. – Hasturkun

7

select không phải là API gốc cho Windows. Cách gốc là WSAWaitForMultipleEvents. Nếu bạn sử dụng điều này để tạo một sự chờ đợi có thể cảnh báo, bạn có thể sử dụng QueueUserAPC để hướng dẫn luồng chờ gửi dữ liệu. (Điều này cũng có nghĩa là bạn không phải thực hiện hàng đợi đầu ra của riêng mình)

+1

Những gì bạn đang đề xuất là sử dụng IOCP có nhiều hạn chế và ... trừ khi bạn xử lý 10000 kết nối tốt hơn là tránh chúng. – Artyom

+0

Vâng, IOCP là một cách để thoát ra khỏi một WSAWaitForMultipleEvents. Bạn cũng có thể thêm Sự kiện vào danh sách các đối tượng đang đợi và báo hiệu sự kiện khi có dữ liệu để gửi. Tôi không nhớ những vấn đề với sự kết hợp của WaitForMultipleEvents/QueueUserAPC, mặc dù, đó là lý do tại sao tôi đề nghị nó. – MSalters

+1

Artyom, 'nhiều nhược điểm' của IOCP là gì? Không phải là tôi cho rằng chúng là giải pháp cho câu hỏi gốc hoặc vấn đề cụ thể này; Tôi chỉ tò mò về những gì bạn nghĩ là khó về truyền thông dựa trên IOCP .. –

0

Mô hình điển hình là để nhân viên xử lý văn bản riêng của mình. Có một lý do tại sao bạn muốn gửi tất cả các đầu ra-IO thông qua select ing thread?

Nếu bạn chắc chắn về mô hình này, bạn có thể yêu cầu người lao động gửi dữ liệu về chủ đề chính bằng cách sử dụng các bộ mô tả tệp (pipe(2)) và chỉ cần thêm các mô tả đó vào cuộc gọi select() của bạn. Và, nếu bạn đặc biệt chắc chắn rằng bạn sẽ không sử dụng đường ống để gửi dữ liệu trở lại quy trình tổng thể của bạn, cuộc gọi select cho phép bạn chỉ định thời gian chờ. Bạn có thể bận chờ trong khi kiểm tra chuỗi công việc của mình và định kỳ gọi số select để tìm ra cổng TCP nào cần đọc.

+0

cửa sổ không có các đường ống có thể lựa chọn – Artyom

+0

Ah. Tôi đã giả định các cửa sổ thực hiện các thông số kỹ thuật POSIX chặt chẽ. Cảm ơn ghi chú. – sarnold

+1

"cửa sổ thực hiện thông số kỹ thuật POSIX chặt chẽ" Ohhhhh, bạn làm tôi cười. :) – Artyom

0

Một cách nhanh chóng khác & giải pháp bẩn là thêm ổ cắm cục bộ vào bộ này. Bây giờ, hãy sử dụng các socket đó làm hàng đợi truyền thông giữa các luồng. Mỗi luồng công nhân chỉ đơn giản gửi một cái gì đó đến socket của nó, kết thúc lên trên socket tương ứng trong luồng nhận của bạn. Điều này đánh thức select() và chuỗi nhận của bạn sau đó có thể lặp lại thông báo trên ổ cắm đi thích hợp.

+0

Thực ra, WSAWaitForMultipleEvents dường như ít nhiều sutable nhưng tôi phải đọc một số sách hướng dẫn sử dụng nó trước khi đưa ra quyết định –

4

Xem thêm bài này: How to signal select() to return immediately?

Đối với unix, sử dụng một ống nặc danh. Đối với Windows: Bỏ chặn có thể đạt được bằng cách thêm ổ cắm datagram giả (không ràng buộc) vào fd_set và sau đó đóng nó lại. Để làm cho luồng này an toàn, hãy sử dụng QueueUserAPC:

Cách duy nhất tôi tìm thấy để làm cho multi-threadsafe này là đóng và tạo lại socket trong cùng một luồng như câu lệnh select đang chạy. Tất nhiên điều này là khó khăn nếu thread đang chặn trên lựa chọn. Và sau đó đến trong cửa sổ gọi QueueUserAPC. Khi các cửa sổ đang chặn trong câu lệnh chọn, luồng có thể xử lý các cuộc gọi thủ tục không đồng bộ. Bạn có thể lên lịch biểu này từ một luồng khác bằng QueueUserAPC. Windows ngắt chọn, thực thi hàm của bạn trong cùng một luồng và tiếp tục với câu lệnh select. Bây giờ bạn có thể trong phương pháp APC của bạn đóng socket và tạo lại nó. Đảm bảo thread an toàn và bạn sẽ không bao giờ mất một tín hiệu.

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