Soket digunakan hampir di mana-mana, tetapi merupakan salah satu teknologi yang paling sering disalahpahami. Ini adalah ikhtisar soket 10.000 kaki. Ini sebenarnya bukan tutorial - Anda masih harus bekerja untuk menjalankan semuanya. Itu tidak mencakup poin-poin bagus (dan ada banyak dari mereka), tapi saya harap ini akan memberi Anda latar belakang yang cukup untuk mulai menggunakannya dengan baik.
Soket
Saya hanya akan berbicara tentang INET (mis. e. IPv4), tetapi mereka menyumbang setidaknya 99% dari soket yang digunakan. Dan saya hanya akan berbicara tentang STREAM (mis. e. TCP) soket - kecuali Anda benar-benar tahu apa yang Anda lakukan (dalam hal ini HOWTO ini bukan untuk Anda. ), Anda akan mendapatkan perilaku dan kinerja yang lebih baik dari soket STREAM daripada yang lainnya. Saya akan mencoba untuk menjernihkan misteri tentang apa itu soket, serta beberapa petunjuk tentang cara bekerja dengan soket pemblokiran dan non-pemblokiran. Tapi saya akan mulai dengan berbicara tentang memblokir soket. Anda harus mengetahui cara kerjanya sebelum berurusan dengan soket non-pemblokiran
Bagian dari masalah dalam memahami hal-hal ini adalah bahwa "soket" dapat berarti sejumlah hal yang agak berbeda, bergantung pada konteksnya. Jadi pertama-tama, mari kita bedakan antara soket "klien" - titik akhir percakapan, dan soket "server", yang lebih mirip operator switchboard. Aplikasi klien (browser Anda, misalnya) menggunakan soket "klien" secara eksklusif;
Sejarah
Dari berbagai bentuk IPC, soket adalah yang paling populer. Pada platform apa pun, kemungkinan ada bentuk IPC lain yang lebih cepat, tetapi untuk komunikasi lintas platform, soket adalah satu-satunya permainan di kota.
Mereka ditemukan di Berkeley sebagai bagian dari rasa BSD Unix. Mereka menyebar seperti api dengan internet. Dengan alasan yang bagus - kombinasi soket dengan INET membuat berbicara dengan mesin sewenang-wenang di seluruh dunia menjadi sangat mudah (setidaknya dibandingkan dengan skema lain)
Membuat Soket
Secara kasar, ketika Anda mengklik tautan yang membawa Anda ke halaman ini, browser Anda melakukan hal seperti berikut
# create an INET, STREAMing socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # now connect to the web server on port 80 - the normal http port s.connect(("www.python.org", 80))
Ketika connect selesai, soket s dapat digunakan untuk mengirimkan permintaan teks halaman. Soket yang sama akan membaca balasannya, lalu dihancurkan. Benar, hancur. Soket klien biasanya hanya digunakan untuk satu pertukaran (atau sekumpulan kecil pertukaran berurutan)
Apa yang terjadi di server web sedikit lebih rumit. Pertama, server web membuat "soket server"
# create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) _
Beberapa hal yang perlu diperhatikan. kami menggunakan socket.gethostname() sehingga soket akan terlihat oleh dunia luar. Jika kami telah menggunakan s.bind(('localhost', 80)) atau s.bind(('127.0.0.1', 80)) kami masih memiliki soket "server", tetapi yang hanya terlihat di dalam mesin yang sama. # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) 0 menentukan bahwa soket dapat dijangkau oleh alamat apa pun yang dimiliki mesin
Hal kedua yang perlu diperhatikan. port nomor rendah biasanya disediakan untuk layanan "terkenal" (HTTP, SNMP dll). Jika Anda bermain-main, gunakan angka tinggi yang bagus (4 digit)
Akhirnya, argumen untuk # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) _1 memberi tahu perpustakaan soket bahwa kami ingin mengantri sebanyak 5 permintaan koneksi (maks normal) sebelum menolak koneksi luar. Jika sisa kode ditulis dengan benar, itu pasti banyak
Sekarang kita memiliki soket "server", mendengarkan pada port 80, kita dapat memasuki mainloop dari server web
while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() _
Sebenarnya ada 3 cara umum di mana loop ini dapat bekerja - mengirim utas untuk menangani # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) 2, membuat proses baru untuk menangani # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) 2, atau merestrukturisasi aplikasi ini untuk menggunakan soket non-pemblokiran, dan multipleks antara soket "server" kami dan # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) 2 aktif . Lebih lanjut tentang itu nanti. Hal penting yang harus dipahami sekarang adalah ini. ini semua yang dilakukan soket "server". Itu tidak mengirim data apa pun. Itu tidak menerima data apa pun. Itu hanya menghasilkan soket "klien". Setiap # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) _2 dibuat sebagai respons terhadap soket "klien" lain yang melakukan # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) 7 ke host dan port tempat kita terikat. Segera setelah kami membuat # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) 2 itu, kami kembali mendengarkan untuk koneksi lainnya. Kedua "klien" bebas untuk mengobrol - mereka menggunakan beberapa port yang dialokasikan secara dinamis yang akan didaur ulang saat percakapan berakhir
IPC
Jika Anda membutuhkan IPC cepat antara dua proses pada satu mesin, Anda harus melihat ke dalam pipa atau memori bersama. Jika Anda memutuskan untuk menggunakan soket AF_INET, ikat soket "server" ke # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) 9. Pada sebagian besar platform, ini akan mengambil jalan pintas di sekitar beberapa lapisan kode jaringan dan menjadi sedikit lebih cepat
Lihat juga
IPC lintas platform terintegrasi ke dalam API tingkat yang lebih tinggi
Menggunakan Soket
Hal pertama yang perlu diperhatikan, adalah bahwa soket "klien" browser web dan soket "klien" server web adalah binatang buas yang identik. Artinya, ini adalah percakapan "peer to peer". Atau dengan kata lain, sebagai desainer, Anda harus memutuskan apa aturan etiket untuk percakapan. Biasanya, soket connect_ memulai percakapan, dengan mengirimkan permintaan, atau mungkin tanda masuk. Tapi itu keputusan desain - itu bukan aturan soket
Sekarang ada dua set kata kerja yang digunakan untuk komunikasi. Anda dapat menggunakan while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 2 dan while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 3, atau Anda dapat mengubah soket klien Anda menjadi binatang seperti file dan menggunakan while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 4 dan while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 5. Yang terakhir adalah cara Java menampilkan soketnya. Saya tidak akan membicarakannya di sini, kecuali untuk memperingatkan Anda bahwa Anda perlu menggunakan while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 6 pada soket. Ini adalah "file" buffer, dan kesalahan umum adalah while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 5 sesuatu, lalu while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 4 untuk balasan. Tanpa while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 6 di sana, Anda mungkin menunggu selamanya untuk balasan, karena permintaan mungkin masih ada di buffer output Anda
Sekarang kita sampai pada batu sandungan utama soket - while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 2 dan while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 3 beroperasi pada buffer jaringan. Mereka tidak perlu menangani semua byte yang Anda berikan (atau harapkan dari mereka), karena fokus utama mereka adalah menangani buffer jaringan. Secara umum, mereka kembali ketika buffer jaringan terkait telah diisi (while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 2) atau dikosongkan (while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 3). Mereka kemudian memberi tahu Anda berapa banyak byte yang mereka tangani. Merupakan tanggung jawab Anda untuk menelepon mereka lagi sampai pesan Anda benar-benar ditangani
Ketika while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() _3 mengembalikan 0 byte, itu berarti pihak lain telah menutup (atau sedang dalam proses penutupan) koneksi. Anda tidak akan menerima data lagi pada sambungan ini. Pernah. Anda mungkin berhasil mengirim data;
Protokol seperti HTTP menggunakan soket hanya untuk satu transfer. Klien mengirim permintaan, lalu membaca balasan. Itu dia. Soket dibuang. Ini berarti klien dapat mendeteksi akhir balasan dengan menerima 0 byte
Tetapi jika Anda berencana menggunakan kembali soket Anda untuk transfer lebih lanjut, Anda perlu menyadari bahwa tidak ada EOT pada soket. saya ulangi. jika soket while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 2 atau while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 3 kembali setelah menangani 0 byte, koneksi telah terputus. Jika koneksi belum terputus, Anda dapat menunggu di while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 3 selamanya, karena soket tidak akan memberi tahu Anda bahwa tidak ada lagi yang perlu dibaca (untuk saat ini). Sekarang jika Anda memikirkannya sedikit, Anda akan menyadari kebenaran mendasar tentang soket. pesan harus memiliki panjang tetap (yuck), atau dibatasi (mengangkat bahu), atau menunjukkan berapa panjangnya (jauh lebih baik), atau diakhiri dengan mematikan koneksi. Pilihan sepenuhnya ada di tangan Anda, (tetapi beberapa cara lebih benar dari yang lain)
Dengan asumsi Anda tidak ingin mengakhiri koneksi, solusi paling sederhana adalah pesan dengan panjang tetap
class MySocket: """demonstration class only - coded for clarity, not efficiency """ def __init__(self, sock=None): if sock is None: self.sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM) else: self.sock = sock def connect(self, host, port): self.sock.connect((host, port)) def mysend(self, msg): totalsent = 0 while totalsent < MSGLEN: sent = self.sock.send(msg[totalsent:]) if sent == 0: raise RuntimeError("socket connection broken") totalsent = totalsent + sent def myreceive(self): chunks = [] bytes_recd = 0 while bytes_recd < MSGLEN: chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048)) if chunk == b'': raise RuntimeError("socket connection broken") chunks.append(chunk) bytes_recd = bytes_recd + len(chunk) return b''.join(chunks)
Kode pengiriman di sini dapat digunakan untuk hampir semua skema perpesanan - dengan Python Anda mengirim string, dan Anda dapat menggunakan class MySocket: """demonstration class only - coded for clarity, not efficiency """ def __init__(self, sock=None): if sock is None: self.sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM) else: self.sock = sock def connect(self, host, port): self.sock.connect((host, port)) def mysend(self, msg): totalsent = 0 while totalsent < MSGLEN: sent = self.sock.send(msg[totalsent:]) if sent == 0: raise RuntimeError("socket connection broken") totalsent = totalsent + sent def myreceive(self): chunks = [] bytes_recd = 0 while bytes_recd < MSGLEN: chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048)) if chunk == b'': raise RuntimeError("socket connection broken") chunks.append(chunk) bytes_recd = bytes_recd + len(chunk) return b''.join(chunks) 8 untuk menentukan panjangnya (meskipun telah menyematkan class MySocket: """demonstration class only - coded for clarity, not efficiency """ def __init__(self, sock=None): if sock is None: self.sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM) else: self.sock = sock def connect(self, host, port): self.sock.connect((host, port)) def mysend(self, msg): totalsent = 0 while totalsent < MSGLEN: sent = self.sock.send(msg[totalsent:]) if sent == 0: raise RuntimeError("socket connection broken") totalsent = totalsent + sent def myreceive(self): chunks = [] bytes_recd = 0 while bytes_recd < MSGLEN: chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048)) if chunk == b'': raise RuntimeError("socket connection broken") chunks.append(chunk) bytes_recd = bytes_recd + len(chunk) return b''.join(chunks) 9 karakter). Sebagian besar kode penerima yang menjadi lebih kompleks. (Dan di C, itu tidak jauh lebih buruk, kecuali Anda tidak dapat menggunakan ready_to_read, ready_to_write, in_error = \ select.select( potential_readers, potential_writers, potential_errs, timeout) 0 jika pesan telah menyematkan class MySocket: """demonstration class only - coded for clarity, not efficiency """ def __init__(self, sock=None): if sock is None: self.sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM) else: self.sock = sock def connect(self, host, port): self.sock.connect((host, port)) def mysend(self, msg): totalsent = 0 while totalsent < MSGLEN: sent = self.sock.send(msg[totalsent:]) if sent == 0: raise RuntimeError("socket connection broken") totalsent = totalsent + sent def myreceive(self): chunks = [] bytes_recd = 0 while bytes_recd < MSGLEN: chunk = self.sock.recv(min(MSGLEN - bytes_recd, 2048)) if chunk == b'': raise RuntimeError("socket connection broken") chunks.append(chunk) bytes_recd = bytes_recd + len(chunk) return b''.join(chunks) 9s. )
Penyempurnaan yang paling mudah adalah menjadikan karakter pertama pesan sebagai indikator jenis pesan, dan jenisnya menentukan panjangnya. Sekarang Anda memiliki dua while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 3 - yang pertama untuk mendapatkan (setidaknya) karakter pertama sehingga Anda dapat mencari panjangnya, dan yang kedua dalam satu lingkaran untuk mendapatkan sisanya. Jika Anda memutuskan untuk menggunakan rute yang dibatasi, Anda akan menerima beberapa ukuran potongan acak, (4096 atau 8192 sering cocok untuk ukuran buffer jaringan), dan memindai apa yang telah Anda terima untuk pembatas
Satu komplikasi yang harus diperhatikan. jika protokol percakapan Anda memungkinkan beberapa pesan untuk dikirim kembali ke belakang (tanpa semacam balasan), dan Anda memberikan while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 3 ukuran potongan yang sewenang-wenang, Anda mungkin akan membaca awal dari pesan berikut. Anda harus mengesampingkannya dan mempertahankannya, sampai dibutuhkan
Mengawali pesan dengan panjangnya (misalnya, 5 karakter numerik) menjadi lebih rumit, karena (percaya atau tidak), Anda mungkin tidak mendapatkan semua 5 karakter dalam satu while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 3. Dalam bermain-main, Anda akan lolos begitu saja; . Menjijikan. Ini juga saat Anda akan menemukan bahwa while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 2 tidak selalu berhasil menyingkirkan semuanya sekaligus. Dan meskipun telah membaca ini, pada akhirnya Anda akan digigit olehnya
Demi kepentingan ruang, membangun karakter Anda, (dan mempertahankan posisi kompetitif saya), penyempurnaan ini dibiarkan sebagai latihan bagi pembaca. Mari kita beralih ke pembersihan
Data Biner
Sangat mungkin untuk mengirim data biner melalui soket. Masalah utamanya adalah tidak semua mesin menggunakan format yang sama untuk data biner. Misalnya, adalah big-endian, dengan byte paling signifikan terlebih dahulu, jadi integer 16 bit dengan nilai ready_to_read, ready_to_write, in_error = \ select.select( potential_readers, potential_writers, potential_errs, timeout) 7 akan menjadi dua byte hex ready_to_read, ready_to_write, in_error = \ select.select( potential_readers, potential_writers, potential_errs, timeout) 8. Namun, sebagian besar prosesor umum (x86/AMD64, ARM, RISC-V), adalah little-endian, dengan byte paling signifikan terlebih dahulu - ready_to_read, ready_to_write, in_error = \ select.select( potential_readers, potential_writers, potential_errs, timeout) 7 yang sama akan menjadi connect0
Pustaka soket memiliki panggilan untuk mengonversi bilangan bulat 16 dan 32 bit - connect1 di mana "n" berarti jaringan dan "h" berarti host, "s" berarti pendek dan "l" berarti panjang. Di mana urutan jaringan adalah urutan host, ini tidak melakukan apa-apa, tetapi jika mesin dibalik byte, ini menukar byte dengan tepat
Pada hari-hari mesin 64-bit ini, representasi data biner ASCII seringkali lebih kecil daripada representasi biner. Itu karena jumlah waktu yang mengejutkan, sebagian besar bilangan bulat memiliki nilai 0, atau mungkin 1. String connect2 akan menjadi dua byte, sedangkan integer 64-bit penuh akan menjadi 8. Tentu saja, ini tidak cocok dengan pesan dengan panjang tetap. Keputusan, keputusan
Memutuskan
Sebenarnya, Anda seharusnya menggunakan connect3 pada soket sebelum Anda connect4 itu. connect_3 adalah penasehat untuk soket di ujung lainnya. Bergantung pada argumen yang Anda berikan, itu bisa berarti "Saya tidak akan mengirim lagi, tetapi saya akan tetap mendengarkan", atau "Saya tidak mendengarkan, selamat tinggal. ”. Namun, sebagian besar pustaka soket begitu terbiasa dengan pemrogram yang lalai menggunakan etiket ini sehingga biasanya connect4 sama dengan connect7. Jadi dalam kebanyakan situasi, connect3 eksplisit tidak diperlukan
Salah satu cara untuk menggunakan connect_3 secara efektif adalah dalam pertukaran mirip HTTP. Klien mengirimkan permintaan dan kemudian melakukan s0. Ini memberi tahu server “Klien ini telah selesai mengirim, tetapi masih dapat menerima. ” Server dapat mendeteksi "EOF" dengan menerima 0 byte. Dapat diasumsikan memiliki permintaan lengkap. Server mengirim balasan. Jika while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() _2 selesai dengan sukses maka, memang, klien masih menerima
Python mengambil shutdown otomatis selangkah lebih maju, dan mengatakan bahwa ketika soket dikumpulkan sampah, itu akan secara otomatis melakukan connect4 jika diperlukan. Tetapi mengandalkan ini adalah kebiasaan yang sangat buruk. Jika soket Anda hilang begitu saja tanpa melakukan connect4, soket di ujung lainnya mungkin menggantung tanpa batas waktu, mengira Anda hanya lambat. Harap connect_4 soket Anda setelah selesai
Ketika Soket Mati
Mungkin hal terburuk tentang menggunakan soket pemblokiran adalah apa yang terjadi ketika pihak lain turun dengan keras (tanpa melakukan connect4). Soket Anda kemungkinan akan hang. TCP adalah protokol yang andal, dan akan menunggu sangat lama sebelum menyerah pada koneksi. Jika Anda menggunakan utas, seluruh utas pada dasarnya sudah mati. Tidak banyak yang dapat Anda lakukan tentang itu. Selama Anda tidak melakukan sesuatu yang bodoh, seperti menahan kunci saat membaca pemblokiran, utas tidak terlalu menghabiskan banyak sumber daya. Jangan mencoba mematikan utas - salah satu alasan utas lebih efisien daripada proses adalah karena utas menghindari overhead yang terkait dengan daur ulang otomatis sumber daya. Dengan kata lain, jika Anda berhasil mematikan utas, seluruh proses Anda kemungkinan besar akan kacau
Soket Tanpa Pemblokiran
Jika Anda sudah memahami sebelumnya, Anda sudah tahu sebagian besar dari apa yang perlu Anda ketahui tentang mekanisme penggunaan soket. Anda masih akan menggunakan panggilan yang sama, dengan cara yang hampir sama. Hanya saja, jika Anda melakukannya dengan benar, aplikasi Anda akan hampir habis-habisan
Di Python, Anda menggunakan s_6 untuk membuatnya tidak memblokir. Di C, ini lebih kompleks, (untuk satu hal, Anda harus memilih antara rasa BSD s7 dan rasa POSIX yang hampir tidak dapat dibedakan s8, yang sama sekali berbeda dari s9), tetapi idenya sama persis. Anda melakukan ini setelah membuat soket, tetapi sebelum menggunakannya. (Sebenarnya, jika Anda gila, Anda bisa bolak-balik. )
Perbedaan mekanis utama adalah bahwa while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 2, while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 3, connect dan socket.gethostname()3 dapat kembali tanpa melakukan apa pun. Anda memiliki (tentu saja) sejumlah pilihan. Anda dapat memeriksa kode pengembalian dan kode kesalahan dan umumnya membuat diri Anda gila. Jika Anda tidak mempercayai saya, cobalah kapan-kapan. Aplikasi Anda akan tumbuh besar, bermasalah, dan menyedot CPU. Jadi mari kita lewati solusi mati otak dan lakukan dengan benar
Gunakan # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) _5
Di C, pengkodean # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) _5 cukup rumit. Di Python, ini sangat mudah, tetapi cukup mirip dengan versi C sehingga jika Anda memahami # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) 5 dengan Python, Anda akan mengalami sedikit masalah dengannya di C
ready_to_read, ready_to_write, in_error = \ select.select( potential_readers, potential_writers, potential_errs, timeout)
Anda melewati # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) _5 tiga daftar. yang pertama berisi semua soket yang mungkin ingin Anda coba baca; . Anda harus mencatat bahwa soket dapat masuk ke lebih dari satu daftar. Panggilan # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) 5 memblokir, tetapi Anda dapat memberikan waktu tunggu. Ini umumnya adalah hal yang masuk akal untuk dilakukan - berikan waktu tunggu yang lama (katakanlah sebentar) kecuali Anda memiliki alasan yang kuat untuk melakukan sebaliknya
Sebagai imbalannya, Anda akan mendapatkan tiga daftar. Mereka berisi soket yang sebenarnya dapat dibaca, dapat ditulis, dan dalam kesalahan. Masing-masing daftar ini adalah subset (kemungkinan kosong) dari daftar terkait yang Anda berikan
Jika soket ada dalam daftar keluaran yang dapat dibaca, Anda dapat sedekat mungkin dengan yang pernah kami dapatkan dalam bisnis ini bahwa while True: # accept connections from outside (clientsocket, address) = serversocket.accept() # now do something with the clientsocket # in this case, we'll pretend this is a threaded server ct = client_thread(clientsocket) ct.run() 3 pada soket itu akan mengembalikan sesuatu. Ide yang sama untuk daftar yang dapat ditulis. Anda akan dapat mengirim sesuatu. Mungkin tidak semua yang Anda inginkan, tetapi ada sesuatu yang lebih baik daripada tidak sama sekali. (Sebenarnya, setiap soket yang cukup sehat akan kembali sebagai dapat ditulisi - itu hanya berarti tersedia ruang buffer jaringan keluar. )
Jika Anda memiliki soket "server", masukkan ke dalam daftar potential_readers. Jika muncul dalam daftar yang dapat dibaca, socket.gethostname()3 Anda (hampir pasti) akan berfungsi. Jika Anda telah membuat soket baru untuk connect ke orang lain, masukkan ke dalam daftar penulis_potensial. Jika itu muncul dalam daftar yang dapat ditulisi, Anda memiliki peluang yang layak untuk terhubung
Sebenarnya, # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) _5 dapat berguna bahkan dengan soket pemblokiran. Ini adalah salah satu cara untuk menentukan apakah Anda akan memblokir - soket kembali dapat dibaca ketika ada sesuatu di buffer. Namun, ini masih tidak membantu dengan masalah menentukan apakah ujung yang lain sudah selesai, atau hanya sibuk dengan hal lain
Peringatan portabilitas. Di Unix, # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) 5 berfungsi baik dengan soket maupun file. Jangan coba ini di Windows. Di Windows, # create an INET, STREAMing socket serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # bind the socket to a public host, and a well-known port serversocket.bind((socket.gethostname(), 80)) # become a server socket serversocket.listen(5) _5 hanya berfungsi dengan soket. Perhatikan juga bahwa di C, banyak opsi soket yang lebih canggih dilakukan secara berbeda di Windows. Faktanya, di Windows saya biasanya menggunakan utas (yang bekerja sangat, sangat baik) dengan soket saya