2010-08-09 46 views
16

Tôi có một chương trình máy chủ mạng hướng sự kiện. Chương trình này chấp nhận các kết nối từ các quá trình khác trên các máy chủ khác. Có thể có nhiều kết nối ngắn ngủi từ các cổng khác nhau trên cùng một IP từ xa.Sử dụng accept() và chọn() cùng một lúc?

Hiện tại, tôi có một vòng lặp while(1) gọi accept() và sau đó sinh ra một luồng để xử lý kết nối mới. Mỗi kết nối được đóng lại sau khi tin nhắn được đọc. Ở đầu từ xa, kết nối sẽ bị đóng sau khi tin nhắn được gửi đi.

Tôi muốn loại bỏ chi phí của việc thiết lập và xé các kết nối bằng cách lưu bộ nhớ đệm các ổ cắm FD mở. Về phía người gửi, điều này rất dễ dàng - tôi chỉ không đóng các kết nối và giữ chúng xung quanh.

Ở phía người nhận, khó hơn một chút. Tôi biết tôi có thể lưu trữ FD trả về accept() trong cấu trúc và nghe tin nhắn trên tất cả các ổ cắm như vậy bằng cách sử dụng poll() hoặc select(), nhưng tôi muốn đồng thời cả hai đều nghe kết nối mới qua số accept() nghe trên tất cả các kết nối được lưu trong bộ nhớ cache.

Nếu tôi sử dụng hai luồng, một trên poll() và một trên accept(), sau đó khi trả về cuộc gọi accept() (kết nối mới), tôi phải đánh thức luồng khác đang chờ kết nối cũ. Tôi biết tôi có thể làm điều này với một tín hiệu và pselect(), nhưng toàn bộ mớ hỗn độn này có vẻ như cách quá nhiều công việc cho một cái gì đó rất đơn giản.

Có cuộc gọi hoặc phương pháp vượt trội nào sẽ cho phép tôi xử lý đồng thời các kết nối mới đang được mở và dữ liệu được gửi trên các kết nối cũ không?

Trả lời

23

Thời gian qua tôi đã kiểm tra, bạn có thể chỉ listen trên một socket và sau đó select hoặc poll để xem nếu một kết nối đến trong Nếu vậy, accept nó.; nó sẽ không chặn (nhưng bạn có thể muốn thực sự cần bộ O_NONBLOCK chỉ để đảm bảo)

+9

Hmm, đó là một điều kiện chủng tộc nổi tiếng - 'accept (2)' sẽ chặn nếu khách hàng giảm nỗ lực kết nối giữa hai syscalls. Bạn * cần * ổ cắm nghe không bị chặn. –

+6

Điều này là đúng - bạn có thể thêm bộ mô tả tập tin nghe của bạn vào 'readfds' trong lệnh' select() ', và' select() 'sẽ cho bạn biết bộ mô tả tập tin là" có thể đọc được "nếu nó có kết nối sẵn sàng để' accept() '. @Nikolai cũng chính xác - ổ cắm nghe không được chặn và lệnh 'accept()' được chuẩn bị để xử lý 'EAGAIN'. – caf

0

Tôi muốn đặt một người biết lắng nghe trong quá trình riêng biệt (thread) không để những thứ dơ lên. Và chạy một quy trình công nhân trên một quy trình khác để xử lý các ổ cắm hiện có. Không cần thiết cho người nghe không chặn thực sự. Và không có chủ đề trên không chạy 2 chủ đề.

Nó sẽ hoạt động như thế: bạn chấp nhận chuỗi người nghe cho đến khi nó trả về cho bạn một bộ mô tả ổ cắm máy khách và chuyển nó cho nhân viên đang thực hiện tất cả công việc đọc/ghi bẩn trên đó.

Nếu bạn muốn nghe một số cảng và không muốn giữ một quá trình mỗi người nghe tôi đề nghị bạn cài đặt ổ cắm của bạn trong O_NONBLOCK và làm someth như:

// loop through listeners here and poll'em for read, when read is successful call accept, get descriptor, pass it to worker and continue listen 
    while(1){ 
     foreach(serverSocket in ServerSockets){ 
      if(serverSocket.Poll(10, SelectRead)){ 
        clientSocket = serverSocket.Accept(); 
        // pass to worker here and release 
      } 

     } 
    } 
+0

Đó là quay bạn đã có; không tốt. Và một luồng cho mỗi socket là quá nhiều luồng. – Borealid

+0

có gì xấu về việc kéo sợi này? :) nó sẽ không ăn bất kỳ CPU, bên cạnh đó nó cho phép bạn nghe nhiều cổng trong một thread. – hoodoos

+0

Điều này là xấu, bạn đang bận rộn với thời gian chờ 10usec trên mỗi ổ cắm nghe tại một thời điểm, điều này S eat ăn CPU. Sẽ tốt hơn nếu sử dụng 'select' trên tất cả các khe cắm nghe cùng một lúc, nó sẽ chặn cho đến khi ít nhất có một kết nối đến, và sau đó cố gắng' chấp nhận' từ mỗi người trong số họ (đảm bảo, rõ ràng là mỗi được đánh dấu 'O_NONBLOCK' và đang chuẩn bị cho' accept' để trả về 'EAGAIN' hoặc' EWOULDBLOCK' cho những ổ cắm không có kết nối). –

5

bạn có thể sử dụng lắng nghe sau đó sử dụng chọn hoặc thăm dò ý kiến ​​sau đó chấp nhận

if (listen (socket_fd, Number_connection) <0) 
{ 
    perror("listen"); 
    return 1; 
} 
fd_set set; 
struct timeval timeout; 
int rv; 
FD_ZERO(&set); /* clear the set */ 
FD_SET(socket_fd, &set); /* add our file descriptor to the set */ 

timeout.tv_sec = 20; 
timeout.tv_usec = 0; 

rv = select(socket_fd + 1, &set, NULL, NULL, &timeout); 
if(rv == -1) 
{ 
    perror("select"); /* an error accured */ 
    return 1; 
} 
else if(rv == 0) 
{ 
    printf("timeout occurred (20 second) \n"); /* a timeout occured */ 
    return 1; 
} 
else 
    client_socket_fd = accept (socket_fd,(struct sockaddr *) &client_name, &client_name_len); 
+0

Điều gì xảy ra nếu fd_set cũng có fds mà dữ liệu có thể sẽ đọc và không nghe? – Abhishek

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