Saya memiliki program server kecil yang menerima koneksi pada TCP atau soket UNIX lokal, membaca perintah sederhana dan, tergantung pada perintahnya, mengirimkan balasan. Masalahnya adalah bahwa klien mungkin tidak tertarik pada jawaban kadang-kadang dan keluar lebih awal, jadi menulis ke soket itu akan menyebabkan SIGPIPE dan membuat server saya crash. Apa praktik terbaik untuk mencegah kecelakaan di sini? Apakah ada cara untuk memeriksa apakah sisi lain dari garis masih membaca? (pilih () tampaknya tidak berfungsi di sini karena selalu mengatakan soket dapat ditulisi). Atau haruskah saya menangkap SIGPIPE dengan pawang dan mengabaikannya?
Metode lain adalah mengganti soket sehingga tidak pernah menghasilkan SIGPIPE saat write (). Ini lebih nyaman di perpustakaan, di mana Anda mungkin tidak menginginkan penangan sinyal global untuk SIGPIPE.
Pada kebanyakan sistem berbasis BSD (MacOS, FreeBSD ...), (dengan asumsi Anda menggunakan C / C ++), Anda dapat melakukan ini dengan:
Dengan ini berlaku, bukannya sinyal SIGPIPE yang dihasilkan, EPIPE akan dikembalikan.
sumber
Saya sangat terlambat ke pesta, tetapi
SO_NOSIGPIPE
tidak portabel, dan mungkin tidak bekerja pada sistem Anda (tampaknya menjadi hal BSD).Alternatif yang baik jika Anda menggunakan, katakanlah, sistem Linux tanpa
SO_NOSIGPIPE
akan mengaturMSG_NOSIGNAL
bendera pada panggilan kirim (2) Anda.Contoh penggantian
write(...)
olehsend(...,MSG_NOSIGNAL)
(lihat komentar nobar )sumber
QTcpSocket
objek Qt untuk membungkus penggunaan soket, saya harus menggantiwrite
pemanggilan metode dengan OSsend
(menggunakansocketDescriptor
metode). Adakah yang tahu opsi pembersih untuk mengatur opsi ini diQTcpSocket
kelas?Dalam posting ini saya menjelaskan solusi yang mungkin untuk kasus Solaris ketika SO_NOSIGPIPE atau MSG_NOSIGNAL tidak tersedia.
Contoh kode di https://github.com/kroki/XProbes/blob/1447f3d93b6dbf273919af15e59f35cca58fcc23/src/libxprobes.c#L156
sumber
Menangani SIGPIPE Secara Lokal
Biasanya lebih baik untuk menangani kesalahan secara lokal daripada dalam penangan event sinyal global karena secara lokal Anda akan memiliki lebih banyak konteks tentang apa yang terjadi dan apa yang harus diambil.
Saya memiliki lapisan komunikasi di salah satu aplikasi saya yang memungkinkan aplikasi saya untuk berkomunikasi dengan aksesori eksternal. Ketika kesalahan penulisan terjadi, saya melempar dan mengecualikan di lapisan komunikasi dan membiarkannya menggelembung ke blok coba tangkap untuk menangani di sana.
Kode:
Kode untuk mengabaikan sinyal SIGPIPE sehingga Anda dapat menanganinya secara lokal adalah:
Kode ini akan mencegah sinyal SIGPIPE dinaikkan, tetapi Anda akan mendapatkan kesalahan baca / tulis saat mencoba menggunakan soket, jadi Anda harus memeriksanya.
sumber
Anda tidak dapat mencegah proses di ujung pipa keluar, dan jika keluar sebelum Anda selesai menulis, Anda akan mendapatkan sinyal SIGPIPE. Jika Anda SIG_IGN sinyal, maka tulisan Anda akan kembali dengan kesalahan - dan Anda perlu mencatat dan bereaksi terhadap kesalahan itu. Hanya menangkap dan mengabaikan sinyal di handler bukanlah ide yang baik - Anda harus mencatat bahwa pipa sekarang mati dan mengubah perilaku program sehingga tidak menulis ke pipa lagi (karena sinyal akan dihasilkan lagi, dan diabaikan) lagi, dan Anda akan mencoba lagi, dan seluruh proses bisa berjalan untuk waktu yang lama dan menghabiskan banyak daya CPU).
sumber
Saya percaya itu benar. Anda ingin tahu kapan ujung lainnya telah menutup deskriptor mereka dan itulah yang dikatakan SIGPIPE kepada Anda.
Sam
sumber
Manual Linux mengatakan:
Tetapi untuk Ubuntu 12.04 itu tidak benar. Saya menulis tes untuk kasus itu dan saya selalu menerima EPIPE tanpa SIGPIPE. SIGPIPE dibuat jika saya mencoba menulis ke soket yang sama untuk kedua kalinya. Jadi Anda tidak perlu mengabaikan SIGPIPE jika sinyal ini terjadi itu berarti kesalahan logika dalam program Anda.
sumber
Nonaktifkan sigpipe sesuai setiap orang, atau tangkap dan abaikan kesalahan tersebut.
Ya, gunakan select ().
Anda harus memilih pada bit baca . Anda mungkin dapat mengabaikan penulisan bit .
Ketika ujung jauh menutup pegangan file-nya, pilih akan memberi tahu Anda bahwa ada data yang siap dibaca. Ketika Anda pergi dan membacanya, Anda akan mendapatkan kembali 0 byte, yang merupakan cara OS memberi tahu Anda bahwa pegangan file telah ditutup.
Satu-satunya waktu Anda tidak dapat mengabaikan bit tulis adalah jika Anda mengirim volume besar, dan ada risiko ujung yang lain ditumpuk, yang dapat menyebabkan buffer Anda terisi. Jika itu terjadi, maka mencoba menulis ke pegangan file dapat menyebabkan program / utas Anda terblokir atau gagal. Tes pilih sebelum menulis akan melindungi Anda dari itu, tetapi itu tidak menjamin bahwa ujung yang lain sehat atau bahwa data Anda akan tiba.
Perhatikan bahwa Anda bisa mendapatkan sigpipe dari close (), serta saat Anda menulis.
Tutup memerah semua data yang disangga. Jika ujung lainnya sudah ditutup, maka tutup akan gagal, dan Anda akan menerima sigpipe.
Jika Anda menggunakan buffer TCPIP, maka penulisan yang berhasil berarti data Anda telah diantrekan untuk dikirim, itu tidak berarti telah dikirim. Sampai Anda berhasil menutup panggilan, Anda tidak tahu bahwa data Anda telah dikirim.
Sigpipe memberi tahu Anda ada yang tidak beres, tidak memberi tahu Anda apa, atau apa yang harus Anda lakukan.
sumber
Di bawah sistem POSIX modern (yaitu Linux), Anda dapat menggunakan
sigprocmask()
fungsinya.Jika Anda ingin mengembalikan keadaan sebelumnya nanti, pastikan untuk menyimpan
old_state
tempat yang aman. Jika Anda memanggil fungsi itu beberapa kali, Anda harus menggunakan tumpukan atau hanya menyimpan yang pertama atau terakhirold_state
... atau mungkin memiliki fungsi yang menghilangkan sinyal yang diblokir tertentu.Untuk info lebih lanjut baca halaman manual .
sumber
sigprocmask
menambah set sinyal yang diblokir, sehingga Anda dapat melakukan semua ini dengan satu panggilan.old_state
sehingga saya bisa mengembalikannya nanti, jika saya mau. Jika Anda tahu Anda tidak perlu lagi memulihkan kondisi, memang tidak perlu membaca dan menyimpannya dengan cara ini.sigprocmask()
membaca keadaan lama, panggilan untuksigaddset
mengubahnya, panggilan kedua untuksigprocmask()
menulisnya. Anda dapat menghapus untuk panggilan pertama, menginisialisasi dengansigemptyset(&set)
dan mengubah panggilan kedua kesigprocmask(SIG_BLOCK, &set, &old_state)
.