Stas Afanasyev. Juno. Pipa berdasarkan pada io.Reader / io.Writer. Bagian 2

Dalam laporan ini, kita akan berbicara tentang konsep io.Reader / io.Writer, mengapa mereka diperlukan, bagaimana menerapkannya dengan benar dan perangkap apa yang ada dalam hal ini, serta tentang membangun jaringan pipa berdasarkan implementasi io.Reader / io.Writer standar dan kebiasaan .



Stas Afanasyev. Juno. Pipa berdasarkan pada io.Reader / io.Writer. Bagian 1

Bug "pada kepercayaan"


Nuansa lain: dalam implementasi ini ada "bagul". Bug ini dikonfirmasi oleh pengembang (saya menulis kepada mereka tentang hal itu). Mungkin seseorang tahu apa "bagul" ini? Itu pada slide adalah garis kedua dari belakang:



Ini dikaitkan dengan terlalu banyak kepercayaan pada Pembaca yang dibungkus: jika Pembaca mengembalikan jumlah byte negatif, maka batas yang ingin kita dapatkan dengan jumlah byte yang dikurangi akan dikurangi. Dan dalam beberapa kasus, ini adalah bug yang cukup serius yang tidak dapat Anda segera pahami.

Saya menulis dalam masalah ini: mari kita lakukan sesuatu, mari kita perbaiki! Dan kemudian lapisan masalah terungkap ... Pertama, mereka mengatakan kepada saya bahwa jika Anda menambahkan cek ini sekarang di sini, Anda harus menambahkan cek ini di mana-mana, dan ada selusin tempat ini. Jika kita ingin mengalihkan ini ke sisi klien, maka kita perlu menentukan sejumlah aturan yang digunakan klien untuk memvalidasi data (dan mungkin juga ada lima atau dua dari mereka). Ternyata semua ini perlu disalin.

Saya setuju bahwa ini tidak optimal. Kalau begitu mari kita ke versi yang konsisten! Mengapa kita memiliki satu implementasi perpustakaan standar yang tidak mempercayai apa pun, sementara yang lain benar-benar mempercayai segalanya?

Secara umum, ketika saya sedang menulis pendapat sipil saya, memikirkannya, kami menutup masalah dengan komentar - “Kami tidak akan melakukan apa-apa. Sampai jumpa "! Mereka membuat saya terlihat seperti orang bodoh ... Dengan sopan, tentu saja, Anda tidak dapat menemukan kesalahan.

Secara umum, kami sekarang memiliki masalah. Terdiri dari fakta bahwa tidak jelas siapa yang harus memvalidasi data Pembaca yang dibungkus. Baik klien, atau kami sepenuhnya mempercayai kontrak ... Kami punya satu solusi! Jika ada waktu yang tersisa, saya akan menceritakannya.

Mari kita beralih ke kasus selanjutnya.

Teereader


Kami melihat contoh cara membungkus data Reader. Contoh pipa berikutnya adalah mengambil alih data Reader di Writer. Ada dua situasi.

Situasi pertama. Kita perlu membaca data dari Reader, entah bagaimana menyalinnya ke Writer (secara transparan) dan bekerja dengannya seperti dengan Reader. Ada implementasi dari TeeReader untuk ini. Ini disajikan dalam cuplikan implementasi atas:



Berfungsi seperti tim Tee di Unix. Saya pikir banyak dari Anda telah mendengar tentang ini.
Perhatikan bahwa implementasi ini memeriksa jumlah byte yang dibaca dari Pembaca terbungkus. Lihat kondisi di baris kedua? Karena ketika Anda menulis implementasi seperti itu, secara intuitif jelas: jika ada angka negatif, Anda akan panik. Dan ini adalah tempat lain di mana kami mempercayai Pembaca yang terbungkus! Saya mengingatkan Anda bahwa ini semua adalah perpustakaan standar.

Mari kita beralih ke kasing, misalnya, bagaimana menggunakannya. Apa yang akan kita lakukan pada cuplikan yang lebih rendah? Kami akan mengunduh file robot.txt dari golang.org menggunakan klien http standar.

Seperti yang Anda ketahui, klien http mengembalikan struktur respons kepada kami, di mana bidang Tubuh adalah implementasi dari antarmuka Reader. Ini harus diklarifikasi dengan mengatakan bahwa ini adalah implementasi dari antarmuka ReadCloser. Tetapi ReadCloser hanyalah antarmuka yang dibangun dari Reader dan Closer. Artinya, ini adalah Pembaca, yang secara umum dapat ditutup.

Dalam contoh ini (dalam cuplikan bawah) kami mengumpulkan TeeReader, yang akan membaca data dari Badan ini dan menulisnya ke file. Pembuatan file hari ini, sayangnya, tetap di belakang layar, karena semuanya tidak sesuai. Tetapi, sekali lagi, jika Anda melihat pada dendrogram, tipe file mengimplementasikan antarmuka Writer, yaitu, kita dapat menulisnya. Sudah jelas.

Kami mengumpulkan TeeReader kami dan membacanya menggunakan ReadAll. Semuanya berfungsi seperti yang diharapkan: kita kurangi Tubuh yang dihasilkan, tulis ke file, dan lihat di Assad out.

Cara pemula


Situasi kedua. Kita hanya perlu membaca data dari Reader dan menulisnya ke Writer. Solusinya jelas ...

Ketika saya baru saja mulai bekerja dengan Go, saya memecahkan masalah seperti pada slide:



Saya menemukan buffer, mengisinya dengan data dari Reader, dan mentransfer slice yang terisi ke Writer. Semuanya sederhana.

Dua poin. Pertama, tidak ada jaminan bahwa seluruh Pembaca akan dikurangkan dalam satu panggilan ke metode Baca, karena mungkin ada data yang tersisa (dengan cara yang baik, ini harus dilakukan dalam satu lingkaran).

Poin kedua adalah bahwa jalur ini tidak optimal. Berikut ini adalah kode boilerplate yang ditulis di hadapan kami.

Untuk ini, ada keluarga khusus pembantu di perpustakaan standar - ini adalah Copy, CopyN dan CopyBuffer.

io.Copy. WriterTo dan ReaderFrom


io.Copy pada dasarnya melakukan apa yang ada di slide sebelumnya: ia mengalokasikan buffer default sebesar 32 KB dan menulis data dari Reader ke Writer (tanda tangan dari Salinan ini ditampilkan pada snippet atas):



Selain rutin template ini, ia juga mengandung serangkaian optimasi rumit. Dan sebelum kita berbicara tentang optimasi ini, kita perlu berkenalan dengan dua antarmuka lagi:

  • Penulis Untuk;
  • Baca dari.

Situasi hipotetis. Pembaca Anda bekerja dengan buffer memori. Dia telah memindahkannya, menulis, membaca sesuatu dari sana, yaitu, tempat di bawahnya telah dipindahkan. Anda ingin membaca Pustaka ini di suatu tempat dari luar.

Kita telah melihat bagaimana ini terjadi: buffer dibuat, buffer dilewatkan, yang diteruskan ke metode Baca; Pembaca, yang bekerja dengan memori, membuangnya dari bagian yang direplikasi ... Tapi ini tidak lagi optimal - tempat telah direposisi. Kenapa melakukannya lagi?



Di suatu tempat 5-6 tahun yang lalu (ada tautan ke daftar perubahan) dua antarmuka dibuat: WriteTo dan ReadFrom, yang diimplementasikan secara lokal. Reader mengimplementasikan WriteTo, dan Writer mengimplementasikan ReadFrom. Ternyata Pustaka, yang memiliki irisan dengan data yang sudah direplikasi, dapat menghindari lokasi tambahan dan menerima metode Tulis Ke Penulis dan meneruskan buffer yang tersedia di dalamnya.

Ini adalah bagaimana implementasi bytes.Buffer dan bufio bekerja. Dan jika Anda melihat dendrogram lagi, Anda akan melihat bahwa dua antarmuka ini tidak terlalu populer. Mereka hanya diimplementasikan untuk tipe yang bekerja dengan buffer internal - di mana memori sudah dipindahkan. Ini tidak akan membantu Anda menghindari kefasihan setiap waktu, tetapi hanya jika Anda sudah bekerja dengan bagian yang dipindahkan.

ReaderFrom bekerja dengan cara yang serupa (hanya diterapkan oleh Writer). ReaderFrom membaca seluruh Pembaca, yang datang sebagai argumen untuk itu (sebelum EOF) dan menulis di suatu tempat dalam implementasi internal Writer.

Implementasi CopyBuffer


Cuplikan ini menunjukkan implementasi pembantu copyBuffer. CopyBuffer yang tidak dapat diekspor ini digunakan di bawah kap io.Copy, CopyN, dan CopyBuffer.

Dan di sini ada nuansa kecil yang layak disebut. CopyN baru-baru ini telah dioptimalkan - terlepas dari logika ini. Ini persis optimasi yang saya bicarakan sebelumnya: sebelum membuat buffer tambahan 32 KB, pemeriksaan dibuat - mungkin sumber data mengimplementasikan antarmuka WriterTo, dan buffer tambahan ini tidak diperlukan?

Jika ini tidak terjadi, kami memeriksa: mungkin Writer mengimplementasikan ReaderFrom untuk menghubungkan mereka tanpa perantara ini? Jika ini tidak terjadi, harapan terakhir tetap ada: mungkin kita diberi semacam buffer yang dipindahkan yang bisa kita gunakan?



Begitulah cara kerja io.Copy.

Ada satu masalah, yang merupakan semi-proposal, semi-bug - tidak jelas apa. Sudah menggantung selama satu setengah tahun. Kedengarannya seperti ini: CopyBuffer secara semantik salah.

Sayangnya, tidak ada tanda tangan untuk copyBuffer ini, tetapi sepertinya metode ini tidak bisa diekspor.

Ketika Anda memanggil copyBuffer dengan harapan menghindari lokasi tambahan, berikan byte yang dipindahkan ke sana, logika berikut berfungsi: jika Reader atau Writer memiliki antarmuka WriterTo dan ReaderFrom, maka tidak ada jaminan bahwa Anda akan dapat menghindari lokasi ini. Ini diterima sebagai proposal dan berjanji untuk memikirkannya di Go 2.0. Untuk saat ini, Anda hanya perlu tahu.

Bekerja dengan io.Pipe. PipeReader dan pipeWriter


Kasus lain: Anda harus mendapatkan data dari Writer, entah bagaimana di Reader. Kasus kehidupan yang cantik.

Bayangkan Anda sudah memiliki beberapa data, mereka mengimplementasikan antarmuka Reader - semuanya jelas dengan ini. Anda perlu mengompres data ini, "tweak" dan kirim ke S3. Apa nuansanya? ..
Siapa yang bekerja dengan tipe gzip dalam paket compess tahu bahwa gzip'er itu sendiri hanyalah proxy: ia mengambil data ke dalam dirinya, mengimplementasikan antarmuka Writer, menulis data, sesuatu akan dilakukan pada mereka, dan maka saya harus meletakkannya di suatu tempat. Pada konstruktor, dibutuhkan implementasi dari antarmuka Writer.

Oleh karena itu, di sini kita memerlukan semacam Writer antara, di mana kita akan membuang data yang sudah dikompresi yang diarsipkan pada tahap pertama. Langkah kami selanjutnya adalah mengunggah data ini ke S3. Dan klien AWS standar menerima antarmuka io.Reader sebagai sumber data.



Slide memperlihatkan pipeline - ini menunjukkan tampilannya: kita perlu menyalip data untuk menyalip dari Reader ke Writer, dari Writer ke Reader. Bagaimana cara melakukannya?

Pustaka standar memiliki fitur keren - io.Pipe. Ini mengembalikan dua nilai: pipeReader dan pipeWriter. Pasangan ini terkait erat. Bayangkan "telepon bayi" dalam cangkir dengan tali: tidak masuk akal untuk berbicara dalam satu cangkir sementara tidak ada yang mendengarkan di ujung yang lain ...



Apa yang dilakukan io.Pipe ini? Itu tidak akan membaca sampai tidak ada yang menulis data. Dan sebaliknya, dia tidak akan menulis apa pun sampai tidak ada yang membaca data ini di ujung lainnya. Berikut adalah contoh implementasi:



Kami akan melakukan hal yang sama di sini. Kami akan membaca file robot.txt, yang telah dibaca sebelumnya, kami akan mengompresnya menggunakan gzip kami dan mengirimkannya ke S3.

  • Pada baris pertama, pasangan dibuat - pipeReader, pipeWriter. Selanjutnya, kita harus menjalankan setidaknya satu goroutine, yang akan membaca data dari satu ujung (semacam pipa). Di gorutin ini, jalankan pengunggah dengan sumber data (sumber - pipeReader).
  • Pada langkah selanjutnya, kita perlu mengompres data. Kami mengompres data dan menulisnya ke pipeWriter (itu akan menjadi ujung pipa), dan sudah menjalankan goroutine menerima data di ujung pipa yang lain dan membacanya. Ketika seluruh sandwich ini siap, yang tersisa hanyalah membakar sumbu ...
  • Lihat: io.Copy pada baris terakhir menulis data dari Tubuh ke gzip yang kami buat (mis. Dari Reader ke Writer). Semua ini berjalan seperti yang diharapkan.

Contoh ini dapat diselesaikan dengan cara lain. Jika Anda menggunakan implementasi apa pun yang mengimplementasikan Reader dan Writer. Pertama-tama Anda akan menulis data ke dalamnya, dan kemudian membacanya.
Itu adalah demonstrasi yang jelas tentang cara bekerja dengan io.Pipe.

Implementasi lainnya


Itu pada dasarnya semua untukku. Kami sampai pada implementasi menarik yang ingin saya bicarakan.



Saya tidak mengatakan apa-apa tentang MultiReader, atau tentang MultiWriter. Dan ini adalah implementasi keren lain dari perpustakaan standar, yang memungkinkan Anda untuk menghubungkan implementasi yang berbeda. Misalnya, MultiWriter menulis ke semua Penulis secara bersamaan, dan MultiReader membaca Pembaca secara berurutan.

Implementasi lain disebut limio. Ini memungkinkan Anda untuk menetapkan batas pengurangan. Anda dapat mengatur kecepatan dalam byte per detik di mana Pustaka Anda perlu dibaca.

Implementasi lain yang menarik hanyalah visualisasi dari kemajuan membaca - bilah Kemajuan (dari beberapa pria). Ini disebut ioprogress.

Kenapa aku mengatakan semua ini? Apa yang saya maksud dengan itu?



  • Jika Anda tiba-tiba perlu mengimplementasikan antarmuka Reader dan Writer, lakukan dengan benar. Belum ada keputusan tunggal yang bertanggung jawab atas implementasi - kami akan menganggap bahwa semua orang mempercayai kontrak. Jadi Anda harus mematuhinya tanpa cela.
  • Jika kasing Anda berfungsi dengan buffer yang diposisikan ulang, jangan lupa tentang antarmuka ReaderFrom dan WriterTo.
  • Jika Anda menemui jalan buntu dan Anda membutuhkan contoh - lihat perpustakaan standar, ada banyak implementasi keren yang dapat Anda andalkan. Ada dokumentasi di sana.
  • Jika ada sesuatu yang benar-benar tidak bisa dipahami oleh Anda, maka silakan menulis masalah. Orang-orang di sana cukup, merespons dengan cepat, sangat sopan dan kompeten membantu Anda.



Itu semua untuk saya. Terima kasih sudah datang!

Pertanyaan


Pertanyaan dari hadirin (B): - Saya punya pertanyaan sederhana, saya kira. Tolong beritahu kami tentang beberapa kasus penggunaan dari kehidupan: yang digunakan dan mengapa? Anda mengatakan bahwa Reader / Writer mengembalikan panjang yang dibaca. Pernahkah Anda mengalami masalah dengan ini; kapan Anda menuntut untuk membaca (tidak hanya ReadAll ada), tetapi sesuatu tidak berhasil?

SA: - Saya harus jujur ​​mengakui bahwa saya tidak pernah memiliki kasus seperti itu, karena saya selalu bekerja dengan implementasi perpustakaan standar. Tapi secara hipotesis, situasi seperti itu, tentu saja, mungkin. Adapun kasus-kasus tertentu, kami sering mengumpulkan pipa multilayer, dan jika Anda secara hipotesis membiarkan bug semacam itu, seluruh pipa akan berantakan ...

T:- Ini bukan bug. Mari kita ceritakan tentang pengalaman kecilku. Saya punya masalah dengan Booking.com: mereka menggunakan driver yang saya tulis, dan mereka punya masalah - ada sesuatu yang tidak berfungsi. Ada protokol biner standar yang kami lakukan; secara lokal, semuanya bekerja dengan baik, semua orang baik-baik saja, tetapi ternyata mereka memiliki jaringan yang sangat buruk dengan pusat data. Maka Pustaka tidak benar-benar mengembalikan semuanya (kartu jaringan buruk, sesuatu yang lain).

CA: - Tetapi jika dia tidak mengembalikan semuanya, maka dia seharusnya tidak mengembalikan tanda akhir (akhir), dan klien harus datang lagi. Di bawah kontrak yang dijelaskan, Pembaca tidak boleh ... Katakan saja Pembaca itu, tentu saja, memutuskan kapan dia ingin datang, ketika dia tidak ingin, namun, jika dia ingin membaca semuanya, dia harus menunggu EOF.

DI:"Tapi itu justru karena hubungannya." Ini persis masalah yang terjadi dalam paket net standar.

CA: - Dan dia mengembalikan EOF?

T: - Dia tidak mengembalikan semuanya - dia tidak membaca semuanya. Saya mengatakan kepadanya: "Baca 20 byte berikutnya." Dia membaca. Dan saya tidak membaca semuanya.

SA: - Secara hipotesis, ini dimungkinkan, karena itu hanya antarmuka yang menggambarkan protokol komunikasi. Perlu untuk menonton dan secara khusus membongkar kasus ini. Di sini saya hanya dapat menjawab Anda bahwa klien, secara teori, seharusnya datang lagi jika dia tidak menerima semua yang diinginkannya. Anda memintanya untuk sepotong 20 byte, dia mengurangi 15 untuk Anda, tetapi EOF tidak datang - Anda harus pergi lagi ...

T: - Ada io.ReadFull untuk situasi ini. Ini dirancang khusus untuk membaca slice hingga akhir.

CA:- Iya. Saya tidak mengatakan apa-apa tentang ReadFull.

T: - Ini adalah situasi yang sepenuhnya normal ketika Baca tidak mengisi seluruh irisan. Anda harus siap untuk ini.

SA: - Ini adalah kasus yang sangat diharapkan!

T: - Terima kasih atas laporannya - itu menarik. Saya menggunakan Pembaca dalam proksi kecil dan sederhana yang membaca http dan menulis dengan cara lain. Saya menggunakan Tutup Pembaca untuk menyelesaikan satu masalah - untuk menutup apa yang saya baca sepanjang waktu. Apakah saya perlu mempercayai kontrak secara membuta? Anda mengatakan bahwa mungkin ada masalah. Atau tambahkan cek tambahan? Secara teori dimungkinkan bahwa sesuatu tidak akan datang sepenuhnya di situs ini. Apakah saya perlu melakukan pemeriksaan tambahan ini dan tidak mempercayai kontrak?

CA:- Saya akan mengatakan ini: jika aplikasi Anda toleran terhadap kesalahan ini (misalnya, jika Anda sepenuhnya mempercayai kontrak), maka mungkin tidak. Tetapi jika Anda tidak ingin mendapatkan "kepanikan" dalam diri Anda (seperti yang saya tunjukkan pada bacaan negatif dalam byte.Buffer), maka saya masih akan memeriksa.
Tetapi ini “terserah Anda.” Apa yang bisa saya rekomendasikan kepada Anda? Saya pikir hanya menimbang pro dan kontra. Apa yang terjadi jika Anda tiba-tiba mendapatkan jumlah byte negatif?

T: - Terima kasih atas laporannya. Sayangnya, saya tidak tahu apa-apa di Go. Jika "panik" telah terjadi, apakah ada cara untuk mencegat informasi ini dan mendapatkan informasi tentang apa, di mana, bagaimana menjadi bias, untuk menghindari masalah pada Jumat malam?

CA: - Ya. Mekanisme Sembuh memungkinkan Anda untuk "menangkap" kepanikan dan membawanya keluar tanpa jatuh, secara relatif.



DI:- Bagaimana rekomendasi Anda untuk menggunakan implementasi Writer dan Reader konsisten dengan kesalahan yang dikembalikan saat menerapkan soket web. Saya tidak akan memberikan contoh konkret, tetapi apakah file selalu digunakan di sana? Sejauh yang saya ingat, pesan berakhir dengan beberapa makna lain ...

SA: - Ini adalah pertanyaan yang bagus, karena saya tidak punya apa-apa untuk dijawab. Harus ditonton! Jika EOF tidak datang, maka klien, jika dia ingin mendapatkan segalanya, harus pergi lagi.

T: - Berapa lama pipa bisa dipasang? Adakah keyakinan internal bahwa pipa tidak layak mengumpulkan lebih dari lima peserta, atau dengan cabang? Berapa lama Anda berhasil membangun pohon dari pipa-pipa ini (Baca, Tulis)?

CA:- Dalam praktik saya, sekitar lima panggilan berturut-turut adalah optimal, karena lebih sulit untuk di-debug, perhatikan apa yang mengalir dan ke mana ia pergi. Struktur bercabang cukup diperoleh. Tapi saya akan mengatakan suatu tempat 5-7 maksimum.

P: - 5-7 - dalam hal ini?

SA: - Ini sedang membaca, misalnya, beberapa data. Anda perlu berjanji, dan apa yang Anda login, Anda harus memotong. Dijanjikan - maka Anda membaca data ini - Anda perlu mengirimnya kembali ke beberapa penyimpanan (baik, secara hipotesis). Dalam penyimpanan apa pun yang mengimplementasikan antarmuka Writer. Dengan pipa ini, 5-6 langkah terjadi, meskipun pada salah satu langkah itu masih bercabang ke samping, dan Anda terus bekerja dengan Pembaca.

DI:- Menurut cara Pemula, Anda memiliki slide yang menarik. Dapatkah Anda menunjukkan 2-3 poin menarik lain yang ada di sana, tetapi sekarang lebih baik tidak melakukannya, tetapi melakukannya secara berbeda sekarang?

SA: - Dengan slide itu, saya ingin menunjukkan dengan tepat bagaimana melakukannya tanpa perlu membaca Reader. Tidak pernah terlintas dalam pikiran saya bahwa sesuatu seperti cara Pemula ... Ini mungkin kesalahan utama, pola utama yang harus dihindari ketika bekerja dengan Pembaca.
Presenter: - Saya ingin menambahkan sendiri bahwa sangat penting bagi seorang pemula untuk membaca semua dokumentasi paket io, pada semua antarmuka yang ada, dan memahaminya. Karena sebenarnya ada banyak dari mereka, dan Anda sering mulai melakukan sesuatu sendiri, meskipun sudah ada di sana dan diimplementasikan dengan benar ("benar" - dengan mempertimbangkan semua fitur).
Pertanyaan pemimpin: - Bagaimana cara hidup lebih jauh?

CA: - Pertanyaan bagus! Saya berjanji untuk memberi tahu jika kita punya waktu. Sebagai hasil dari diskusi bug, LimitedReader datang dengan keputusan berikut: untuk membuat kondom Reader dalam beberapa hal, yang melindungi terhadap ancaman eksternal, bungkus beberapa Reader yang Anda tidak percaya - untuk mencegah infeksi memasuki sistem Anda.

Dan dalam Pustaka ini, Anda menerapkan semua pemeriksaan yang tidak dapat Anda lakukan: misalnya, bacaan negatif, percobaan dengan jumlah byte (katakanlah Anda mengirim sepotong 10 byte, dan Anda mendapat 15 kembali - bagaimana bereaksi terhadap ini?) ... Dalam hal ini Pembaca dan Anda dapat menerapkan seperangkat cek tersebut. Saya berkata: "Mungkin mari kita tambahkan ke perpustakaan standar, karena akan berguna bagi semua orang untuk menggunakan"?

Saya diberi jawaban yang sepertinya tidak masuk akal dalam hal ini - ini adalah hal sederhana yang dapat Anda terapkan sendiri. Semua. Kita hidup terus. Kami percaya pada kawan-kawan kontraknya. Tapi saya tidak akan percaya.



T: - Saat kami bekerja dengan Pembaca, Penulis, dan ada peluang untuk mengalami "bom" gzip ... Seberapa besar kita mempercayai aliran ReadAll dan WriteAll? Atau, bagaimanapun, menerapkan pembacaan buffer dan hanya bekerja dengan buffer?

CA:- ReadAll sendiri hanya menggunakan byte. Buffer di bawah tenda. Ketika Anda ingin menggunakan ini atau itu, disarankan bagi Anda untuk masuk dan melihat bagaimana "nyali" ini diimplementasikan. Sekali lagi, itu tergantung pada kebutuhan Anda: jika Anda tidak toleran terhadap kesalahan yang saya tunjukkan, Anda perlu melihat apakah apa yang berasal dari Pembaca terbungkus diperiksa. Jika tidak dicentang, gunakan, misalnya, bufio (semuanya sudah diperiksa). Atau lakukan apa yang baru saja saya katakan: Pembaca proxy tertentu, yang menurut daftar persyaratan Anda akan memeriksa data ini dan mengembalikannya ke klien atau mengembalikannya ke klien.




Sedikit iklan :)


Terima kasih untuk tetap bersama kami. Apakah Anda suka artikel kami? Ingin melihat materi yang lebih menarik? Dukung kami dengan melakukan pemesanan atau merekomendasikan kepada teman Anda VPS berbasis cloud untuk pengembang mulai $ 4,99 , analog unik dari server entry-level yang diciptakan oleh kami untuk Anda: Seluruh kebenaran tentang VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps mulai dari $ 19 atau cara membagi server? (opsi tersedia dengan RAID1 dan RAID10, hingga 24 core dan hingga 40GB DDR4).

Dell R730xd 2 kali lebih murah di pusat data Equinix Tier IV di Amsterdam? Hanya kami yang memiliki 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV dari $ 199 di Belanda!Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - mulai dari $ 99! Baca tentang Cara Membangun Infrastruktur Bldg. kelas c menggunakan server Dell R730xd E5-2650 v4 seharga 9.000 euro untuk satu sen?

All Articles