Pertanyaan Apakah ada cara untuk beberapa proses untuk berbagi soket mendengarkan?


Dalam pemrograman socket, Anda membuat soket mendengarkan dan kemudian untuk setiap klien yang terhubung, Anda mendapatkan soket aliran normal yang dapat Anda gunakan untuk menangani permintaan klien. OS mengelola antrian koneksi yang masuk di belakang layar.

Dua proses tidak dapat mengikat ke port yang sama secara bersamaan - secara default, juga.

Saya bertanya-tanya apakah ada cara (pada OS terkenal, khususnya Windows) untuk meluncurkan beberapa contoh proses, sehingga mereka semua mengikat ke soket, dan sehingga mereka secara efektif berbagi antrian. Setiap contoh proses kemudian bisa menjadi ulir tunggal; itu hanya akan memblokir ketika menerima koneksi baru. Ketika klien terhubung, salah satu contoh proses tidak aktif akan menerima klien itu.

Ini akan memungkinkan setiap proses untuk memiliki implementasi single-threaded yang sangat sederhana, berbagi apa-apa kecuali melalui memori bersama eksplisit, dan pengguna akan dapat menyesuaikan bandwidth pemrosesan dengan memulai lebih banyak instance.

Apakah fitur semacam itu ada?

Edit: Bagi mereka yang bertanya, "Mengapa tidak menggunakan utas?" Tentunya untaian adalah opsi. Tetapi dengan beberapa utas dalam satu proses, semua objek dapat dibagikan dan perhatian besar harus diambil untuk memastikan bahwa objek tidak dibagikan, atau hanya dapat dilihat satu utas pada satu waktu, atau benar-benar tidak berubah, dan bahasa yang paling populer dan runtime tidak memiliki dukungan bawaan untuk mengelola kompleksitas ini.

Dengan memulai beberapa proses pekerja yang identik, Anda akan mendapatkan sistem konkuren di mana default tidak berbagi, membuatnya lebih mudah untuk membangun implementasi yang benar dan skalabel.


76
2018-03-22 11:56


asal


Jawaban:


Anda dapat berbagi soket antara dua (atau lebih) proses di Linux dan bahkan Windows.

Di Linux (Atau OS tipe POSIX), menggunakan fork() akan menyebabkan anak bercabang memiliki salinan semua deskriptor file orang tua. Setiap yang tidak ditutup akan terus dibagikan, dan (misalnya dengan soket mendengarkan TCP) dapat digunakan untuk accept() soket baru untuk klien. Ini adalah berapa banyak server, termasuk Apache dalam banyak kasus, berfungsi.

Pada Windows, hal yang sama pada dasarnya benar, kecuali tidak ada fork() sistem panggilan sehingga proses orang tua perlu digunakan CreateProcess atau sesuatu untuk membuat proses anak (yang tentu saja dapat menggunakan eksekusi yang sama) dan perlu meneruskannya pegangan yang dapat diwariskan.

Membuat soket mendengarkan pegangan yang diwariskan bukanlah kegiatan yang sepenuhnya sepele tetapi juga tidak terlalu rumit. DuplicateHandle() perlu digunakan untuk membuat duplikat menangani (masih dalam proses induk namun), yang akan memiliki bendera yang diwariskan di atasnya. Maka Anda dapat memberikan pegangan itu di STARTUPINFO struktur ke proses anak di CreateProcess sebagai STDIN, OUT atau ERR menangani (dengan asumsi Anda tidak ingin menggunakannya untuk hal lain).

EDIT:

Membaca perpustakaan MDSN, tampaknya itu WSADuplicateSocket adalah mekanisme yang lebih kuat atau benar dalam melakukan ini; ini masih tidak sepele karena proses orang tua / anak perlu bekerja yang perlu digandakan oleh beberapa mekanisme IPC (meskipun ini bisa sesederhana file dalam sistem file)

KLARIFIKASI:

Sebagai jawaban atas pertanyaan asli OP, tidak, banyak proses tidak bisa bind(); hanya proses induk asli yang akan dipanggil bind(), listen() dll, proses anak hanya akan memproses permintaan oleh accept(), send(), recv() dll.


81
2018-03-22 12:02



Sebagian besar lainnya telah memberikan alasan teknis mengapa ini berhasil. Berikut ini beberapa kode python yang dapat Anda jalankan untuk mendemonstrasikan ini untuk Anda sendiri:

import socket
import os

def main():
    serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serversocket.bind(("127.0.0.1", 8888))
    serversocket.listen(0)

    # Child Process
    if os.fork() == 0:
        accept_conn("child", serversocket)

    accept_conn("parent", serversocket)

def accept_conn(message, s):
    while True:
        c, addr = s.accept()
        print 'Got connection from in %s' % message
        c.send('Thank you for your connecting to %s\n' % message)
        c.close()

if __name__ == "__main__":
    main()

Perhatikan bahwa memang ada dua proses yang sedang didengarkan:

$ lsof -i :8888
COMMAND   PID    USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
Python  26972 avaitla    3u  IPv4 0xc26aa26de5a8fc6f      0t0  TCP localhost:ddi-tcp-1 (LISTEN)
Python  26973 avaitla    3u  IPv4 0xc26aa26de5a8fc6f      0t0  TCP localhost:ddi-tcp-1 (LISTEN)

Berikut hasil dari menjalankan telnet dan program:

$ telnet 127.0.0.1 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Thank you for your connecting to parent
Connection closed by foreign host.
$ telnet 127.0.0.1 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Thank you for your connecting to child
Connection closed by foreign host.
$ telnet 127.0.0.1 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Thank you for your connecting to parent
Connection closed by foreign host.

$ python prefork.py 
Got connection from in parent
Got connection from in child
Got connection from in parent

26
2017-10-09 10:27



Sepertinya pertanyaan ini telah dijawab sepenuhnya oleh MarkR dan zackthehack tetapi saya ingin menambahkan bahwa Nginx adalah contoh dari model warisan soket mendengarkan.

Berikut ini deskripsi yang bagus:

         Implementation of HTTP Auth Server Round-Robin and
                Memory Caching for NGINX Email Proxy

                            June 6, 2007
             Md. Mansoor Peerbhoy <mansoor@zimbra.com>

...

Alur proses pekerja NGINX

Setelah proses NGINX utama membaca file konfigurasi dan garpu   ke dalam jumlah proses pekerja yang dikonfigurasi, setiap proses pekerja   masuk ke dalam lingkaran di mana ia menunggu untuk setiap kejadian pada masing-masing   set soket.

Setiap proses pekerja dimulai dengan hanya soket yang mendengarkan,   karena belum ada koneksi yang tersedia. Karena itu, acara tersebut   deskriptor yang ditetapkan untuk setiap proses pekerja dimulai hanya dengan   mendengarkan soket.

(CATATAN) NGINX dapat dikonfigurasi untuk menggunakan salah satu dari beberapa peristiwa   mekanisme polling:   aio / devpoll / epoll / eventpoll / kqueue / poll / rtsig / select

Ketika koneksi tiba di salah satu soket mendengarkan   (POP3 / IMAP / SMTP), setiap proses pekerja muncul dari jajak pendapatnya,   karena setiap proses pekerja NGINX mewarisi soket mendengarkan. Kemudian,   setiap proses pekerja NGINX akan mencoba untuk memperoleh mutex global.   Salah satu proses pekerja akan memperoleh kunci, sedangkan   yang lain akan kembali ke putaran polling acara masing-masing.

Sementara itu, proses pekerja yang memperoleh mutex global akan   memeriksa peristiwa yang dipicu, dan akan membuat antrian kerja yang diperlukan   permintaan untuk setiap peristiwa yang dipicu. Sebuah acara sesuai dengan   deskriptor soket tunggal dari set deskriptor bahwa   pekerja mengawasi acara dari.

Jika peristiwa yang dipicu berhubungan dengan koneksi masuk baru,   NGINX menerima koneksi dari soket mendengarkan. Lalu itu   mengaitkan struktur data konteks dengan deskriptor file. Ini   konteks memegang informasi tentang koneksi (apakah   POP3 / IMAP / SMTP, apakah pengguna belum diautentikasi, dll.). Kemudian,   socket yang baru dibangun ini ditambahkan ke set deskriptor event   untuk proses pekerja itu.

Pekerja sekarang melepaskan mutex (yang berarti setiap kejadian   yang tiba di pekerja lain dapat diproses), dan mulai diproses   setiap permintaan yang sebelumnya diantrekan. Setiap permintaan sesuai dengan suatu   acara yang ditandai. Dari setiap deskriptor soket itu   memberi isyarat, proses pekerja mengambil konteks yang sesuai   struktur data yang sebelumnya terkait dengan deskriptor itu, dan   kemudian memanggil fungsi panggilan balik yang sesuai yang berfungsi   tindakan berdasarkan keadaan koneksi itu. Misalnya, dalam kasus   koneksi IMAP yang baru didirikan, hal pertama yang NGINX   akan lakukan adalah menulis pesan selamat datang IMAP standar ke
socket terhubung (* OK IMAP4 siap).

Dengan dan oleh, setiap proses pekerja selesai memproses antrian kerja   entri untuk setiap acara yang luar biasa, dan kembali ke acaranya   putaran polling. Setelah koneksi dibuat dengan klien, maka   Peristiwa biasanya lebih cepat, karena setiap kali soket tersambung   siap untuk membaca, acara baca dipicu, dan   tindakan yang sesuai harus diambil.


13
2017-09-02 09:33



Saya ingin menambahkan bahwa soket dapat dibagikan di Unix / Linux melalui soket AF__UNIX (soket antar-proses). Apa yang tampaknya terjadi adalah deskriptor soket baru dibuat yang agak dari alias ke yang asli. Keterangan soket baru ini dikirim melalui soket AFUNIX ke proses lainnya. Ini sangat berguna dalam kasus di mana proses tidak dapat fork () untuk membagikan deskriptor file. Misalnya, ketika menggunakan pustaka yang mencegah hal ini karena masalah penguliran. Anda harus membuat soket dan penggunaan domain Unix libancillary untuk mengirim deskriptor.

Lihat:

Untuk membuat AF_UNIX Sockets:

Misalnya kode:


12
2017-07-16 18:29



Tidak yakin seberapa relevan hal ini dengan pertanyaan aslinya, tetapi di kernel Linux 3.9 ada tambalan menambahkan fitur TCP / UDP: TCP dan dukungan UDP untuk opsi soket SO_REUSEPORT; Opsi soket baru memungkinkan beberapa soket pada host yang sama untuk mengikat ke port yang sama, dan dimaksudkan untuk meningkatkan kinerja aplikasi server jaringan multithread yang berjalan di atas sistem multicore. informasi lebih lanjut dapat ditemukan di tautan LWN LWN SO_REUSEPORT di Linux Kernel 3.9 sebagaimana disebutkan dalam tautan referensi:

opsi SO_REUSEPORT tidak standar, tetapi tersedia dalam bentuk serupa pada sejumlah sistem UNIX lainnya (terutama, BSD, tempat ide berasal). Tampaknya menawarkan alternatif yang berguna untuk menekan kinerja maksimum dari aplikasi jaringan yang berjalan pada sistem multicore, tanpa harus menggunakan pola garpu.


9
2018-05-04 03:25



Memiliki tugas tunggal yang tugas utamanya adalah mendengarkan koneksi masuk. Ketika koneksi diterima, ia menerima koneksi - ini menciptakan deskriptor soket terpisah. Soket yang diterima dilewatkan ke salah satu tugas pekerja Anda yang tersedia, dan tugas utama kembali ke mendengarkan.

s = socket();
bind(s);
listen(s);
while (1) {
  s2 = accept(s);
  send_to_worker(s2);
}

3
2018-03-22 13:44



Dimulai dengan Linux 3.9, Anda dapat mengatur SO_REUSEPORT pada soket dan kemudian memiliki beberapa proses yang tidak terkait berbagi soket itu. Itu lebih sederhana daripada skema prefork, tidak ada lagi masalah sinyal, kebocoran fd untuk proses anak, dll.

Linux 3.9 memperkenalkan cara baru untuk menulis server soket

Opsi soket SO_REUSEPORT


3
2018-02-20 18:20



Pendekatan lain (yang menghindari banyak detail rumit) di Windows jika Anda menggunakan HTTP, adalah untuk digunakan HTTP.SYS. Ini memungkinkan beberapa proses untuk mendengarkan URL yang berbeda pada port yang sama. Pada Server 2003/2008 / Vista / 7 ini adalah cara kerja IIS, sehingga Anda dapat berbagi port dengannya. (Pada XP SP2 HTTP.SYS didukung, tetapi IIS5.1 tidak menggunakannya.)

API tingkat tinggi lainnya (termasuk WCF) menggunakan HTTP.SYS.


2
2018-03-22 12:19



Di bawah Windows (dan Linux) adalah mungkin untuk satu proses untuk membuka soket dan kemudian meneruskan soket itu ke proses lain sehingga proses kedua itu juga dapat menggunakan soket itu (dan meneruskannya pada gilirannya, jika ingin melakukannya) .

Panggilan fungsi penting adalah WSADuplicateSocket ().

Ini mengisi struktur dengan informasi tentang soket yang ada. Struktur ini kemudian, melalui mekanisme IPC pilihan Anda, diteruskan ke proses lain yang ada (perhatikan saya katakan ada - ketika Anda memanggil WSADuplicateSocket (), Anda harus menunjukkan proses target yang akan menerima informasi yang dipancarkan).

Proses penerimaan kemudian dapat memanggil WSASocket (), meneruskan struktur informasi ini, dan menerima pegangan ke soket yang mendasari.

Kedua proses sekarang memegang pegangan ke soket yang mendasari yang sama.


2
2018-03-28 18:15



Kedengarannya seperti apa yang Anda inginkan adalah satu proses mendengarkan untuk klien baru dan kemudian serahkan koneksi setelah Anda mendapatkan koneksi. Untuk melakukan itu di thread mudah dan dalam. Net Anda bahkan memiliki metode BeginAccept dll untuk mengurus banyak pipa untuk Anda. Untuk menyerahkan koneksi melintasi batas-batas proses akan rumit dan tidak akan memiliki keunggulan kinerja.

Atau Anda dapat memiliki beberapa proses terikat dan mendengarkan pada soket yang sama.

TcpListener tcpServer = new TcpListener(IPAddress.Loopback, 10090);
tcpServer.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
tcpServer.Start();

while (true)
{
    TcpClient client = tcpServer.AcceptTcpClient();
    Console.WriteLine("TCP client accepted from " + client.Client.RemoteEndPoint + ".");
}

Jika Anda menjalankan dua proses, masing-masing mengeksekusi kode di atas akan berfungsi dan proses pertama tampaknya mendapatkan semua koneksi. Jika proses pertama terbunuh, yang kedua kemudian mendapat koneksi. Dengan berbagi soket seperti itu saya tidak tahu pasti bagaimana Windows memutuskan proses mana yang mendapat koneksi baru meskipun tes cepat menunjukkan proses tertua untuk mendapatkan mereka terlebih dahulu. Seperti apakah itu dibagikan jika proses pertama sibuk atau hal seperti itu saya tidak tahu.


1
2018-03-22 12:18