Linux: apakah ada read atau recv dari socket dengan timeout?

105

Bagaimana cara saya mencoba membaca data dari soket dengan batas waktu? Saya tahu, pilih, pilih, jajak pendapat, memiliki kolom batas waktu, tetapi menggunakannya menonaktifkan "jalur cepat tcp" di tumpukan reno tcp.

Satu-satunya ide yang saya miliki adalah menggunakan recv (fd, ..., MSG_DONTWAIT) dalam satu lingkaran

osgx
sumber
Ada juga opsi untuk menggunakan utas :) tetapi sinyal utas masih diperlukan
osgx

Jawaban:

189

Anda dapat menggunakan fungsi setsockopt untuk mengatur batas waktu pada operasi penerimaan:

SO_RCVTIMEO

Menyetel nilai batas waktu yang menentukan jumlah waktu maksimum yang menunggu fungsi masukan hingga selesai. Ini menerima struktur timeval dengan jumlah detik dan mikrodetik yang menentukan batas berapa lama menunggu operasi input selesai. Jika operasi penerimaan telah diblokir selama ini tanpa menerima data tambahan, itu akan kembali dengan hitungan parsial atau errno diatur ke [EAGAIN] atau [EWOULDBLOCK] jika tidak ada data yang diterima. Default untuk opsi ini adalah nol, yang menunjukkan bahwa operasi penerimaan tidak boleh habis. Opsi ini membutuhkan struktur interval waktu. Perhatikan bahwa tidak semua implementasi mengizinkan opsi ini disetel.

// 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);

Kabarnya di Windows ini harus dilakukan sebelum menelepon bind. Saya telah memverifikasi dengan eksperimen bahwa itu dapat dilakukan sebelum atau sesudah binddi Linux dan OS X.

Robert S. Barnes
sumber
1
Jawaban ini menyelamatkan pantatku. Saya terjebak menerapkan omong kosong "pilih" yang berbelit-belit itu, tanpa hasil. Ini bekerja dengan segera, jauh lebih sederhana.
MiloDC
Sekarang itulah mengapa waktu tunggu di windows tidak berfungsi karena saya menggunakan kode untuk linux. Terima kasih. Jika windows tidak digunakan, struct timeval tv;apakah itu berarti pilih () tidak akan berfungsi juga? Saya mencoba mem-porting kode pilih () saya ke windows dan itu hanya waktu tunggu segera sepertinya mengabaikan nilai yang saya atur pada timeval.
kuchi
1
Saya menetapkan nilai batas waktu ke 5 detik. Mengapa selalu membutuhkan waktu 5 detik untuk setiap siklus pembacaan terlepas dari ada data yang masuk atau tidak?
Han
ini juga berfungsi di windows bahkan setelah operasi bind. mencoba di windows 10
cahit beyaz
1
@ user463035818 Jawaban ini mengklaim seharusnya tidak.
Tomeamis
22

Berikut beberapa kode sederhana untuk menambahkan waktu ke recvfungsi Anda menggunakan pollC:

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;
}
Abdessamad Doughri
sumber
ini tidak akan bekerja persis seperti yang diharapkan. pollakan menunggu untuk menerima setidaknya satu byte atau batas waktu, sedangkan saat memanggil recvfungsi itu akan menunggu sizeof(buf)byte, menyebabkannya memblokir lagi jika jumlah ini belum tiba, tetapi kali ini tanpa batas waktu.
LoPiTaL
0

// bekerja juga setelah operasi bind untuk WINDOWS

DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
cahit beyaz
sumber
-1

Pasang penangan untuk SIGALRM, lalu gunakan alarm()atau ualarm()sebelum pemblokiran biasa recv(). Jika alarm berbunyi, recv()akan mengembalikan kesalahan dengan errnodisetel ke EINTR.

kafe
sumber
8
alarm (dan sinyal) adalah cara yang salah untuk tugas ini. Jika saya ingin menggunakan jalur cepat tcp, daripada saya membutuhkan latensi minimal. Sinyal lambat.
osgx
2
@osgx Sinyal hanya muncul jika ada batas waktu.
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));

JENDELA

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

CATATAN : Anda telah meletakkan pengaturan ini sebelum bind()panggilan fungsi untuk menjalankan dengan benar

vivek
sumber
4
Pertanyaan ini sudah dijawab bertahun-tahun yang lalu. Nilai baru apa yang diberikan solusi Anda?
Maciej Jureczko
Anda telah meletakkan pengaturan ini sebelum panggilan fungsi bind () untuk menjalankan dengan benar, bagian ini tidak disebutkan dalam jawaban
vivek