InggrisPerancisSpanyol

favorit OnWorks

perlipc - Online di Cloud

Jalankan perlipc di penyedia hosting gratis OnWorks melalui Ubuntu Online, Fedora Online, emulator online Windows atau emulator online MAC OS

Ini adalah perintah perlipc yang dapat dijalankan di penyedia hosting gratis OnWorks menggunakan salah satu dari beberapa workstation online gratis kami seperti Ubuntu Online, Fedora Online, emulator online Windows atau emulator online MAC OS

PROGRAM:

NAMA


perlipc - Perl komunikasi antarproses (sinyal, fifos, pipa, subproses yang aman,
socket, dan semaphore)

DESKRIPSI


Fasilitas IPC dasar Perl dibangun dari sinyal Unix lama yang bagus, bernama pipa,
pipa terbuka, rutinitas soket Berkeley, dan panggilan SysV IPC. Masing-masing digunakan dalam sedikit
situasi yang berbeda.

sinyal


Perl menggunakan model penanganan sinyal sederhana: hash %SIG berisi nama atau referensi dari
penangan sinyal yang dipasang pengguna. Handler ini akan dipanggil dengan argumen yaitu
nama sinyal yang memicunya. Sebuah sinyal dapat dibangkitkan secara sengaja dari
urutan keyboard tertentu seperti control-C atau control-Z, dikirimkan kepada Anda dari yang lain
proses, atau dipicu secara otomatis oleh kernel ketika peristiwa khusus terjadi, seperti a
proses anak keluar, proses Anda sendiri kehabisan ruang tumpukan, atau mengenai proses
batas ukuran file.

Misalnya, untuk menjebak sinyal interupsi, siapkan pengendali seperti ini:

$sialan kami;

sub catch_zap {
$signname saya = shift;
$sial++;
die "Seseorang mengirimi saya nama SIG$";
}
$SIG{INT} = __PACKAGE__ . "::catch_zap";
$SIG{INT} = \&catch_zap; #strategi terbaik

Sebelum Perl 5.8.0, Anda perlu melakukan sesedikit mungkin dalam
pawang; perhatikan bagaimana semua yang kita lakukan adalah mengatur variabel global dan kemudian memunculkan pengecualian.
Itu karena pada kebanyakan sistem, perpustakaan tidak masuk kembali; khususnya, memori
alokasi dan rutinitas I/O tidak. Itu berarti melakukan hampir apa saja di Anda
handler secara teori dapat memicu kesalahan memori dan dump inti berikutnya - lihat "Ditangguhkan
Sinyal (Sinyal Aman)" di bawah.

Nama-nama sinyal adalah yang terdaftar oleh "kill -l" di sistem Anda, atau Anda bisa
ambil mereka menggunakan modul CPAN IPC::Signal.

Anda juga dapat memilih untuk menetapkan string "IGNORE" atau "DEFAULT" sebagai handler, di mana
case Perl akan mencoba membuang sinyal atau melakukan hal default.

Pada sebagian besar platform Unix, sinyal "CHLD" (kadang-kadang juga dikenal sebagai "CLD") memiliki ciri khusus
perilaku sehubungan dengan nilai "ABAIKAN". Mengatur $SIG{CHLD} ke "IGNORE" pada a
platform memiliki efek tidak membuat proses zombie ketika proses induk gagal
"tunggu()" pada proses turunannya (yaitu, proses turunan secara otomatis dituai). Panggilan
"wait()" dengan $SIG{CHLD} disetel ke "IGNORE" biasanya mengembalikan "-1" pada platform tersebut.

Beberapa sinyal tidak dapat dijebak atau diabaikan, seperti KILL dan STOP (tetapi bukan sinyal
TSTP) sinyal. Perhatikan bahwa mengabaikan sinyal membuat mereka menghilang. Jika Anda hanya menginginkannya
diblokir sementara tanpa hilang Anda harus menggunakan sigprocmask POSIX.

Mengirim sinyal ke ID proses negatif berarti Anda mengirim sinyal ke seluruh
Grup proses Unix. Kode ini mengirimkan sinyal hang-up ke semua proses saat ini
grup proses, dan juga menyetel $SIG{HUP} ke "IGNORE" agar tidak mati sendiri:

# cakupan blok untuk lokal
{
local $SIG{HUP} = "ABAIKAN";
bunuh HUP => -$$;
# penulisan manis dari: kill("HUP", -$$)
}

Sinyal lain yang menarik untuk dikirim adalah sinyal nomor nol. Ini sebenarnya tidak mempengaruhi
proses anak, tetapi sebaliknya memeriksa apakah itu hidup atau telah mengubah UID-nya.

kecuali (bunuh 0 => $kid_pid) {
warning "sesuatu yang jahat terjadi pada $kid_pid";
}

Nomor sinyal nol mungkin gagal karena Anda tidak memiliki izin untuk mengirim sinyal saat diarahkan
pada proses yang UID nyata atau yang disimpan tidak identik dengan UID nyata atau efektif dari
proses pengiriman, meskipun proses tersebut hidup. Anda mungkin dapat menentukan penyebabnya
kegagalan menggunakan $! atau "%!".

kecuali (kill(0 => $pid) || $!{EPERM}) {
warning "$pid terlihat mati";
}

Anda mungkin juga ingin menggunakan fungsi anonim untuk penangan sinyal sederhana:

$SIG{INT} = sub { die "\nKeluar dari sini!\n" };

Penangan SIGCHLD memerlukan perawatan khusus. Jika anak kedua meninggal saat dalam sinyal
handler yang disebabkan oleh kematian pertama, kami tidak akan mendapatkan sinyal lain. Jadi harus mengulang di sini lagi kita
akan meninggalkan anak yang belum dituai sebagai zombie. Dan lain kali dua anak meninggal kita dapatkan
zombie lain. Dan seterusnya.

gunakan POSIX ":sys_wait_h";
$SIG{CHLD} = sub{
while (($anak saya = waitpid(-1, WNOHANG)) > 0) {
$Kid_Status{$anak} = $?;
}
};
# lakukan sesuatu yang bercabang...

Hati-hati: qx(), sistem(), dan beberapa modul untuk memanggil perintah eksternal melakukan a garpu(),
kemudian tunggu() untuk hasilnya. Dengan demikian, penangan sinyal Anda akan dipanggil. Karena tunggu() adalah
sudah dipanggil oleh sistem() or qx(), yang tunggu() di penangan sinyal tidak akan melihat lagi
zombie dan karena itu akan memblokir.

Cara terbaik untuk mencegah masalah ini adalah dengan menggunakan tunggu(), seperti pada contoh berikut:

gunakan POSIX ":sys_wait_h"; # untuk membaca tanpa blokir

anak-anak saya;

$SIG{CHLD} = sub{
# jangan ubah $! dan $? pawang luar
lokal ($!, $?);
while ( ($pid saya = waitpid(-1, WNOHANG)) > 0 ) {
hapus $anak-anak{$pid};
pembersihan_anak($pid, $?);
}
};

sementara (1) {
$pid saya = garpu();
die "tidak dapat melakukan fork" kecuali ditentukan $pid;
jika ($pid == 0) {
# ...
keluar 0;
} Else {
$anak-anak{$pid}=1;
# ...
sistem($perintah);
# ...
}
}

Penanganan sinyal juga digunakan untuk timeout di Unix. Saat dilindungi dengan aman di dalam
blok "eval{}", Anda menyetel penangan sinyal untuk menjebak sinyal alarm dan kemudian menjadwalkannya
satu dikirimkan kepada Anda dalam beberapa detik. Kemudian coba operasi pemblokiran Anda,
menghapus alarm setelah selesai tetapi tidak sebelum Anda keluar dari blok "eval{}". Jika
padam, Anda akan menggunakan mati () untuk melompat keluar dari blok.

Ini sebuah contoh:

my $ALARM_EXCEPTION = "jam alarm restart";
evaluasi {
local $SIG{ALRM} = sub { mati $ALARM_EXCEPTION };
alarm 10;
kawanan(FH, 2) # memblokir kunci tulis
|| die "tidak bisa berkelompok: $!";
alarm 0;
};
if ($@ && $@ !~ quotemeta($ALARM_EXCEPTION)) { mati }

Jika waktu operasi habis adalah sistem() or qx(), teknik ini bertanggung jawab untuk menghasilkan
zombie. Jika ini penting bagi Anda, Anda harus melakukannya sendiri garpu() dan exec (), dan bunuh
proses anak yang salah.

Untuk penanganan sinyal yang lebih kompleks, Anda mungkin melihat modul POSIX standar. Sayangnya,
ini hampir seluruhnya tidak terdokumentasi, tetapi t/lib/posix.t file dari sumber Perl
distribusi memiliki beberapa contoh di dalamnya.

Penanganan itu MENINDAKLANJUTI Sinyal in Daemon
Proses yang biasanya dimulai saat sistem boot dan mati saat sistem dimatikan
down disebut daemon (Disk And Execution MOnitor). Jika proses daemon memiliki
file konfigurasi yang dimodifikasi setelah proses dimulai, harus ada a
cara untuk memberitahu proses itu untuk membaca ulang file konfigurasinya tanpa menghentikan prosesnya.
Banyak daemon menyediakan mekanisme ini menggunakan penangan sinyal "SIGHUP". Ketika Anda ingin memberi tahu
daemon untuk membaca ulang file, cukup kirimkan sinyal "SIGHUP".

Contoh berikut mengimplementasikan daemon sederhana, yang restart sendiri setiap kali
Sinyal "SIGHUP" diterima. Kode sebenarnya terletak di subrutin "code()", yang
hanya mencetak beberapa info debug untuk menunjukkan bahwa itu berfungsi; itu harus diganti dengan yang asli
kode.

#!/usr/bin/Perl

gunakan ketat;
gunakan peringatan;

gunakan POSIX();
gunakan FindBin();
gunakan File::Nama Dasar();
gunakan File::Spec::Functions qw(catfile);

$| = 1;

# buat daemon cross-platform, jadi exec selalu memanggil script
# sendiri dengan jalur yang benar, tidak peduli bagaimana skrip dipanggil.
my $script = File::Nama Dasar::namadasar($0);
$SELF saya = catfile($FindBin::Bin, $script);

# POSIX membuka kedok sigprocmask dengan benar
$SIG{HUP} = sub{
print "dapat SIGHUP\n";
exec($SELF, @ARGV) || die "$0: tidak dapat memulai ulang: $!";
};

kode();

sub kode {
print "PID: $$\n";
print "ARGV: @ARGV\n";
$hitungan saya = 0;
sementara (1) {
tidur 2;
print ++$hitung, "\n";
}
}

Ditangguhkan sinyal (Aman sinyal)
Sebelum Perl 5.8.0, menginstal kode Perl untuk menangani sinyal yang membuat Anda terancam bahaya
dua hal. Pertama, beberapa fungsi perpustakaan sistem masuk kembali. Jika sinyal terputus
saat Perl menjalankan satu fungsi (seperti malloc(3) atau Printf(3)), dan sinyal Anda
handler kemudian memanggil fungsi yang sama lagi, Anda bisa mendapatkan perilaku yang tidak terduga--sering, a
pembuangan inti. Kedua, Perl sendiri tidak masuk kembali pada level terendah. Jika sinyal
menyela Perl saat Perl mengubah struktur data internalnya sendiri, demikian juga
perilaku yang tidak terduga dapat terjadi.

Ada dua hal yang dapat Anda lakukan, mengetahui hal ini: menjadi paranoid atau pragmatis. Itu
pendekatan paranoid adalah melakukan sesedikit mungkin pada penangan sinyal Anda. Tetapkan yang sudah ada
variabel integer yang sudah memiliki nilai, dan kembali. Ini tidak membantu Anda jika Anda masuk
panggilan sistem yang lambat, yang hanya akan dimulai ulang. Itu berarti Anda harus "mati" untuk longjmp(3)
keluar dari pawang. Bahkan ini sedikit angkuh untuk paranoiac sejati, yang menghindari
"mati" di pawang karena sistem is keluar untuk mendapatkan Anda. Pendekatan pragmatis adalah untuk
katakan "Saya tahu risikonya, tetapi lebih suka kenyamanannya", dan lakukan apa pun yang Anda inginkan dalam
penangan sinyal, dan bersiaplah untuk membersihkan pembuangan inti sekarang dan lagi.

Perl 5.8.0 dan yang lebih baru menghindari masalah ini dengan "menunda" sinyal. Artinya, ketika
sinyal dikirim ke proses oleh sistem (ke kode C yang mengimplementasikan Perl) a
flag disetel, dan pawang segera kembali. Kemudian di titik "aman" strategis di
Penerjemah Perl (misalnya ketika akan mengeksekusi opcode baru) flag diperiksa dan
handler level Perl dari %SIG dijalankan. Skema "ditangguhkan" memungkinkan lebih banyak lagi
fleksibilitas dalam pengkodean penangan sinyal seperti yang kita ketahui bahwa juru bahasa Perl berada di tempat yang aman
menyatakan, dan bahwa kita tidak berada dalam fungsi pustaka sistem saat handler dipanggil.
Namun implementasinya berbeda dari Perls sebelumnya dengan cara berikut:

Opcode yang berjalan lama
Karena penerjemah Perl hanya melihat flag sinyal ketika akan mengeksekusi yang baru
opcode, sinyal yang datang selama opcode yang berjalan lama (misalnya ekspresi reguler
operasi pada string yang sangat besar) tidak akan terlihat sampai opcode saat ini selesai.

Jika sinyal dari jenis tertentu menyala beberapa kali selama opcode (seperti dari a
penghitung waktu berbutir halus), pengendali untuk sinyal itu hanya akan dipanggil sekali, setelah
opcode selesai; semua contoh lainnya akan dibuang. Selanjutnya, jika Anda
antrian sinyal sistem kebanjiran sampai ada sinyal yang sudah
dinaikkan tetapi belum ditangkap (dan dengan demikian tidak ditangguhkan) pada saat opcode selesai,
sinyal-sinyal itu mungkin ditangkap dan ditangguhkan selama opcode berikutnya, dengan
terkadang hasil yang mengejutkan. Misalnya, Anda mungkin melihat alarm dikirim bahkan setelah
panggilan alarm(0) karena yang terakhir menghentikan peningkatan alarm tetapi tidak membatalkan
pengiriman alarm dinaikkan tetapi belum tertangkap. Jangan bergantung pada perilaku
dijelaskan dalam paragraf ini karena merupakan efek samping dari implementasi saat ini dan
dapat berubah di versi Perl yang akan datang.

Mengganggu IO
Ketika sinyal dikirimkan (misalnya, SIGINT dari kontrol-C) sistem operasi rusak
ke dalam operasi IO seperti Baca baca(2), yang digunakan untuk mengimplementasikan Perl's garis baca()
fungsi, operator "<>". Pada Perls yang lebih tua, pawang segera dipanggil (dan sebagai
"baca" bukan "tidak aman", ini bekerja dengan baik). Dengan skema "ditangguhkan", pawangnya adalah
tidak dipanggil segera, dan jika Perl menggunakan perpustakaan "stdio" sistem perpustakaan itu
dapat memulai ulang "baca" tanpa kembali ke Perl untuk memberinya kesempatan memanggil %SIG
pawang Jika ini terjadi pada sistem Anda, solusinya adalah menggunakan lapisan ":perlio" untuk
lakukan IO--setidaknya pada pegangan yang ingin Anda tembus dengan sinyal.
(Lapisan ":perlio" memeriksa tanda sinyal dan memanggil penangan %SIG sebelum melanjutkan
operasi IO.)

Default di Perl 5.8.0 dan yang lebih baru adalah secara otomatis menggunakan lapisan ":perlio".

Perhatikan bahwa tidak disarankan untuk mengakses pegangan file di dalam penangan sinyal di mana
sinyal itu telah menginterupsi operasi I/O pada pegangan yang sama. Sementara perl akan di
setidaknya berusaha keras untuk tidak crash, tidak ada jaminan integritas data; Misalnya,
beberapa data mungkin jatuh atau ditulis dua kali.

Beberapa fungsi perpustakaan jaringan seperti gethostbyname() diketahui memilikinya sendiri
implementasi batas waktu yang mungkin bertentangan dengan batas waktu Anda. Jika Anda memiliki
masalah dengan fungsi seperti itu, coba gunakan POSIX sigasi() fungsi, yang melewati
Sinyal aman Perl. Berhati-hatilah bahwa ini membuat Anda mungkin mengingatnya
korupsi, seperti yang dijelaskan di atas.

Alih-alih menyetel $SIG{ALRM}:

local $SIG{ALRM} = sub { mati "alarm" };

coba sesuatu seperti berikut:

gunakan POSIX qw(SIGALRM);
POSIX::sigaction(SIGALRM, POSIX::SigAction->new(sub { die "alarm" }))
|| die "Kesalahan mengatur penangan SIGALRM: $!\n";

Cara lain untuk menonaktifkan perilaku sinyal aman secara lokal adalah dengan menggunakan
Modul "Perl::Unsafe::Signals" dari CPAN, yang memengaruhi semua sinyal.

Panggilan sistem yang dapat dimulai ulang
Pada sistem yang mendukungnya, Perl versi lama menggunakan flag SA_RESTART ketika
menginstal penangan %SIG. Ini berarti bahwa panggilan sistem yang dapat dimulai ulang akan terus berlanjut
daripada kembali ketika sinyal tiba. Untuk mengirimkan sinyal yang ditangguhkan
segera, Perl 5.8.0 dan yang lebih baru lakukan tidak gunakan SA_RESTART. Akibatnya, dapat dimulai ulang
panggilan sistem dapat gagal (dengan $! disetel ke "EINTR") di tempat-tempat yang sebelumnya akan terjadi
telah berhasil.

Lapisan ":perlio" default mencoba ulang "baca", "tulis" dan "tutup" seperti dijelaskan di atas;
panggilan "tunggu" dan "waitpid" yang terputus akan selalu dicoba lagi.

Sinyal sebagai "kesalahan"
Sinyal tertentu seperti SEGV, ILL, dan BUS dihasilkan oleh pengalamatan memori virtual
kesalahan dan "kesalahan" serupa. Ini biasanya fatal: hanya ada sedikit level Perl
pawang dapat melakukannya dengan mereka. Jadi Perl segera mengirimkannya daripada mencoba
menunda mereka.

Sinyal dipicu oleh status sistem operasi
Pada beberapa sistem operasi penangan sinyal tertentu seharusnya "melakukan sesuatu"
sebelum kembali. Salah satu contoh dapat CHLD atau CLD, yang menunjukkan proses anak memiliki
lengkap. Pada beberapa sistem operasi, pengendali sinyal diharapkan "menunggu" untuk
selesai proses anak. Pada sistem seperti itu, skema sinyal yang ditangguhkan tidak akan berfungsi untuk
sinyal-sinyal itu: ia tidak melakukan "menunggu". Sekali lagi kegagalan akan terlihat seperti lingkaran sebagai
sistem operasi akan mengeluarkan kembali sinyal karena ada anak yang selesai
proses yang belum "ditunggu".

Jika Anda ingin perilaku sinyal lama kembali meskipun ada kemungkinan kerusakan memori, atur
variabel lingkungan "PERL_SIGNALS" menjadi "tidak aman". Fitur ini pertama kali muncul di Perl
5.8.1.

Bernama Pipa


Pipa bernama (sering disebut sebagai FIFO) adalah mekanisme IPC Unix lama untuk proses
berkomunikasi pada mesin yang sama. Ini berfungsi seperti pipa anonim biasa, kecuali
bahwa proses pertemuan menggunakan nama file dan tidak perlu terkait.

Untuk membuat pipa bernama, gunakan fungsi "POSIX::mkfifo()".

gunakan POSIX qw(mkfifo);
mkfifo($jalur, 0700) || die "mkfifo $path gagal: $!";

Anda juga dapat menggunakan perintah Unix mknod(1), atau pada beberapa sistem, mkfifo(1). Ini mungkin tidak
berada di jalur normal Anda.

# val pengembalian sistem terbalik, jadi && tidak ||
#
$ENV{PATH} .= ":/ Etc:/usr/dll";
if ( system("mknod", $path, "p")
&& system("mkfifo", $path) )
{
die "mk{nod,fifo} $path gagal";
}

Fifo nyaman ketika Anda ingin menghubungkan proses ke proses yang tidak terkait. ketika kamu
buka fifo, program akan memblokir sampai ada sesuatu di ujung yang lain.

Sebagai contoh, katakanlah Anda ingin memiliki .tanda tangan file menjadi pipa bernama yang memiliki
Program Perl di ujung yang lain. Sekarang setiap kali ada program (seperti mailer, news reader,
program jari, dll.) mencoba membaca dari file itu, program membaca akan membaca yang baru
tanda tangan dari program Anda. Kami akan menggunakan operator pengujian file pemeriksaan pipa, -p, mencari
mengetahui apakah ada orang (atau apa pun) yang secara tidak sengaja menghapus fifo kami.

chdir(); # pulang ke rumah
$FIFO saya = ".tanda tangan";

sementara (1) {
kecuali (-p $FIFO) {
batalkan tautan $FIFO; # buang kegagalan apa pun, akan ditangkap nanti
membutuhkan POSIX; # pemuatan modul berat yang tertunda
POSIX::mkfifo($FIFO, 0700)
|| die "tidak bisa mkfifo $FIFO: $!";
}

# blok baris berikutnya sampai ada pembaca
buka (FIFO, "> $FIFO") || die "tidak bisa membuka $FIFO: $!";
print FIFO "John Smith (smith\@host.org)\n", `fortune -s`;
tutup(FIFO) || die "tidak dapat menutup $FIFO: $!";
tidur 2; # untuk menghindari sinyal duplikat
}

Menggunakan Buka() untuk IPC


Dasar Perl Buka() pernyataan juga dapat digunakan untuk interproses searah
komunikasi dengan menambahkan atau menambahkan simbol pipa ke argumen kedua ke
Buka(). Berikut cara memulai sesuatu dalam proses anak yang ingin Anda tulis:

buka(SPOOLER, "| cat -v | lpr -h 2>/dev/null")
|| die "tidak bisa garpu: $!";
local $SIG{PIPE} = sub { die "pipa spooler pecah" };
cetak SPOOLER "barang\n";
tutup SPOOLER || die "kumparan buruk: $! $?";

Dan inilah cara memulai proses anak yang ingin Anda baca:

buka(STATUS, "netstat -an 2>&1 |")
|| die "tidak bisa garpu: $!";
ketika ( ) {
selanjutnya jika /^(tcp|udp)/;
cetak;
}
tutup STATUS || die "netstat buruk: $! $?";

Jika seseorang dapat yakin bahwa program tertentu adalah skrip Perl yang mengharapkan nama file di
@ARGV, programmer yang pintar dapat menulis sesuatu seperti ini:

% program f1 "cmd1|" - f2 "cmd2|" f3 < file tmp

dan tidak peduli dari jenis shell apa itu dipanggil, program Perl akan membaca dari
fillet f1, proses cmd1, masukan standar (file tmp dalam hal ini), f2 file, file cmd2
perintah, dan akhirnya f3 mengajukan. Cukup bagus, ya?

Anda mungkin memperhatikan bahwa Anda dapat menggunakan backticks untuk efek yang sama seperti membuka pipa
untuk dibaca:

print grep { !/^(tcp|udp)/ } `netstat -an 2>&1`;
die "status net buruk ($?)" jika $?;

Meskipun ini benar di permukaan, jauh lebih efisien untuk memproses file satu baris
atau rekam sekaligus karena Anda tidak perlu membaca semuanya ke dalam memori di
sekali. Ini juga memberi Anda kontrol yang lebih baik dari seluruh proses, membiarkan Anda mematikan
proses anak lebih awal jika Anda mau.

Berhati-hatilah untuk memeriksa nilai pengembalian dari keduanya Buka() dan Menutup(). Jika kamu penulisan untuk
pipa, Anda juga harus menjebak SIGPIPE. Jika tidak, pikirkan apa yang terjadi ketika Anda memulai
pipa ke perintah yang tidak ada: the Buka() kemungkinan besar akan berhasil (hanya
mencerminkan garpu()'s success), tetapi kemudian output Anda akan gagal--secara spektakuler. Perl tidak bisa
tahu apakah perintah itu berfungsi, karena perintah Anda sebenarnya berjalan secara terpisah
proses yang exec () mungkin telah gagal. Oleh karena itu, sementara pembaca perintah palsu kembali
hanya EOF cepat, penulis untuk perintah palsu akan terkena sinyal, yang mereka terbaik
bersiaplah untuk menangani. Mempertimbangkan:

buka(FH, "|bogus") || die "tidak bisa garpu: $!";
cetak FH "bang\n"; # tidak perlu atau cukup
# untuk memeriksa retval cetak!
tutup(FH) || die "tidak bisa menutup: $!";

Alasan untuk tidak memeriksa nilai pengembalian dari mencetak() adalah karena buffering pipa;
penulisan fisik tertunda. Itu tidak akan meledak sampai penutupan, dan itu akan meledak dengan
sebuah SIGPIPE. Untuk menangkapnya, Anda bisa menggunakan ini:

$SIG{PIPE} = "ABAIKAN";
buka(FH, "|bogus") || die "tidak bisa garpu: $!";
cetak FH "bang\n";
tutup(FH) || die "tidak bisa menutup: status=$?";

Pegangan file
Baik proses utama maupun proses turunannya memiliki STDIN, STDOUT, dan . yang sama
menangani file STDERR. Jika kedua proses mencoba mengaksesnya sekaligus, hal-hal aneh dapat
terjadi. Anda mungkin juga ingin menutup atau membuka kembali pegangan file untuk anak. Kamu bisa mendapatkan
sekitar ini dengan membuka pipa Anda dengan Buka(), tetapi pada beberapa sistem ini berarti bahwa
proses anak tidak bisa hidup lebih lama dari orang tua.

Latar Belakang proses
Anda dapat menjalankan perintah di latar belakang dengan:

sistem("cmd &");

STDOUT dan STDERR perintah (dan mungkin STDIN, tergantung pada shell Anda) akan menjadi
sama dengan milik orang tua. Anda tidak perlu menangkap SIGCHLD karena pengambilan garpu ganda
tempat; lihat di bawah untuk detailnya.

Menyelesaikan Disosiasi of Anak dari Induk
Dalam beberapa kasus (memulai proses server, misalnya) Anda ingin sepenuhnya
memisahkan proses anak dari orang tua. Ini sering disebut daemonisasi. SEBUAH
daemon yang berperilaku baik juga akan chdir() ke direktori root sehingga tidak mencegah
melepas sistem file yang berisi direktori tempat peluncurannya, dan
mengarahkan deskriptor file standarnya dari dan ke / dev / null sehingga keluaran acak tidak
berakhir di terminal pengguna.

gunakan POSIX "setsid";

sub daemonisasi {
chdir("/") || die "tidak bisa chdir ke /: $!";
buka(STDIN, "< /dev/null") || die "tidak bisa membaca /dev/null: $!";
buka(STDOUT, "> /dev/null") || die "tidak dapat menulis ke /dev/null: $!";
didefinisikan($pid saya = garpu()) || die "tidak bisa garpu: $!";
keluar jika $pid; # bukan nol sekarang berarti saya orang tua
(setsid() != -1) || die "Tidak dapat memulai sesi baru: $!";
buka(STDERR, ">&STDOUT") || die "tidak bisa menipu stdout: $!";
}

garpu() harus datang sebelum setid() untuk memastikan Anda bukan pemimpin kelompok proses;
itu setid() akan gagal jika Anda. Jika sistem Anda tidak memiliki setid() fungsi,
Buka /dev/tty dan gunakan "TIOCNOTTY" ioctl() di atasnya sebagai gantinya. Melihat tty(4) untuk rincian.

Pengguna non-Unix harus memeriksa "OS_Anda::Proses" modul untuk solusi lain yang mungkin.

Aman Pipe Membuka
Pendekatan lain yang menarik untuk IPC adalah membuat program tunggal Anda menjadi multiproses dan
berkomunikasi antara - atau bahkan di antara - Anda sendiri. Itu Buka() fungsi akan menerima file
argumen dari salah satu "-|" atau "|-" untuk melakukan hal yang sangat menarik: membuat anak terhubung
ke filehandle yang telah Anda buka. Anak menjalankan program yang sama dengan orang tua.
Ini berguna untuk membuka file dengan aman saat dijalankan di bawah asumsi UID atau GID, untuk
contoh. Jika Anda membuka pipa untuk minus, Anda dapat menulis ke filehandle yang Anda buka dan
anak akan menemukannya di -nya STDIN. Jika Anda membuka pipa dari minus, Anda dapat membaca dari
filehandle Anda membuka apa pun yang ditulis anak Anda -nya STDOUT.

gunakan Bahasa Inggris;
my $PRECIOUS = "/path/to/some/safe/file";
$sleep_count saya;
$pid saya;

lakukan {
$pid = buka(KID_TO_WRITE, "|-");
kecuali (didefinisikan $pid) {
peringatkan "tidak dapat melakukan fork: $!";
die "bailing out" jika $sleep_count++ > 6;
tidur 10;
}
} sampai didefinisikan $pid;

if ($pid) { # Saya orang tuanya
cetak KID_TO_WRITE @some_data;
tutup(KID_TO_WRITE) || memperingatkan "anak keluar $?";
} else { # Saya adalah anak
# jatuhkan izin di program setuid dan/atau setgid:
($EUID, $EGID) = ($UID, $GID);
buka (OUTFILE, "> $PRECIOUS")
|| die "tidak bisa membuka $PRECIOUS: $!";
ketika ( ) {
cetak OUTFILE; # STDIN anak adalah KID_TO_WRITE orang tua
}
tutup(OUTFILE) || die "tidak dapat menutup $PRECIOUS: $!";
keluar(0); # jangan lupa ini!!
}

Penggunaan umum lainnya untuk konstruksi ini adalah ketika Anda perlu menjalankan sesuatu tanpa
gangguan cangkang. Dengan sistem(), itu mudah, tetapi Anda tidak dapat menggunakan pipa terbuka
atau backtick dengan aman. Itu karena tidak ada cara untuk menghentikan cangkangnya untuk mendapatkannya
tangan pada argumen Anda. Sebagai gantinya, gunakan kontrol tingkat rendah untuk menelepon exec () langsung.

Inilah backtick atau pipa yang aman untuk dibaca:

$pid saya = buka(KID_TO_READ, "-|");
didefinisikan($pid) || die "tidak bisa garpu: $!";

if ($pid) { # induk
ketika ( ) {
#lakukan sesuatu yang menarik
}
tutup(KID_TO_READ) || memperingatkan "anak keluar $?";

} lain { # anak
($EUID, $EGID) = ($UID, $GID); # suid saja
exec($program, @option, @args)
|| die "tidak dapat menjalankan program: $!";
# TIDAK TERCAPAI
}

Dan inilah pipa aman yang terbuka untuk menulis:

$pid saya = buka(KID_TO_WRITE, "|-");
didefinisikan($pid) || die "tidak bisa garpu: $!";

$SIG{PIPE} = sub { die "ups, $pipa program rusak" };

if ($pid) { # induk
cetak KID_TO_WRITE @data;
tutup(KID_TO_WRITE) || memperingatkan "anak keluar $?";

} lain { # anak
($EUID, $EGID) = ($UID, $GID);
exec($program, @option, @args)
|| die "tidak dapat menjalankan program: $!";
# TIDAK TERCAPAI
}

Sangat mudah untuk melakukan dead-lock suatu proses menggunakan bentuk Buka(), atau memang dengan penggunaan apa pun
of pipa() dengan beberapa subproses. Contoh di atas adalah "aman" karena sederhana
dan panggilan exec (). Lihat "Menghindari Kebuntuan Pipa" untuk prinsip keselamatan umum, tetapi ada
adalah gotcha ekstra dengan Safe Pipe Opens.

Khususnya, jika Anda membuka pipa menggunakan "buka FH, "|-"", maka Anda tidak bisa begitu saja menggunakan
Menutup() dalam proses induk untuk menutup penulis yang tidak diinginkan. Pertimbangkan kode ini:

$pid saya = open(WRITER, "|-"); #garpu buka anak
didefinisikan($pid) || die "garpu pertama gagal: $!";
jika ($pid) {
if ($sub_pid saya = garpu()) {
didefinisikan($sub_pid) || die "garpu kedua gagal: $!";
tutup(Penulis) || die "tidak bisa menutup WRITER: $!";
# sekarang lakukan sesuatu yang lain...
}
else {
# pertama tulis ke PENULIS
# ...
#lalu setelah selesai
tutup(Penulis) || die "tidak bisa menutup WRITER: $!";
keluar(0);
}
}
else {
# pertama lakukan sesuatu dengan STDIN, lalu
keluar(0);
}

Pada contoh di atas, induk sebenarnya tidak ingin menulis ke filehandle WRITER, jadi
itu menutupnya. Namun, karena WRITER dibuka menggunakan "open FH, "|-"", ia memiliki keistimewaan
perilaku: menutupnya memanggil tunggu() (lihat "waitpid" di perlfunc), yang menunggu
subproses untuk keluar. Jika proses anak berakhir menunggu sesuatu terjadi di
bagian bertanda "melakukan sesuatu yang lain", Anda menemui jalan buntu.

Ini juga bisa menjadi masalah dengan subproses perantara dalam kode yang lebih rumit, yang
akan menelepon tunggu() pada semua pegangan file yang terbuka selama kehancuran global--tidak dapat diprediksi
ketertiban.

Untuk mengatasi ini, Anda harus menggunakan secara manual pipa(), garpu(), dan bentuk Buka() yang menetapkan satu
deskriptor file ke yang lain, seperti yang ditunjukkan di bawah ini:

pipa(BACA, PENULIS) || die "pipa gagal: $!";
$pid = garpu();
didefinisikan($pid) || die "garpu pertama gagal: $!";
jika ($pid) {
tutup PEMBACA;
if ($sub_pid saya = garpu()) {
didefinisikan($sub_pid) || die "garpu pertama gagal: $!";
tutup(Penulis) || die "tidak bisa menutup WRITER: $!";
}
else {
# tulis ke PENULIS...
# ...
#lalu setelah selesai
tutup(Penulis) || die "tidak bisa menutup WRITER: $!";
keluar(0);
}
# tulis ke PENULIS...
}
else {
buka(STDIN, "<&READER") || die "tidak dapat membuka kembali STDIN: $!";
tutup(Penulis) || die "tidak bisa menutup WRITER: $!";
# lakukan sesuatu...
keluar(0);
}

Sejak Perl 5.8.0, Anda juga dapat menggunakan bentuk daftar "terbuka" untuk pipa. Ini lebih disukai
ketika Anda ingin menghindari shell menafsirkan karakter meta yang mungkin ada di . Anda
string perintah.

Jadi misalnya, alih-alih menggunakan:

buka(PS_PIPE, "ps aux|") || die "pipe ps tidak bisa dibuka: $!";

Seseorang akan menggunakan salah satu dari ini:

buka(PS_PIPE, "-|", "ps", "aux")
|| die "pipe ps tidak bisa dibuka: $!";

@ps_args = qw[ ps aux ];
buka(PS_PIPE, "-|", @ps_args)
|| die "tidak bisa membuka @ps_args|: $!";

Karena ada lebih dari tiga argumen untuk Buka(), garpu ps(1) perintah tanpa
menelurkan shell, dan membaca output standarnya melalui filehandle "PS_PIPE". Itu
sintaks yang sesuai untuk menulis untuk memerintahkan pipa adalah menggunakan "|-" sebagai ganti "-|".

Ini memang contoh yang agak konyol, karena Anda menggunakan literal string yang
konten benar-benar aman. Oleh karena itu tidak ada alasan untuk menggunakan yang lebih sulit dibaca,
bentuk pipa multi-argumen Buka(). Namun, kapan pun Anda tidak dapat yakin bahwa
argumen program bebas dari karakter meta shell, bentuk yang lebih bagus dari Buka() seharusnya
digunakan. Sebagai contoh:

@grep_args = ("egrep", "-i", $some_pattern, @many_files);
buka(GREP_PIPE, "-|", @grep_args)
|| die "tidak bisa membuka @grep_args|: $!";

Di sini bentuk pipa multi-argumen Buka() lebih disukai karena pola dan memang
bahkan nama file itu sendiri mungkin mengandung metakarakter.

Sadarilah bahwa operasi ini adalah garpu Unix penuh, yang berarti mereka mungkin tidak benar
diimplementasikan pada semua sistem alien.

Menghindari Pipe Kebuntuan
Setiap kali Anda memiliki lebih dari satu subproses, Anda harus berhati-hati agar setiap subproses menutup yang mana pun
setengah dari semua pipa yang dibuat untuk komunikasi antarproses yang tidak digunakan. Hal ini karena
setiap anak yang memproses membaca dari pipa dan mengharapkan EOF tidak akan pernah menerimanya, dan
oleh karena itu jangan pernah keluar. Satu proses menutup pipa tidak cukup untuk menutupnya; yang terakhir
proses dengan pipa terbuka harus menutupnya agar dapat membaca EOF.

Fitur built-in Unix tertentu membantu mencegah hal ini hampir sepanjang waktu. Misalnya,
filehandles memiliki flag "close on exec", yang disetel en massa di bawah kendali $^F
variabel. Ini agar setiap filehandle yang tidak Anda rutekan secara eksplisit ke STDIN, STDOUT atau
STDERR seorang anak program akan otomatis tertutup.

Selalu secara eksplisit dan segera hubungi Menutup() di ujung yang dapat ditulisi dari pipa apa pun, kecuali
proses yang benar-benar menulis untuk itu. Bahkan jika Anda tidak secara eksplisit menelepon Menutup(), Perl
akan tetap Menutup() semua filehandles selama kehancuran global. Seperti yang telah dibahas sebelumnya, jika
filehandle tersebut telah dibuka dengan Safe Pipe Open, ini akan menghasilkan panggilan
tunggu(), yang mungkin lagi menemui jalan buntu.

Dua arah Komunikasi dengan Lain Proses
Meskipun ini bekerja cukup baik untuk komunikasi searah, bagaimana dengan
komunikasi dua arah? Pendekatan yang paling jelas tidak berhasil:

# INI TIDAK BEKERJA!!
buka(PROG_FOR_READING_AND_WRITING, "| beberapa program |")

Jika Anda lupa untuk "menggunakan peringatan", Anda akan kehilangan sepenuhnya diagnostik yang bermanfaat
pesan:

Tidak dapat melakukan pipa dua arah di -e baris 1.

Jika Anda benar-benar ingin, Anda dapat menggunakan standar buka2() dari modul "IPC::Open2" ke
menangkap kedua ujungnya. Ada juga buka3() di "IPC::Open3" untuk I/O tiga arah sehingga Anda
juga dapat menangkap STDERR anak Anda, tetapi melakukannya akan membutuhkan canggung Pilih()
loop dan tidak mengizinkan Anda menggunakan operasi input Perl normal.

Jika Anda melihat sumbernya, Anda akan melihatnya buka2() menggunakan primitif tingkat rendah seperti
pipa() dan exec () syscalls untuk membuat semua koneksi. Meskipun mungkin saja
lebih efisien dengan menggunakan pasangan soket(), ini akan menjadi lebih portabel daripada itu
sudah. Itu buka2() dan buka3() fungsi tidak mungkin bekerja di mana saja kecuali pada a
Sistem Unix, atau setidaknya satu yang menyatakan kepatuhan POSIX.

Berikut adalah contoh penggunaan buka2():

gunakan FileHandle;
gunakan IPC::Open2;
$pid = open2(*Pembaca, *Penulis, "cat -un");
print Penulis "barang\n";
$dapat = ;

Masalah dengan ini adalah buffering benar-benar akan merusak hari Anda. Meskipun
pegangan file "Penulis" Anda otomatis memerah sehingga proses di ujung lain memasukkan data Anda
tepat waktu, Anda biasanya tidak dapat melakukan apa pun untuk memaksa proses itu memberikan datanya kepada
Anda dengan cara yang sama cepatnya. Dalam kasus khusus ini, kami sebenarnya bisa, karena kami
memberikan kucing a -u bendera untuk membuatnya tidak buffer. Tetapi sangat sedikit perintah yang dirancang untuk beroperasi
melalui pipa, jadi ini jarang berhasil kecuali Anda sendiri yang menulis program di ujung yang lain
pipa berujung ganda.

Solusi untuk ini adalah dengan menggunakan perpustakaan yang menggunakan pseudottys untuk membuat program Anda berperilaku
lebih masuk akal. Dengan cara ini Anda tidak perlu memiliki kendali atas kode sumber dari
program yang Anda gunakan. Modul "Expect" dari CPAN juga membahas hal semacam ini.
Modul ini membutuhkan dua modul lain dari CPAN, "IO::Pty" dan "IO::Stty". Ini mengatur
terminal semu untuk berinteraksi dengan program yang bersikeras berbicara dengan perangkat terminal
sopir. Jika sistem Anda didukung, ini mungkin pilihan terbaik Anda.

Dua arah Komunikasi dengan Diri
Jika Anda mau, Anda dapat membuat level rendah pipa() dan garpu() syscalls untuk menjahit ini bersama-sama dengan
tangan. Contoh ini hanya berbicara untuk dirinya sendiri, tetapi Anda dapat membuka kembali pegangan yang sesuai untuk
STDIN dan STDOUT dan panggil proses lainnya. (Contoh berikut tidak memiliki kesalahan yang tepat
memeriksa.)

#!/usr/bin/Perl -w
# pipe1 - komunikasi dua arah menggunakan dua pasang pipa
# dirancang untuk socketpair-challenged
gunakan IO::Menangani; # ribuan baris hanya untuk autoflush :-(
pipa(PARENT_RDR, CHILD_WTR); # XXX: periksa gagal?
pipa(CHILD_RDR, PARENT_WTR); # XXX: periksa gagal?
ANAK_WTR->pembilasan otomatis(1);
PARENT_WTR->pembilasan otomatis(1);

if ($pid = garpu()) {
tutup PARENT_RDR;
tutup PARENT_WTR;
print CHILD_WTR "Pid Induk $$ mengirim ini\n";
chomp($baris = );
print "Pid Induk $$ baru saja membaca ini: '$line'\n";
tutup CHILD_RDR; tutup CHILD_WTR;
tunggupid($pid, 0);
} Else {
die "tidak bisa garpu: $!" kecuali didefinisikan $pid;
tutup CHILD_RDR;
tutup CHILD_WTR;
chomp($baris = );
print "Anak Pid $$ baru saja membaca ini: '$line'\n";
print PARENT_WTR "Child Pid $$ mengirim ini\n";
tutup PARENT_RDR;
tutup PARENT_WTR;
keluar(0);
}

Tetapi Anda sebenarnya tidak perlu melakukan dua panggilan pipa. Jika Anda memiliki pasangan soket() sistem
panggilan, itu akan melakukan ini semua untuk Anda.

#!/usr/bin/Perl -w
# pipe2 - komunikasi dua arah menggunakan socketpair
# "yang terbaik selalu berjalan dua arah"

gunakan Soket;
gunakan IO::Menangani; # ribuan baris hanya untuk autoflush :-(

# Kami mengatakan AF_UNIX karena meskipun *_LOCAL adalah
# POSIX 1003.1g bentuk konstan, banyak mesin
# masih belum punya.
pasangan soket(ANAK, ORANGTUA, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
|| die "pasangan soket: $!";

ANAK->pembilasan otomatis(1);
ORANG TUA->pembilasan otomatis(1);

if ($pid = garpu()) {
tutup ORANG TUA;
print CHILD "Pid Induk $$ mengirim ini\n";
chomp($baris = );
print "Pid Induk $$ baru saja membaca ini: '$line'\n";
tutup ANAK;
tunggupid($pid, 0);
} Else {
die "tidak bisa garpu: $!" kecuali didefinisikan $pid;
tutup ANAK;
chomp($baris = );
print "Anak Pid $$ baru saja membaca ini: '$line'\n";
print PARENT "Child Pid $$ mengirim ini\n";
tutup ORANG TUA;
keluar(0);
}

Soket: Server klien Komunikasi


Meskipun tidak sepenuhnya terbatas pada sistem operasi turunan Unix (mis., WinSock di PC
menyediakan dukungan soket, seperti halnya beberapa perpustakaan VMS), Anda mungkin tidak memiliki soket di
sistem, dalam hal ini bagian ini mungkin tidak akan banyak membantu Anda. Dengan
socket, Anda dapat melakukan kedua sirkuit virtual seperti aliran TCP dan datagram seperti paket UDP.
Anda mungkin dapat melakukan lebih banyak lagi tergantung pada sistem Anda.

Fungsi Perl untuk menangani soket memiliki nama yang sama dengan yang sesuai
panggilan sistem dalam C, tetapi argumen mereka cenderung berbeda karena dua alasan. Pertama, Perl
filehandles bekerja secara berbeda dari deskriptor file C. Kedua, Perl sudah tahu
panjang stringnya, jadi Anda tidak perlu meneruskan informasi itu.

Salah satu masalah utama dengan kode soket kuno di Perl adalah bahwa
menggunakan nilai-nilai hard-code untuk beberapa konstanta, yang sangat merugikan portabilitas. Jika kamu
pernah melihat kode yang melakukan sesuatu seperti secara eksplisit mengatur "$AF_INET = 2", Anda tahu bahwa Anda
dalam masalah besar. Pendekatan yang jauh lebih unggul adalah dengan menggunakan modul "Socket",
yang lebih andal memberikan akses ke berbagai konstanta dan fungsi yang Anda perlukan.

Jika Anda tidak sedang menulis server/klien untuk protokol yang sudah ada seperti NNTP atau SMTP, Anda
harus memikirkan bagaimana server Anda akan tahu kapan klien selesai
berbicara, dan sebaliknya. Sebagian besar protokol didasarkan pada pesan dan tanggapan satu baris (jadi
satu pihak mengetahui pihak lain telah selesai ketika "\n" diterima) atau pesan multi-baris dan
tanggapan yang diakhiri dengan titik pada baris kosong ("\n.\n" mengakhiri pesan/tanggapan).

Internet baris Terminator
Terminator saluran Internet adalah "\015\012". Di bawah varian ASCII dari Unix, itu bisa
biasanya ditulis sebagai "\r\n", tetapi di bawah sistem lain, "\r\n" kadang-kadang mungkin
"\015\015\012", "\012\012\015", atau sesuatu yang sama sekali berbeda. Standar menentukan
menulis "\015\012" untuk menjadi konforman (tegas dalam apa yang Anda berikan), tetapi mereka juga
merekomendasikan menerima satu-satunya "\012" pada input (lunak dalam apa yang Anda butuhkan). Kami belum
selalu sangat baik tentang itu dalam kode di halaman manual ini, tetapi kecuali jika Anda menggunakan Mac
dari jalan kembali di zaman kegelapan pra-Unix, Anda mungkin akan baik-baik saja.

Internet TCP Klien dan Server
Gunakan soket domain-Internet ketika Anda ingin melakukan komunikasi server-klien yang mungkin
memperluas ke mesin di luar sistem Anda sendiri.

Berikut ini contoh klien TCP yang menggunakan soket domain Internet:

#!/usr/bin/Perl -w
gunakan ketat;
gunakan Soket;
my ($jarak jauh, $port, $iaddr, $paddr, $proto, $line);

$jarak jauh = shift || "host lokal";
$port = shift || 2345; # port acak
if ($port =~ /\D/) { $port = getservbyname($port, "tcp") }
die "Tidak ada port" kecuali $port;
$iaddr = inet_aton($jarak jauh) || die "tidak ada host: $jarak jauh";
$paddr = sockaddr_in($pelabuhan, $iaddr);

$proto = getprotobyname("tcp");
socket(SOCK, PF_INET, SOCK_STREAM, $proto) || die "soket: $!";
hubungkan(SOCK, $paddr) || die "koneksi: $!";
sementara ($baris = ) {
cetak $baris;
}

tutup (SOCK) || die "tutup: $!";
keluar(0);

Dan inilah server yang sesuai untuk menyertainya. Kami akan meninggalkan alamat sebagai
"INADDR_ANY" sehingga kernel dapat memilih antarmuka yang sesuai pada host multihomed.
Jika Anda ingin duduk di antarmuka tertentu (seperti sisi eksternal gateway atau firewall
mesin), isi ini dengan alamat asli Anda.

#!/usr/bin/Perl -Dua
gunakan ketat;
MULAI { $ENV{PATH} = "/ usr / bin:/tempat sampah"}
gunakan Soket;
menggunakan Ikan Mas;
$EOL saya = "\015\012";

sub logmsg { print "$0 $$: @_ at ", scalar localtime(), "\n" }

$port saya = shift || 2345;
die "port tidak valid" kecuali jika $port =~ /^ \d+ $/x;

$proto saya = getprotobyname("tcp");

socket(Server, PF_INET, SOCK_STREAM, $proto) || die "soket: $!";
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, paket("l", 1))
|| die "setsockopt: $!";
bind(Server, sockaddr_in($port, INADDR_ANY)) || die "mengikat: $!";
dengarkan(Server, SOMAXCONN) || die "dengarkan: $!";

logmsg "server dimulai pada port $port";

$paddr saya;

$SIG{CHLD} = \&REAPER;

untuk ( ; $paddr = menerima(Klien, Server); tutup Klien) {
saya($port, $iaddr) = sockaddr_in($paddr);
$nama saya = gethostbyaddr($iaddr, AF_INET);

logmsg "koneksi dari $nama [",
inet_ntoa($iaddr), "]
di port $port";

print Client "Halo, $name, sekarang",
skalar waktu lokal(), $EOL;
}

Dan inilah versi multitasking. Ini multitasking seperti kebanyakan server biasa, itu
menelurkan (garpu()s) server budak untuk menangani permintaan klien sehingga server master dapat
cepat kembali ke layanan klien baru.

#!/usr/bin/Perl -Dua
gunakan ketat;
MULAI { $ENV{PATH} = "/ usr / bin:/tempat sampah"}
gunakan Soket;
menggunakan Ikan Mas;
$EOL saya = "\015\012";

sub bibit; # deklarasi maju
sub logmsg { print "$0 $$: @_ at ", scalar localtime(), "\n" }

$port saya = shift || 2345;
die "port tidak valid" kecuali $port =~ /^ \d+ $/x;

$proto saya = getprotobyname("tcp");

socket(Server, PF_INET, SOCK_STREAM, $proto) || die "soket: $!";
setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, paket("l", 1))
|| die "setsockopt: $!";
bind(Server, sockaddr_in($port, INADDR_ANY)) || die "mengikat: $!";
dengarkan(Server, SOMAXCONN) || die "dengarkan: $!";

logmsg "server dimulai pada port $port";

$waitedpid saya = 0;
$paddr saya;

gunakan POSIX ":sys_wait_h";
gunakan Errno;

sub PENUAI {
$ lokal!; # jangan biarkan waitpid() menimpa kesalahan saat ini
while (($pid saya = waitpid(-1, WNOHANG)) > 0 && WIFEXITED($?)) {
logmsg "menuai $waitedpid" . ($? ? " dengan exit $?" : "");
}
$SIG{CHLD} = \&REAPER; # benci SysV
}

$SIG{CHLD} = \&REAPER;

sementara (1) {
$paddr = terima(Klien, Server) || melakukan {
# coba lagi jika accept() dikembalikan karena mendapat sinyal
selanjutnya jika $!{EINTR};
die "terima: $!";
};
saya ($port, $iaddr) = sockaddr_in($paddr);
$nama saya = gethostbyaddr($iaddr, AF_INET);

logmsg "koneksi dari $nama [",
inet_ntoa($iaddr),
"] di port $port";

bibit sub {
$| = 1;
print "Halo, $nama, sekarang", scalar localtime(), $EOL;
exec "/usr/games/fortune" # XXX: terminator baris "salah"
atau mengaku "tidak dapat mengeksekusi keberuntungan: $!";
};
tutup Klien;
}

sub bibit {
$coderef saya = shift;

kecuali (@_ == 0 && $coderef && ref($coderef) eq "KODE") {
mengaku "penggunaan: spawn CODEREF";
}

$pid saya;
kecuali (defined($pid = fork())) {
logmsg "tidak dapat melakukan fork: $!";
kembali;
}
elsif ($pid) {
logmsg "mulai $pid";
kembali; # Saya orang tuanya
}
# jika tidak, saya adalah anak-anak -- pergilah bertelur

buka(STDIN, "<&Klien") || die "tidak dapat memalsukan klien ke stdin";
buka(STDOUT, ">&Klien") || die "tidak dapat menipu klien ke stdout";
## buka(STDERR, ">&STDOUT") || die "tidak dapat menduplikasi stdout ke stderr";
exit($coderef->());
}

Server ini mengambil kesulitan untuk mengkloning versi anak melalui garpu() untuk setiap masuk
meminta. Dengan begitu ia dapat menangani banyak permintaan sekaligus, yang mungkin tidak selalu Anda inginkan.
Bahkan jika Anda tidak garpu(), yang dengarkan () akan memungkinkan banyak koneksi yang tertunda. garpu
server harus sangat berhati-hati dalam membersihkan anak-anak mereka yang mati (disebut
"zombie" dalam bahasa Unix), karena jika tidak, Anda akan segera mengisi tabel proses Anda.
Subrutin REAPER digunakan di sini untuk memanggil tunggu() untuk setiap proses anak yang memiliki
selesai, dengan demikian memastikan bahwa mereka berakhir dengan bersih dan tidak bergabung dengan barisan
mayat hidup.

Dalam loop while kita sebut menerima() dan periksa untuk melihat apakah itu mengembalikan nilai yang salah. Ini
biasanya akan menunjukkan kesalahan sistem yang perlu dilaporkan. Namun, pengenalan
sinyal aman (lihat "Sinyal Ditangguhkan (Sinyal Aman)" di atas) di Perl 5.8.0 berarti bahwa
menerima() mungkin juga terganggu ketika proses menerima sinyal. Ini biasanya
terjadi ketika salah satu subproses bercabang keluar dan memberi tahu proses induk dengan a
sinyal CHLD.

If menerima() terganggu oleh sinyal, $! akan diatur ke EINTR. Jika ini terjadi, kita bisa
melanjutkan dengan aman ke iterasi loop berikutnya dan panggilan lain ke menerima(). Sekarang
penting bahwa kode penanganan sinyal Anda tidak mengubah nilai $!, atau tes ini
kemungkinan besar akan gagal. Dalam subrutin REAPER kami membuat versi lokal $! sebelum menelepon
tunggu(). Ketika tunggu() set $! untuk ECHILD seperti yang pasti terjadi ketika tidak ada lagi
anak-anak menunggu, itu memperbarui salinan lokal dan membiarkan aslinya tidak berubah.

Anda harus menggunakan -T tandai untuk mengaktifkan pemeriksaan noda (lihat perlsec) meskipun kami tidak melakukannya
menjalankan setuid atau setgid. Ini selalu merupakan ide bagus untuk server atau program apa pun yang dijalankan
atas nama orang lain (seperti skrip CGI), karena mengurangi kemungkinan orang dari
luar akan dapat membahayakan sistem Anda.

Mari kita lihat klien TCP lainnya. Yang ini terhubung ke layanan "waktu" TCP pada nomor
mesin yang berbeda dan menunjukkan seberapa jauh jam mereka berbeda dari sistem di mana itu
sedang dijalankan:

#!/usr/bin/Perl -w
gunakan ketat;
gunakan Soket;

$SECS_OF_70_YEARS saya = 2208988800;
sub kwaktu { skalar waktu setempat(shift() || waktu()) }

my $iaddr = gethostbyname("localhost");
$proto saya = getprotobyname("tcp");
$port saya = getservbyname("waktu", "tcp");
$paddr saya = sockaddr_in(0, $iaddr);
saya($host);

$| = 1;
printf "%-24s %8s %s\n", "localhost", 0, ctime();

foreach $host (@ARGV) {
printf "%-24s", $host;
$hisiaddr saya = inet_aton($host) || die "host tidak dikenal";
$hispaddr saya = sockaddr_in($port, $hisiaddr);
soket (SOCKET, PF_INET, SOCK_STREAM, $proto)
|| die "soket: $!";
hubungkan(SOCKET, $hispaddr) || die "koneksi: $!";
my $rtime = paket("C4", ());
baca(SOCKET, $waktu, 4);
tutup (SOCKET);
$histime saya = unpack("N", $rtime) - $SECS_OF_70_YEARS;
printf "%8d %s\n", $histime - time(), ctime($histime);
}

Unix-Domain TCP Klien dan Server
Itu bagus untuk klien dan server domain Internet, tetapi bagaimana dengan komunikasi lokal?
Meskipun Anda dapat menggunakan pengaturan yang sama, terkadang Anda tidak menginginkannya. Soket domain Unix adalah
lokal ke host saat ini, dan sering digunakan secara internal untuk mengimplementasikan pipa. Tidak seperti
Soket domain internet, soket domain Unix dapat muncul di sistem file dengan ls(1)
daftar.

% ls -l /dev/log
srw-rw-rw- 1 root 0 Okt 31 07:23 /dev/log

Anda dapat mengujinya dengan Perl's -S tes berkas:

kecuali (-S "/dev/log") {
die "ada yang salah dengan sistem log";
}

Berikut ini contoh klien domain Unix:

#!/usr/bin/Perl -w
gunakan Soket;
gunakan ketat;
my ($pertemuan, $baris);

$rendezvous = shift || "kaos kaki";
soket(SOCK, PF_UNIX, SOCK_STREAM, 0) || die "soket: $!";
hubungkan(SOCK, sockaddr_un($rendezvous)) || die "koneksi: $!";
sementara (didefinisikan($baris = )) {
cetak $baris;
}
keluar(0);

Dan inilah server yang sesuai. Anda tidak perlu khawatir tentang jaringan konyol
terminator di sini karena soket domain Unix dijamin berada di localhost, dan
dengan demikian semuanya bekerja dengan benar.

#!/usr/bin/Perl -Dua
gunakan ketat;
gunakan Soket;
menggunakan Ikan Mas;

MULAI { $ENV{PATH} = "/ usr / bin:/tempat sampah"}
sub bibit; # deklarasi maju
sub logmsg { print "$0 $$: @_ at ", scalar localtime(), "\n" }

$NAME saya = "kaos kaki";
$uaddr saya = sockaddr_un($NAME);
$proto saya = getprotobyname("tcp");

socket(Server, PF_UNIX, SOCK_STREAM, 0) || die "soket: $!";
batalkan tautan($NAME);
ikat (Server, $uaddr) || die "mengikat: $!";
dengarkan(Server, SOMAXCONN) || die "dengarkan: $!";

logmsg "server dimulai pada $NAME";

$waitedpid saya;

gunakan POSIX ":sys_wait_h";
sub PENUAI {
$anak saya;
while (($waitedpid = waitpid(-1, WNOHANG)) > 0) {
logmsg "menuai $waitedpid" . ($? ? " dengan exit $?" : "");
}
$SIG{CHLD} = \&REAPER; # benci SysV
}

$SIG{CHLD} = \&REAPER;

untuk ( $waitedpid = 0;
terima(Klien, Server) || $tunggupid;
$waitedpid = 0, tutup Klien)
{
selanjutnya jika $waitedpid;
logmsg "koneksi pada $NAME";
bibit sub {
print "Halo, sekarang ", scalar localtime(), "\n";
exec("/usr/games/fortune") || die "tidak dapat mengeksekusi keberuntungan: $!";
};
}

sub bibit {
$coderef saya = shift();

kecuali (@_ == 0 && $coderef && ref($coderef) eq "KODE") {
mengaku "penggunaan: spawn CODEREF";
}

$pid saya;
kecuali (defined($pid = fork())) {
logmsg "tidak dapat melakukan fork: $!";
kembali;
}
elsif ($pid) {
logmsg "mulai $pid";
kembali; # Saya orang tuanya
}
else {
# Saya adalah anak-anak -- pergilah bertelur
}

buka(STDIN, "<&Klien") || die "tidak dapat memalsukan klien ke stdin";
buka(STDOUT, ">&Klien") || die "tidak dapat menipu klien ke stdout";
## buka(STDERR, ">&STDOUT") || die "tidak dapat menduplikasi stdout ke stderr";
exit($coderef->());
}

Seperti yang Anda lihat, ini sangat mirip dengan server TCP domain Internet, sangat mirip, di
faktanya, kami telah menghilangkan beberapa fungsi duplikat--muncul(), pesan log(), waktu(), dan
MESIN PENUAI()--yang sama seperti di server lain.

Jadi mengapa Anda ingin menggunakan soket domain Unix alih-alih pipa bernama yang lebih sederhana?
Karena pipa bernama tidak memberi Anda sesi. Anda tidak dapat membedakan data satu proses dari
milik orang lain. Dengan pemrograman soket, Anda mendapatkan sesi terpisah untuk setiap klien; itu
mengapa menerima() mengambil dua argumen.

Sebagai contoh, katakanlah Anda memiliki daemon server database yang berjalan lama yang Anda inginkan
orang-orang untuk dapat mengakses dari Web, tetapi hanya jika mereka melalui antarmuka CGI.
Anda akan memiliki program CGI kecil dan sederhana yang melakukan pemeriksaan dan pencatatan apa pun yang Anda rasakan
suka, dan kemudian bertindak sebagai klien domain-Unix dan terhubung ke server pribadi Anda.

TCP Klien dengan IO::Soket


Bagi mereka yang lebih memilih antarmuka tingkat yang lebih tinggi daripada pemrograman soket, modul IO::Socket
menyediakan pendekatan berorientasi objek. Jika karena alasan tertentu Anda kekurangan modul ini, Anda dapat
ambil saja IO::Socket dari CPAN, di mana Anda juga akan menemukan modul yang menyediakan antarmuka yang mudah
ke sistem berikut: DNS, FTP, Ident (RFC 931), NIS dan NISPlus, NNTP, Ping, POP3,
SMTP, SNMP, SSLeay, Telnet, dan Time--untuk menyebutkan beberapa saja.

A Sederhana Pelanggan
Berikut adalah klien yang membuat koneksi TCP ke layanan "siang hari" di port 13
nama host "localhost" dan mencetak semua yang disediakan oleh server di sana.

#!/usr/bin/Perl -w
gunakan IO::Socket;
$remote = IO::Socket::INET->new(
Proto => "tcp",
PeerAddr => "host lokal",
PeerPort => "siang hari(13)",
)
|| die "tidak dapat terhubung ke layanan siang hari di localhost";
while (<$jarak jauh>) { print }

Ketika Anda menjalankan program ini, Anda akan mendapatkan sesuatu yang terlihat seperti ini:

Rabu 14 Mei 08:40:46 MDT 1997

Berikut adalah parameter-parameter tersebut untuk baru() konstruktor berarti:

"Proto"
Ini adalah protokol yang digunakan. Dalam hal ini, pegangan soket yang dikembalikan adalah
terhubung ke soket TCP, karena kami menginginkan koneksi berorientasi aliran, yaitu, satu
yang bertindak cukup banyak seperti file lama biasa. Tidak semua soket jenis ini.
Misalnya, protokol UDP dapat digunakan untuk membuat soket datagram, yang digunakan untuk pesan-
lewat.

"PeerAddr"
Ini adalah nama atau alamat Internet dari host jarak jauh tempat server dijalankan. Kami
bisa saja menentukan nama yang lebih panjang seperti "www.perl.com", atau alamat seperti
"207.171.7.72". Untuk tujuan demonstrasi, kami telah menggunakan nama host khusus
"localhost", yang harus selalu berarti mesin yang sedang Anda jalankan. Itu
alamat Internet yang sesuai untuk localhost adalah "127.0.0.1", jika Anda lebih suka menggunakannya.

"PeerPort"
Ini adalah nama layanan atau nomor port yang ingin kami sambungkan. Kita bisa mendapatkan
jauh dengan hanya menggunakan "siang hari" pada sistem dengan layanan sistem yang dikonfigurasi dengan baik
file,[CATATAN KAKI: File layanan sistem ditemukan di / etc / services di bawah Unixy
sistem.] tetapi di sini kami telah menentukan nomor port (13) dalam tanda kurung. Menggunakan hanya
jumlahnya juga akan berfungsi, tetapi literal numerik membuat programmer yang berhati-hati
grogi.

Perhatikan bagaimana nilai kembalian dari konstruktor "baru" digunakan sebagai pegangan file di
lingkaran "sementara"? Itulah yang disebut tidak langsung pegangan file, variabel skalar yang mengandung
menangani file. Anda dapat menggunakannya dengan cara yang sama seperti menangani file biasa. Misalnya, Anda
dapat membaca satu baris darinya dengan cara ini:

$baris = <$pegangan>;

semua baris yang tersisa dari adalah cara ini:

@baris = <$handle>;

dan kirim sebaris data ke sana dengan cara ini:

print $handle "beberapa data\n";

A situs web Pelanggan
Berikut adalah klien sederhana yang mengambil host jarak jauh untuk mengambil dokumen, dan kemudian daftar
file untuk mendapatkan dari host itu. Ini adalah klien yang lebih menarik dari yang sebelumnya
karena pertama kali mengirim sesuatu ke server sebelum mengambil respons server.

#!/usr/bin/Perl -w
gunakan IO::Socket;
kecuali (@ARGV > 1) { die "penggunaan: $0 host url ..." }
$host = shift(@ARGV);
$EOL = "\015\012";
$KOSONG = $EOL x 2;
untuk $dokumen saya (@ARGV) {
$remote = IO::Socket::INET->new( Proto => "tcp",
PeerAddr => $host,
PeerPort => "http(80)",
) || die "tidak dapat terhubung ke httpd di $host";
$jarak jauh->pembilasan otomatis(1);
print $remote "DAPATKAN $document HTTP/1.0" . $KOSONG;
while ( <$remote> ) { print }
tutup $jauh;
}

Server web yang menangani layanan HTTP diasumsikan pada port standarnya, nomor 80.
Jika server yang Anda coba sambungkan berada di port yang berbeda, seperti 1080 atau 8080, Anda
harus menentukannya sebagai pasangan parameter bernama, "PeerPort => 8080". Metode "pembilasan otomatis"
digunakan pada soket karena jika tidak, sistem akan menyangga output yang kami kirimkan.
(Jika Anda menggunakan Mac prasejarah, Anda juga perlu mengubah setiap "\n" dalam kode Anda yang
mengirimkan data melalui jaringan menjadi "\015\012".)

Menghubungkan ke server hanyalah bagian pertama dari proses: setelah Anda memiliki
koneksi, Anda harus menggunakan bahasa server. Setiap server di jaringan memiliki sendiri
sedikit bahasa perintah yang diharapkan sebagai input. String yang kami kirim ke server
dimulai dengan "GET" dalam sintaks HTTP. Dalam hal ini, kami hanya meminta setiap yang ditentukan
dokumen. Ya, kami benar-benar membuat koneksi baru untuk setiap dokumen, meskipun itu
tuan rumah yang sama. Begitulah cara Anda dulu harus berbicara HTTP. Versi terbaru dari
browser web dapat meminta server jarak jauh untuk membiarkan koneksi terbuka sebentar,
tetapi server tidak harus memenuhi permintaan seperti itu.

Berikut adalah contoh menjalankan program itu, yang akan kita sebut dapatkan web:

% webget www.perl.com /guanaco.html
File HTTP/1.1 404 Tidak Ditemukan
Tanggal: Kam, 08 Mei 1997 18:02:32 GMT
Server: Apache/1.2b6
Koneksi: tutup
Jenis konten: teks/html

404 Berkas Tidak Ditemukan
Berkas tidak ditemukan
URL yang diminta /guanaco.html tidak ditemukan di server ini.


Ok, jadi kurang menarik, karena tidak menemukan dokumen tertentu. Tetapi
tanggapan yang panjang tidak akan muat di halaman ini.

Untuk versi yang lebih berfitur dari program ini, Anda harus melihat ke lwp-permintaan program
disertakan dengan modul LWP dari CPAN.

Interaktif Pelanggan dengan IO::Soket
Yah, tidak apa-apa jika Anda ingin mengirim satu perintah dan mendapatkan satu jawaban, tetapi bagaimana dengan
menyiapkan sesuatu yang sepenuhnya interaktif, agak seperti caranya telnet bekerja? Dengan begitu kamu
dapat mengetik baris, mendapatkan jawaban, mengetik baris, mendapatkan jawaban, dll.

Klien ini lebih rumit daripada dua yang telah kami lakukan sejauh ini, tetapi jika Anda menggunakan sistem
yang mendukung panggilan "garpu" yang kuat, solusinya tidak terlalu sulit. Setelah Anda membuat
koneksi ke layanan apa pun yang ingin Anda ajak mengobrol, panggil "fork" untuk mengkloning Anda
proses. Masing-masing dari dua proses yang identik ini memiliki pekerjaan yang sangat sederhana untuk dilakukan: induk
menyalin semuanya dari soket ke output standar, sementara anak secara bersamaan
menyalin semuanya dari input standar ke soket. Untuk mencapai hal yang sama menggunakan
hanya satu proses akan menjadi banyak lebih sulit, karena lebih mudah mengkode dua proses untuk melakukan satu
hal daripada kode satu proses untuk melakukan dua hal. (Prinsip sederhana ini a
landasan filosofi Unix, dan rekayasa perangkat lunak yang baik juga, yaitu
mungkin mengapa itu menyebar ke sistem lain.)

Berikut kodenya:

#!/usr/bin/Perl -w
gunakan ketat;
gunakan IO::Socket;
saya ($host, $port, $kidpid, $handle, $line);

kecuali (@ARGV == 2) { die "penggunaan: $0 port host" }
($host, $port) = @ARGV;

# buat koneksi tcp ke host dan port yang ditentukan
$handle = IO::Socket::INET->new(Proto => "tcp",
PeerAddr => $host,
PeerPort => $port)
|| die "tidak dapat terhubung ke port $port pada $host: $!";

$pegangan->pembilasan otomatis(1); # jadi hasilnya langsung sampai
print STDERR "[Terhubung ke $host:$port]\n";

# bagi program menjadi dua proses, kembar identik
die "tidak bisa garpu: $!" kecuali jika didefinisikan($kidpid = garpu());

# blok if{} hanya berjalan dalam proses induk
jika ($anak) {
# salin soket ke output standar
while (didefinisikan ($line = <$handle>)) {
cetak STDOUT $baris;
}
kill("JANGKA", $kidpid); # kirim SIGTERM ke anak
}
# blok else{} hanya berjalan dalam proses anak
else {
# salin input standar ke soket
while (didefinisikan ($line = )) {
cetak $handle $line;
}
keluar(0); # untuk berjaga-jaga
}

Fungsi "bunuh" di blok "jika" orang tua ada untuk mengirim sinyal ke anak kita
proses, saat ini berjalan di blok "lain", segera setelah server jarak jauh ditutup
ujung sambungannya.

Jika server jarak jauh mengirimkan data satu byte pada suatu waktu, dan Anda memerlukan data itu segera tanpa
menunggu baris baru (yang mungkin tidak terjadi), Anda mungkin ingin mengganti loop "sementara"
pada orang tua dengan ketentuan sebagai berikut:

$byte saya;
while (sysread($handle, $byte, 1) == 1) {
cetak STDOUT $byte;
}

Membuat panggilan sistem untuk setiap byte yang ingin Anda baca tidak terlalu efisien (dengan kata lain
sedikit) tetapi yang paling sederhana untuk dijelaskan dan bekerja dengan cukup baik.

TCP Server dengan IO::Soket


Seperti biasa, menyiapkan server sedikit lebih terlibat daripada menjalankan klien. Itu
modelnya adalah server membuat jenis soket khusus yang tidak melakukan apa pun selain mendengarkan
port tertentu untuk koneksi masuk. Ia melakukan ini dengan memanggil
Metode "IO::Socket::INET->new()" dengan argumen yang sedikit berbeda dari yang dilakukan klien.

proto
Ini adalah protokol yang digunakan. Seperti klien kami, kami masih akan menentukan "tcp" di sini.

Pelabuhan Lokal
Kami menentukan port lokal dalam argumen "LocalPort", yang tidak kami lakukan untuk
klien. Ini adalah nama layanan atau nomor port yang ingin Anda jadikan server.
(Di bawah Unix, port di bawah 1024 dibatasi untuk superuser.) Dalam contoh kami, kami akan
gunakan port 9000, tetapi Anda dapat menggunakan port apa pun yang saat ini tidak digunakan di sistem Anda.
Jika Anda mencoba menggunakan yang sudah digunakan, Anda akan mendapatkan pesan "Alamat sudah digunakan".
Di bawah Unix, perintah "netstat -a" akan menunjukkan layanan mana yang saat ini memiliki server.

Mendengarkan
Parameter "Dengarkan" diatur ke jumlah maksimum koneksi tertunda yang kami dapat
terima sampai kami menolak klien yang masuk. Anggap saja sebagai antrian panggilan tunggu untuk
telepon Anda. Modul Soket tingkat rendah memiliki simbol khusus untuk sistem
maksimum, yaitu SOMAXCONN.

Reuse
Parameter "Reuse" diperlukan agar server kita restart secara manual tanpa menunggu
beberapa menit untuk memungkinkan buffer sistem keluar.

Setelah soket server generik dibuat menggunakan parameter yang tercantum di atas,
server kemudian menunggu klien baru untuk terhubung. Server memblokir di "terima"
metode, yang akhirnya menerima koneksi dua arah dari klien jarak jauh. (Membuat
pastikan untuk autoflush pegangan ini untuk menghindari buffering.)

Untuk menambah keramahan pengguna, server kami meminta pengguna untuk perintah. Sebagian besar server tidak
melakukan hal ini. Karena prompt tanpa baris baru, Anda harus menggunakan "sysread"
varian dari klien interaktif di atas.

Server ini menerima salah satu dari lima perintah yang berbeda, mengirimkan output kembali ke klien.
Tidak seperti kebanyakan server jaringan, server ini hanya menangani satu klien yang masuk dalam satu waktu.
Server multitasking tercakup dalam Bab 16 dari Unta.

Berikut kodenya. Sehat

#!/usr/bin/Perl -w
gunakan IO::Socket;
gunakan Net::host; # untuk gethostbyaddr versi OOish

$PORT = 9000; # pilih sesuatu yang tidak digunakan

$server = IO::Socket::INET->new( Proto => "tcp",
LocalPort => $PORT,
Dengarkan => SOMAXCONN,
Gunakan kembali => 1);

die "tidak dapat mengatur server" kecuali $server;
print "[Server $0 menerima klien]\n";

while ($client = $server->accept()) {
$klien->pembilasan otomatis(1);
print $client "Selamat datang di $0; ketik bantuan untuk daftar perintah.\n";
$hostinfo = gethostbyaddr($klien->peeraddr);
printf "[Hubungkan dari %s]\n", $hostinfo ? $hostinfo->nama : $client->peerhost;
print $client "Perintah? ";
sementara ( <$klien>) {
berikutnya kecuali /\S/; # garis kosong
if (/keluar|keluar/i) { terakhir }
elsif (/tanggal|waktu/i) { printf $client "%s\n", scalar localtime() }
elsif (/who/i ) { print $client `who 2>&1` }
elsif (/cookie/i ) { print $client `/usr/games/fortune 2>&1` }
elsif (/motd/i ) { print $client `cat /etc/motd 2>&1` }
else {
print $client "Perintah: tanggal keluar siapa cookie motd\n";
}
} melanjutkan {
print $client "Perintah? ";
}
tutup $klien;
}

PDU: Pesan Lewat


Jenis lain dari pengaturan client-server adalah yang tidak menggunakan koneksi, tetapi pesan. UDP
komunikasi melibatkan overhead yang jauh lebih rendah tetapi juga memberikan keandalan yang lebih rendah, karena ada
tidak ada janji bahwa pesan akan sampai sama sekali, apalagi teratur dan tidak tercerai-berai. Tetap,
UDP menawarkan beberapa keunggulan dibandingkan TCP, termasuk dapat "menyiarkan" atau "multicast" ke
sejumlah besar host tujuan sekaligus (biasanya di subnet lokal Anda). Jika kamu menemukan
diri Anda terlalu khawatir tentang keandalan dan mulai membangun pemeriksaan ke dalam pesan Anda
sistem, maka Anda mungkin harus menggunakan TCP saja untuk memulai.

Datagram UDP adalah tidak bytestream dan tidak boleh diperlakukan seperti itu. Ini membuat menggunakan
Mekanisme I/O dengan buffering internal seperti stdio (mis mencetak() dan teman-teman) terutama
rumit. Menggunakan penulisan sistem(), atau lebih baik Kirim(), seperti pada contoh di bawah ini.

Berikut adalah program UDP yang mirip dengan contoh klien Internet TCP yang diberikan sebelumnya. Namun,
alih-alih memeriksa satu host pada satu waktu, versi UDP akan memeriksa banyak dari mereka
secara asinkron dengan mensimulasikan multicast dan kemudian menggunakan Pilih() untuk melakukan waktu tunggu
untuk I/O. Untuk melakukan sesuatu yang mirip dengan TCP, Anda harus menggunakan pegangan soket yang berbeda
untuk setiap tuan rumah.

#!/usr/bin/Perl -w
gunakan ketat;
gunakan Soket;
gunakan Sys::Hostname;

my( $hitung, $hisiaddr, $hispaddr, $histime,
$host, $iaddr, $paddr, $port, $proto,
$rin, $keluar, $waktu, $SECS_OF_70_YEARS);

$SECS_OF_70_YEARS = 2_208_988_800;

$iaddr = gethostbyname(namahost());
$proto = getprotobyname("udp");
$port = getservbyname("waktu", "udp");
$paddr = sockaddr_in(0, $iaddr); # 0 berarti biarkan kernel memilih

socket(SOCKET, PF_INET, SOCK_DGRAM, $proto) || die "soket: $!";
bind(SOCKET, $paddr) || die "mengikat: $!";

$| = 1;
printf "%-12s %8s %s\n", "localhost", 0, skalar waktu lokal();
$ Hitung = 0;
untuk $host (@ARGV) {
$hitung++;
$hisiaddr = inet_aton($host) || die "host tidak dikenal";
$hispaddr = sockaddr_in($pelabuhan, $hisiaddr);
didefinisikan(kirim(SOCKET, 0, 0, $hispaddr)) || die "kirim $host: $!";
}

$rin = "";
vec($rin, fileno(SOCKET), 1) = 1;

# batas waktu setelah 10.0 detik
while ($hitung && pilih($rout = $rin, undef, undef, 10.0)) {
$rwaktu = "";
$hispaddr = recv(SOCKET, $rtime, 4, 0) || die "rekv: $!";
($pelabuhan, $hisiaddr) = sockaddr_in($hispaddr);
$host = gethostbyaddr($hisiaddr, AF_INET);
$histime = unpack("N", $rtime) - $SECS_OF_70_YEARS;
printf "%-12s", $host;
printf "%8d %s\n", $histime - time(), scalar localtime($histime);
$hitung--;
}

Contoh ini tidak termasuk percobaan ulang dan akibatnya mungkin gagal untuk menghubungi yang dapat dijangkau
tuan rumah. Alasan paling menonjol untuk ini adalah kemacetan antrian di host pengirim
jika jumlah host yang harus dihubungi cukup besar.

sysv IPC


Meskipun System V IPC tidak begitu banyak digunakan sebagai soket, ia masih memiliki beberapa kegunaan yang menarik.
Namun, Anda tidak dapat menggunakan SysV IPC atau Berkeley peta() untuk memiliki variabel yang dibagikan di antara
beberapa proses. Itu karena Perl akan merealokasi string Anda ketika Anda tidak
menginginkannya. Anda mungkin melihat modul "IPC::Shareable" atau "threads::shared" untuk
bahwa.

Berikut adalah contoh kecil yang menunjukkan penggunaan memori bersama.

gunakan IPC::SysV qw(IPC_PRIVATE IPC_RMID S_IRUSR S_IWUSR);

$ukuran = 2000;
$id = shmget(IPC_PRIVATE, $ukuran, S_IRUSR | S_IWUSR);
didefinisikan($id) || die "shmget: $!";
print "kunci shm $id\n";

$pesan = "Pesan #1";
shmwrite($id, $message, 0, 60) || die "shmwrite: $!";
print "tulis: '$pesan'\n";
shmread($id, $buff, 0, 60) || die "shmread: $!";
print "baca : '$buff'\n";

# buffer shmread adalah bantalan akhir karakter nol.
substr($penggemar, indeks($penggemar, "\0")) = "";
print "un" kecuali $buff eq $message;
print "membengkak\n";

print "menghapus shm $id\n";
shmctl($id, IPC_RMID, 0) || die "shmctl: $!";

Berikut adalah contoh semaphore:

gunakan IPC::SysV qw(IPC_CREAT);

$IPC_KEY = 1234;
$id = semget($IPC_KEY, 10, 0666 | IPC_CREAT);
didefinisikan($id) || die "semget: $!";
print "sem id $id\n";

Letakkan kode ini di file terpisah untuk dijalankan di lebih dari satu proses. Panggil filenya mengambil:

# buat semafor

$IPC_KEY = 1234;
$id = semget($IPC_KEY, 0, 0);
didefinisikan($id) || die "semget: $!";

$semnum = 0;
$semflag = 0;

# "ambil" semafor
# tunggu semaphore menjadi nol
$semop = 0;
$opstring1 = pack("s!s!s!", $semnum, $semop, $semflag);

# Tingkatkan jumlah semaphore
$semop = 1;
$opstring2 = pack("s!s!s!", $semnum, $semop, $semflag);
$opstring = $opstring1 . $opstring2;

semop($id, $opstring) || die "semop: $!";

Letakkan kode ini di file terpisah untuk dijalankan di lebih dari satu proses. Panggil file ini memberikan:

# "berikan" semaphore
# jalankan ini dalam proses asli dan Anda akan melihat
# agar proses kedua berlanjut

$IPC_KEY = 1234;
$id = semget($IPC_KEY, 0, 0);
mati kecuali jika didefinisikan($id);

$semnum = 0;
$semflag = 0;

# Kurangi jumlah semaphore
$semop = -1;
$opstring = pack("s!s!s!", $semnum, $semop, $semflag);

semop($id, $opstring) || die "semop: $!";

Kode IPC SysV di atas sudah lama ditulis, dan pasti terlihat kikuk. Untuk sebuah
tampilan lebih modern, lihat modul IPC::SysV.

Contoh kecil yang menunjukkan antrian pesan SysV:

gunakan IPC::SysV qw(IPC_PRIVATE IPC_RMID IPC_CREAT S_IRUSR S_IWUSR);

$id saya = msgget(IPC_PRIVATE, IPC_CREAT | S_IRUSR | S_IWUSR);
didefinisikan($id) || die "pesan gagal: $!";

$terkirim saya = "pesan";
$type_sent saya = 1234;

msgsnd($id, pack("l! a*", $type_sent, $sent), 0)
|| die "pesan gagal: $!";

msgrcv($id, $rcvd_buf saya, 60, 0, 0)
|| die "msgrcv gagal: $!";

my($type_rcvd, $rcvd) = unpack("l! a*", $rcvd_buf);

if ($rcvd eq $terkirim) {
cetak "oke\n";
} Else {
print "tidak baik\n";
}

msgctl($id, IPC_RMID, 0) || die "msgctl gagal: $!\n";

CATATAN


Sebagian besar rutinitas ini diam-diam tapi sopan mengembalikan "undef" ketika mereka gagal, bukan
menyebabkan program Anda mati saat itu juga karena pengecualian yang tidak tertangkap. (Sebenarnya,
beberapa yang baru Stopkontak fungsi konversi lakukan menggaok() pada argumen yang buruk.) Oleh karena itu
penting untuk memeriksa nilai kembalian dari fungsi-fungsi ini. Selalu mulai program soket Anda
cara ini agar sukses optimal, dan jangan lupa tambahkan -T bendera pemeriksa noda ke
"#!" baris untuk server:

#!/usr/bin/Perl -Dua
gunakan ketat;
gunakan tanda tangan;
gunakan Soket;

Gunakan perlipc online menggunakan layanan onworks.net


Server & Workstation Gratis

Unduh aplikasi Windows & Linux

  • 1
    AstrOrzPlayer
    AstrOrzPlayer
    AstrOrz Player adalah pemutar media gratis
    perangkat lunak, sebagian berdasarkan WMP dan VLC. Itu
    pemain dalam gaya minimalis, dengan
    lebih dari sepuluh warna tema, dan bisa juga
    b ...
    Unduh AstrOrzPlayer.dll
  • 2
    movistartv
    movistartv
    Kodi Movistar+ TV adalah ADDON untuk XBMC/
    Kode yang mengizinkan penyalurnya
    decodificador de the service IPTV de
    Movistar terintegrasi ke salah satu dari mereka
    mediacenter ma...
    Unduh movistartv.dll
  • 3
    Kode :: Blok
    Kode :: Blok
    Code::Blocks adalah gratis, sumber terbuka,
    lintas platform C, C++ dan Fortran IDE
    dibangun untuk memenuhi kebutuhan yang paling menuntut
    penggunanya. Ini dirancang untuk menjadi sangat
    ekstensi...
    Unduh Kode::Blok
  • 4
    Di tengah
    Di tengah
    Di tengah atau Antarmuka Minecraft Tingkat Lanjut
    dan Pelacakan Data/Struktur adalah alat untuk
    tampilkan ikhtisar Minecraft
    dunia, tanpa benar-benar menciptakannya. Dia
    bisa ...
    Unduh Di tengah
  • 5
    MSYS2
    MSYS2
    MSYS2 adalah kumpulan alat dan
    perpustakaan menyediakan Anda dengan
    lingkungan yang mudah digunakan untuk membangun,
    menginstal dan menjalankan Windows asli
    perangkat lunak. Ini men...
    Unduh MSYS2.dll
  • 6
    libjpeg-turbo
    libjpeg-turbo
    libjpeg-turbo adalah codec gambar JPEG
    yang menggunakan instruksi SIMD (MMX, SSE2,
    NEON, AltiVec) untuk mempercepat baseline
    Kompresi dan dekompresi JPEG aktif
    x86, x8...
    Unduh libjpeg-turbo.dll
  • Lebih banyak lagi »

Perintah Linux

Ad