Linux: có đọc hoặc đọc lại từ socket với thời gian chờ không?


105

Làm cách nào tôi có thể thử đọc dữ liệu từ socket với thời gian chờ? Tôi biết, select, pselect, thăm dò, có trường thời gian chờ, nhưng việc sử dụng chúng sẽ vô hiệu hóa "tcp fast-path" trong tcp Reno stack.

Ý tưởng duy nhất tôi có là sử dụng recv (fd, ..., MSG_DONTWAIT) trong một vòng lặp


Ngoài ra còn có một tùy chọn sử dụng chủ đề :) nhưng tín hiệu chủ đề vẫn cần thiết
osgx

Câu trả lời:


189

Bạn có thể sử dụng hàm setsockopt để đặt thời gian chờ cho các hoạt động nhận:

SO_RCVTIMEO

Đặt giá trị thời gian chờ chỉ định lượng thời gian tối đa mà một hàm đầu vào đợi cho đến khi hoàn thành. Nó chấp nhận cấu trúc định thời gian với số giây và micro giây xác định giới hạn về thời gian chờ một thao tác nhập hoàn tất. Nếu một thao tác nhận đã bị chặn trong khoảng thời gian này mà không nhận được thêm dữ liệu, nó sẽ trả về với số lượng một phần hoặc lỗi được đặt thành [EAGAIN] hoặc [EWOVERBLOCK] nếu không có dữ liệu nào được nhận. Giá trị mặc định cho tùy chọn này là 0, điều này chỉ ra rằng thao tác nhận sẽ không hết thời gian. Tùy chọn này có cấu trúc thời gian. Lưu ý rằng không phải tất cả các triển khai đều cho phép thiết lập tùy chọn này.

// LINUX
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

// WINDOWS
DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);

// MAC OS X (identical to Linux)
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

Được biết trên Windows, điều này nên được thực hiện trước khi gọi bind. Tôi đã xác minh bằng thử nghiệm rằng nó có thể được thực hiện trước hoặc sau bindtrên Linux và OS X.


1
Câu trả lời này đã cứu tôi. Tôi đã gặp khó khăn khi triển khai cái tào lao "chọn lọc" phức tạp đó, không thành công. Điều này đã hoạt động ngay lập tức, rất đơn giản.
MiloDC

Đó là lý do tại sao thời gian chờ trên windows không hoạt động vì tôi đang sử dụng mã cho linux. Cảm ơn. Nếu windows không được sử dụng struct timeval tv;thì có nghĩa là select () cũng không hoạt động? Tôi đã thử chuyển mã select () của mình sang cửa sổ và nó chỉ hết thời gian ngay lập tức, có vẻ như nó đang bỏ qua giá trị mà tôi đang đặt tại timeval.
kuchi

1
Tôi đặt giá trị thời gian chờ là 5 giây. Tại sao nó luôn mất 5 giây cho mỗi chu kỳ đọc bất kể có dữ liệu đến hay không?
Han

điều này cũng hoạt động trên các cửa sổ ngay cả sau khi hoạt động liên kết. đã thử trên windows 10
cahit beyaz

1
@ user463035818 Câu trả lời này khẳng định là không nên.
Tomeamis

22

Đây là một số mã đơn giản để thêm thời gian chờ vào recvhàm của bạn bằng cách sử dụng polltrong C:

struct pollfd fd;
int ret;

fd.fd = mySocket; // your socket handler 
fd.events = POLLIN;
ret = poll(&fd, 1, 1000); // 1 second for timeout
switch (ret) {
    case -1:
        // Error
        break;
    case 0:
        // Timeout 
        break;
    default:
        recv(mySocket,buf,sizeof(buf), 0); // get your data
        break;
}

điều này sẽ không hoạt động chính xác như mong đợi. pollsẽ đợi nhận ít nhất một byte hoặc thời gian chờ, trong khi khi gọi recvhàm, nó sẽ đợi sizeof(buf)byte, khiến nó bị chặn lại nếu số lượng này chưa đến, nhưng lần này không có thời gian chờ.
LoPiTaL

0

// cũng hoạt động sau khi hoạt động liên kết cho WINDOWS

DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);

-1

Cài đặt một trình xử lý cho SIGALRM, sau đó sử dụng alarm()hoặc ualarm()trước một trình chặn thông thường recv(). Nếu báo thức kêu, recv()sẽ trả về lỗi với errnođược đặt thành EINTR.


8
báo động (và tín hiệu) là cách sai để thực hiện nhiệm vụ này. Nếu tôi muốn sử dụng đường dẫn nhanh tcp, tôi cần độ trễ tối thiểu. Tín hiệu chậm.
osgx

2
@osgx Tín hiệu chỉ xảy ra nếu có thời gian chờ.
David Schwartz

-4

LINUX

struct timeval tv;
tv.tv_sec = 30;        // 30 Secs Timeout
tv.tv_usec = 0;        // Not init'ing this can cause strange errors
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,sizeof(struct timeval));

CÁC CỬA SỔ

DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));

LƯU Ý : Bạn đã đặt cài đặt này trước khi bind()gọi hàm để chạy đúng cách


4
Câu hỏi này đã được trả lời từ nhiều năm trước. Giải pháp của bạn mang lại giá trị mới nào?
Maciej Jureczko

Bạn đã đặt thiết lập này trước khi bind () gọi hàm cho chạy đúng phần này không được đề cập đến trong ans
vivek
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.