2009-10-09 27 views
27

Làm thế nào để bạn thực hiện một ổ cắm không chặn?Làm cách nào để thay đổi ổ cắm TCP không bị chặn?

Tôi biết về chức năng fcntl(), nhưng tôi đã nghe nói rằng nó không phải lúc nào cũng đáng tin cậy.

+1

Tôi chỉ cần chuyển đổi một TCP Socket thành ổ cắm không chặn. –

+1

Nó chỉ không đáng tin cậy nếu bạn không kiểm tra lỗi và giả sử nó luôn thành công. :) – Qix

Trả lời

19

Bạn có ý nghĩa gì bởi "không phải lúc nào cũng đáng tin cậy"? Nếu hệ thống thành công trong việc thiết lập ổ cắm của bạn không chặn, nó sẽ không bị chặn. Hoạt động của ổ cắm sẽ trả về EWOULDBLOCK nếu chúng sẽ chặn cần chặn (ví dụ: nếu bộ đệm đầu ra đầy và bạn đang gọi gửi/ghi quá thường xuyên).

This forum thread có một vài điểm tốt khi làm việc với các cuộc gọi không chặn.

+12

Tôi không nghĩ đây là câu trả lời hay. Bạn nói với anh ta rằng nó nên đáng tin cậy sau đó gửi một chuỗi diễn đàn có thể trở nên nhanh chóng lỗi thời. Chỉ cần ý kiến ​​của tôi, nhưng một người nào đó với đại diện của bạn, tôi đã có thể nghĩ sẽ đủ chu đáo để cung cấp một câu trả lời toàn diện hơn là một liên kết lười biếng và bình luận nhanh. – Matt

+4

@Matt Xin lỗi vì đã thất vọng. Xin lưu ý rằng câu trả lời này là bốn năm rưỡi. Tôi nghĩ câu trả lời của tôi cho thấy sự thất vọng của tôi với sự thiếu chi tiết của câu hỏi.Thật khó để tranh cãi với "Tôi đã nghe nói rằng làm theo cách rõ ràng không phải lúc nào cũng đáng tin cậy". Tôi vẫn nghĩ như vậy, không chắc chắn làm thế nào để cải thiện về điều này. – unwind

+0

Vâng, tôi nhận ra sau đó câu hỏi bao nhiêu tuổi. Nghĩ rằng nó là mới vì ai đó đã cập nhật nó và nó xuất hiện trong danh sách hoạt động ... oops. đừng bận tâm. – Matt

12

fcntl() hoặc ioctl() được sử dụng để đặt thuộc tính cho luồng tệp. Khi bạn sử dụng chức năng này để tạo ổ cắm không chặn, chức năng như accept(), recv() và v.v., đang chặn trong tự nhiên sẽ trả về lỗi và errno sẽ được đặt thành EWOULDBLOCK. Bạn có thể thăm dò ý kiến ​​bộ mô tả tập tin để thăm dò ý kiến ​​trên ổ cắm.

+0

Có cách nào khác để chuyển đổi một TCPIP thành NON BLOCKING socket hay không. Tôi không muốn dựa vào những phương pháp này có thể là do những trải nghiệm quá khứ của tôi –

+8

Điều đó ** là ** cách chuyển đổi ổ cắm thành không bị chặn. Nếu bạn có thể cụ thể về "kinh nghiệm quá khứ" của bạn và lý do tại sao bạn bị thuyết phục rằng điều này sẽ không hoạt động, có thể chúng tôi có thể giúp bạn. –

0

Phương pháp tốt nhất để đặt ổ cắm là không chặn trong C là sử dụng ioctl. Một ví dụ nơi một ổ cắm chấp nhận được thiết lập để không chặn được như sau:

long on = 1L; 
unsigned int len; 
struct sockaddr_storage remoteAddress; 
len = sizeof(remoteAddress); 
int socket = accept(listenSocket, (struct sockaddr *)&remoteAddress, &len) 
if (ioctl(socket, (int)FIONBIO, (char *)&on)) 
{ 
    printf("ioctl FIONBIO call failed\n"); 
} 
+0

Không phải là ổ cắm không đồng bộ thay vì không khóa? – JuliandotNut

+0

Trong C/C++, nó thực sự không chặn bằng cách sử dụng ổ cắm tiêu chuẩn/winsock. –

1

Nói chung, bạn có thể đạt được hiệu quả tương tự bằng cách sử dụng bình thường chặn IOghép nhiều hoạt động IO sử dụng select(2), poll(2) hay một số khác các cuộc gọi hệ thống có sẵn trên hệ thống của bạn.

Xem The C10K problem để so sánh các phương pháp tiếp cận với ghép kênh IO có thể mở rộng.

+0

Không theo POSIX, như trạng thái 'man select' trong phần BUG,' select' có thể báo cáo socket sẵn sàng cho 'read', nhưng sau đó' read' có thể chặn. Vì vậy, nó không hoàn toàn giống nhau. – zoska

+0

Chúng tôi đã vượt qua đáng kể các vấn đề C10K ngay bây giờ. – Matt

+0

thăm dò syscall (2) là O (n), epoll (2) là O (1) chỉ là giải pháp cho C10K + – Anatoly

64

fcntl() luôn làm việc đáng tin cậy cho tôi. Trong mọi trường hợp, đây là chức năng tôi sử dụng để bật/tắt tính năng chặn trên ổ cắm:

#include <fcntl.h> 

/** Returns true on success, or false if there was an error */ 
bool SetSocketBlockingEnabled(int fd, bool blocking) 
{ 
    if (fd < 0) return false; 

#ifdef _WIN32 
    unsigned long mode = blocking ? 0 : 1; 
    return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false; 
#else 
    int flags = fcntl(fd, F_GETFL, 0); 
    if (flags == -1) return false; 
    flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); 
    return (fcntl(fd, F_SETFL, flags) == 0) ? true : false; 
#endif 
} 
+0

Tôi nghĩ bạn định #include thay vì #import. – tambre

+0

Tôi đã sửa nó, cảm ơn. –

+0

(cờ <0) không đúng. Chỉ '-1' có nghĩa là lỗi, các giá trị số nguyên âm khác có thể là các bitmask cờ hợp lệ. –

33

Bạn bị thông báo sai về fcntl() không phải lúc nào cũng đáng tin cậy. Đó là không đúng sự thật.

Để đánh dấu một ổ cắm như non-blocking mã cũng đơn giản như:

// where socketfd is the socket you want to make non-blocking 
int status = fcntl(socketfd, F_SETFL, fcntl(socketfd, F_GETFL, 0) | O_NONBLOCK); 

if (status == -1){ 
    perror("calling fcntl"); 
    // handle the error. By the way, I've never seen fcntl fail in this way 
} 

Dưới Linux, về hạt nhân> 2.6.27 bạn cũng có thể tạo ổ cắm non-blocking ngay từ đầu bằng socket()accept4() .

ví dụ:

// client side 
    int socketfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); 

    // server side - see man page for accept4 under linux 
    int socketfd = accept4(... , SOCK_NONBLOCK); 

Nó tiết kiệm một chút công việc nhưng ít di động hơn vì vậy tôi có xu hướng đặt nó với fcntl().

+0

oops, chỉ cần nhận ra đây là một câu hỏi thực sự cũ. à, tôi nghĩ đã đến lúc cung cấp câu trả lời cập nhật. – Matt

+3

có thể cũ, nhưng vẫn hữu ích. Câu trả lời của bạn dường như chính xác là những gì tôi đang tìm kiếm. –

+0

Không phải là nội bộ 'fcntl' của bạn thất bại trước khi' fcntl' bên ngoài thất bại? – CMCDragonkai

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