Soket web Beberapa pengalaman dalam pengembangan dan operasi. Kami memodifikasi klien

Saya menyambut semua yang tertarik pada protokol ini, dan sebelumnya saya minta maaf atas catatan emosional saya yang terlalu berlebihan. Terlibat dalam hal ini dengan tergesa-gesa (sebagaimana diperlukan), tetapi untuk waktu yang lama. Dalam hal ini, praktik tertentu dalam merancang dan menggunakan teknologi ini telah menumpuk dan terbentuk. Opsi implementasi layanan dijelaskan di sini . Sejak itu banyak air yang mengalir. Prinsip dasar tetap sama, tetapi kode aplikasi itu sendiri telah mengalami modifikasi alami. Di beberapa tempat, kesalahan tidak kritis ditemukan dan diperbaiki, di suatu tempat kontrol aliran program, daftar koneksi terbuka, dll dioptimalkan.

Tetapi selain sisi server, seperti yang Anda tahu, ada sisi klien. Dan di sini saya ingin berhenti lebih teliti dan menggambarkan apa yang harus saya hadapi dan apa yang bisa dipengaruhi. Tentu saja, ketika menggunakan JavaScript, Anda tidak akan dapat terutama “bermain-main”, karena semuanya sudah siap dan ditutup, tetapi Anda dapat memberi tahu sesuatu tentang klien Java.

Tidak peduli seberapa bagus programmer Anda, selalu sulit untuk mengembangkan, menciptakan sesuatu yang unik, lalu men-debug sajak Anda sendiri. Karena itu, pada suatu waktu saya menyerah pada godaan untuk menemukan sesuatu yang sudah siap, memungkinkan Anda untuk segera menggunakannya dalam proyek saya. Kriteria pemilihan untuk modul jadi sederhana. Saya ingin mendapatkan kode yang lengkap dan berfungsi di Jawa dengan overhead yang minimal.

Sebagai yang terpilih, saya memilih modul menggunakan perpustakaan Apache. Secara khusus, ini:

  • apache-mime4j-core-0.7.2.jar;
  • httpclient-4.2.1.jar;
  • httpcore-4.2.1.jar;
  • httpmime-4.2.1.jar.

Apa yang bisa dikatakan tentang penggunaannya? Perangkat lunak Apache selalu terkenal dengan keandalan, kecanggihan, dan optimalitasnya. Klien untuk Android bekerja dengan sukses. Ukuran file * .apk yang sudah selesai tidak terlalu besar. Tidak ada keluhan khusus tentang pekerjaan perpustakaan ini. Tapi hidup selalu lebih pintar dari kita. Dan waktu (dan periode ini sekitar empat hingga lima tahun) membuat penyesuaian sendiri. Aplikasi ini ditulis ketika ada versi Android 4.2 - 4.4. Dan kebutuhan untuk solusi baru sudah muncul tahun ini, ketika perangkat dengan versi 10 sudah berjalan lancar.

Pengembangan dilakukan pada saat itu di Eclipse untuk Windows 7. Memperbarui Android SDK ke tingkat yang diinginkan menyebabkan fakta bahwa hard drive SSD 128 GB penuh. Saya harus beralih ke Android Studio. Selain itu, saya harus mengubah sistem operasi dasar. Saya mencoba menginstal Ubuntu (saya tidak ingat nomor versi) dan sudah menggunakan Studio di lingkungan ini. Tetapi sekali lagi, kegagalan, Andriod Studio dengan keras kepala tidak mau menginstal.

Kenapa - sudah dilupakan. Pada akhirnya, atas saran teman-teman, saya menginstal versi terbaru Linux-Mint, dan, lihatlah, toolkit berbaring di atasnya tanpa keluhan. Lalu apa yang sebenarnya terjadi, justru karena semua rincian ini dijelaskan, yaitu, tes menulis rutin.

Jadi, apa yang diharapkan dari tyagomotin ini? Mari kita mulai dengan fakta bahwa dari situs resmi Apache menyalin lebih banyak versi saat ini dari perpustakaan di atas. Menambahkannya ke proyek dan ... Dan kesalahan kompilasi jatuh. Waktu telah berlalu, antarmuka kelas telah berubah. Jadi saya harus (karena kurangnya waktu untuk mempelajari perpustakaan baru) untuk kembali ke versi lama. Tapi ...

Tetapi sekali lagi, kami tidak mencari cara yang mudah. Saya pikir, mengapa saya membutuhkan perpustakaan ini sepenuhnya? Teks dari paket ini adalah. Bagaimana jika Anda mengambil dan menarik hanya kelas yang diperlukan dari mereka? Selain itu, ketika mempertimbangkan teks modul untuk bekerja dengan soket web, Anda hanya bisa melihat dua kelas dari perpustakaan ini.

Jadi saya membuat proyek baru, dan mulai menambahkan kelas yang diperlukan. Dan pada akhirnya ternyata bahwa untuk kompilasi yang sukses perlu untuk menarik 32 kelas. Namun, ya, proyek itu berhasil. Semuanya bernafas. Koneksi ke layanan soket web berhasil. Dan semuanya akan baik-baik saja, tetapi memperhatikan peristiwa berikut, tidak dapat dipahami oleh saya. Saat menutup koneksi, modul yang bertanggung jawab untuk koneksi melemparkan pengecualian:

java.io.EOFException
di java.io.DataInputStream.readByte (DataInputStream.java:77)
di com.example.wsci.HybiParser.start (HybiParser.java:112)
di com.example.wsci.WebSocketClient $ 1.run (WebSocketClient.java:144)
di java.lang.Thread.run (Thread.java:818)

Saya bingung. Berikut bingung. Koneksi ke server berhasil. Paket berhasil datang dan pergi. Tapi, mengapa penutupan itu membuat pengecualian? Apalagi semuanya standar di server. Jelas, di suatu tempat dalam teks, klien memiliki beberapa hal yang mempengaruhi penutupan. Selain itu, teks-teks tersebut menunjukkan fitur seperti itu. Menurut klausa 7.1.1 dokumen, penutupan di sisi klien tidak hanya terdiri dari memanggil metode close (), tetapi juga dalam membentuk dan mengirim paket dengan kode operasi 8 (operasi penutupan). Dalam hal ini, server akan mengirim paket penutupannya, setelah itu klien akan menutup koneksi. Namun dalam kasus kami, urutan panggilan seperti itu tidak diamati. Itu hanya disebut fungsi dekat dan hanya itu. Secara umum, ada sesuatu untuk dipikirkan. Dan semakin saya mengintip dan mempelajari teks-teks modul ini dengan parser paket, semakin saya tidak menyukainya, semakin saya memiliki keinginan untuk menulis ulang dengan visi protokol ini. Pada akhirnya, diputuskan untuk melakukan "prestasi buruh" ini.

Apa yang sebenarnya tidak sesuai, apa yang menyebabkan "protes sipil" dalam modul-modul ini? Pertama, organisasi interaksi antara modul koneksi langsung dengan server dan paket parser. Ternyata modul koneksi berinteraksi dengan server, menghasilkan pengurai yang diteruskan ke parameter itu sendiri sebagai parameter. Akibatnya, parser didelegasikan wewenang untuk membuat keputusan tentang acara jaringan yang akan datang. Dalam hal ini, muncul pertanyaan, tetapi apakah itu baik? Bukankah lebih baik jika modul parser akan memenuhi misinya masing-masing, mengembalikan hasil kerjanya, tetapi keputusan kontrol tentang peristiwa akan dieksekusi oleh objek yang menghasilkan parser? Dalam hal ini, hirarki interaksi yang ketat antara objek akan ditentukan. (Di sini, tentu saja, Anda dapat memperdebatkan apa yang lebih baik - hierarki atau jaringan, tetapi kemudian kami menjauh dari topik.)

Hal kedua yang membuat saya ingin menulis ulang semuanya adalah struktur parser. Objek ini (kelas) harus memenuhi dua fungsi utama, yaitu, untuk membentuk paket data untuk transmisi ke server dan parsing paket yang diterima dari server. Jadi, dua fungsi inilah yang tidak cocok, pada umumnya. Dan inilah masalahnya.

Bayangkan suatu peristiwa jaringan telah terjadi, sebuah paket telah tiba. Apa yang dilakukan HybiParser dalam kasus ini? Objek ini membaca dua byte pertama dari aliran input soket dengan byte dan menentukan tindakan selanjutnya: mem-parsing ukuran data, mask, dll. Sebagai hasilnya, ini diimplementasikan dalam beberapa operasi baca dari aliran input soket. Selain itu, penguraian menjadi rumit dengan membaca tahapan, yang selanjutnya memperumit algoritma. Dan sekali lagi muncul pertanyaan, apakah benar, mengapa kesulitan seperti itu? Bukankah lebih baik menganggap paket sebagai satu operasi, terutama karena ukuran data yang masuk dapat ditentukan?

Yang ketiga. Tampaknya satu aspek lagi yang kontroversial dari pekerjaan parser adalah siklus penerimaan paket "abadi". Siklus berjalan dalam aliran program terpisah. Pada titik tertentu, soket ditutup. Apa selanjutnya, penanganan pengecualian yang biasa? Atau apa yang harus dilakukan? Tidak, saya tidak menentang mekanisme pengecualian, tetapi alangkah baiknya untuk meramalkan keadaan ini dan reaksi terhadapnya sebelumnya. Misalnya, dimungkinkan untuk mengusulkan mekanisme sinkronisasi sebagai solusi, di mana penyelesaian siklus secara teratur dan, karenanya, aliran program akan terjadi.

Sebagai hasil dari evaluasi semua nuansa ini, persyaratan berikut untuk desain modul yang diperlukan ditentukan:

  • Modul harus independen dari perpustakaan pihak ketiga;
  • Modul harus sederhana dan mudah diintegrasikan ke dalam proyek lain;
  • Modul harus siap untuk ekspansi fungsional di masa depan.

Nah, dan agar tidak disalahkan atas kritik berlebihan terhadap solusi yang sudah jadi, kami menambahkan ini juga bahwa bagian dari fungsi siap pakai dan tidak mengkritik dapat ditransfer dengan aman ke implementasi baru. Yah, itu saja, dan seperti yang mereka katakan di Kongres XXII CPSU: “Tujuan kami jelas, tugasnya ditentukan. Untuk bekerja, kawan! Demi kemenangan baru komunisme! "

Secara umum, agar tidak membebani Anda, pembaca yang budiman, untuk tidak mengambil waktu berharga Anda, saya akan menjelaskan secara singkat proposal saya dan hanya fokus pada poin-poin penting.
Jadi, implementasi yang diusulkan hanya berisi empat modul (sesuai dengan persyaratan di atas dari perpustakaan Apache atau kelas individu dari mereka tidak termasuk dalam proyek):

  • WebSocket protocol 07 modul konstanta global;
  • Kelas pengecualian pembantu;
  • Klien soket web;
  • Modul untuk parsing paket tingkat protokol WebSocket 07.

Dalam dua modul pertama, implementasinya sepele, tidak ada yang perlu dipertimbangkan. Modul klien mengimplementasikan kontrol atas koneksi ke server, dan saya ingin membahas beberapa hal berikut. Fungsi koneksi terbuka memiliki loop header yang datang dari server. Di sini, parsing kunci Sec-WebSocket-Accept sebenarnya diimplementasikan, dan dalam kasus kami, parsing dilakukan tanpa menggunakan pustaka Apache.

Selanjutnya, perhatikan fungsi kontrol loop paket. Implementasinya sepele, melalui objek sinkronisasi.

Titik selanjutnya yang harus dihormati adalah fungsi loop. Siklusnya tidak "abadi", tetapi dengan jalan keluar dalam kondisi memeriksa objek sinkronisasi. Dalam satu siklus, paket yang tiba dibaca dalam satu operasi. Paket diuraikan oleh objek yang sesuai untuk parsing. Selanjutnya, keputusan manajemen dibuat pada acara jaringan yang akan datang.

Untuk melengkapi deskripsi, kami mencatat fungsi mengakhiri sambungan dengan aman. Ini dilakukan sebagai berikut. Paket penghentian koneksi dikirim ke server dan variabel kelas Runnable dihasilkan, yang kemudian ditempatkan dalam prosesor antrian untuk dieksekusi melalui fungsi postDelayed, salah satu parameter di antaranya adalah keterlambatan pengoperasian dalam milidetik. Dalam variabel Runnable, fungsi run berisi urutan panggilan ketika aliran program berakhir dan koneksi ke server ditutup. Jika paket respons penutupan tiba lebih awal, posisi ini dalam daftar pemrosesan dihapus.

Kelas yang mengimplementasikan analisis paket WebSocket berisi dua metode yang memerlukan perhatian: penguraian sendiri dan pembentukan paket untuk transmisi sesuai dengan parameter yang relevan. Saat parsing, semua flag dan data dari paket yang diterima disimpan di depan umum, variabel kelas. Mengapa publik? Ya, untuk kesederhanaan, agar tidak membuat fungsi get / set tambahan untuk mereka.

Sebenarnya pembaca yang baik. Arsip dengan proyek untuk Android Studio terlampir . Saya tidak akan mengklaim penggunaan teks-teks ini dalam proyek Anda. Kritik konstruktif diterima. Jawab pertanyaan sejauh mungkin.

Source: https://habr.com/ru/post/id478546/


All Articles