Selasa, 16 Juni 2020

Programming KDE Using Qt


Mengenal Qt

Qt yang di baca “cute” adalah UI framework dan platform yang ditulis dengan bahasa C++, ada yang inget dengan KDE salah satu desktopnya linux selain Gnome, kalau di Ubuntu pakenya Gnome kalau Kubuntu pakenya KDE. Nah Qt ini yang dipakai untuk membuat KDE beserta applikasi-applikasi yang berada didalamnya.
Qt sebelumnya dimiliki oleh perusahaan yang bernama Trolltech dan sekarang perusahaan tersebut telah dibeli oleh Nokia. Qt framework tersedia secara gratis dan multiplatform (ada versi untuk linux, windows dan Mac). 


Kita bisa menggunakan Qt dengan 2 bahasa yaitu C++ sebagai bahasa utama dan Python dengan menggunakan PyQt. Sekarang saya menggunakan C++ pada Qt. Ya tujuan utamanya adalah belajar C++ juga :).
Nah Qt ini menyediakan Qt GUI Designer untuk mempermudah desain User Interface selain itu juga ada Qt Assistance yang berisi dokumentasi kelas-kelas pada Qt beserta beberapa tutorial serta ada Qt Linguist yang berguna ketika kita ingin membuat aplikasi Qt yang multilanguage. Ketika Tool tersebut sekarang telah dikumpulkan jadi satu pada tool bernama Qt Creator.


Selain Qt Creator tersedia juga plugin untuk eclipse dan visual studio. Pada eclipse maupun visual studio tool-tool seperti Gui Designer, Qt Assistance, dan Qt Linguist telah terintegrasi didalam IDE tersebut. Sedangkan untuk NetBeans versi 6.7 juga telah tersedia plugin Qt yang telah digabung dengan NetBeans CDT (Netbeans untuk C++) walaupun tool-tool seperti Gui Designer, Qt Assistance, dan Qt Linguist masih menggunaakan tool external walaupun dapat langsung diakses dari dalam NetBeans. Plugin untuk eclipse dan Visual Studio dapat di download secara gratis di situs resmi Qt sedangkan untuk NetBeans plugin Qt sudah di bundle pada NetBeans versi 6.7.
Untuk memulai Qt terlebih dahulu kita harus mendownload Qt Framework. Qt Framework dapat di download secara terpisah atau bundle dengan Qt Creator. Untuk yang versi linux Qt Framework berbentuk tar.gz sehingga untuk instalasi kita cukup meng-extrack file tersebut ke tempat yang diinginkan contoh /opt. Setelah itu kita harus menambahkan direktori /bin kedalam system path pada environtment variable. Kebetulan saya menggunakan linux mint (ubuntu) maka environtment berada pada file /etc/environtment. Qt juga membutuhkan compiler C++, pada linux kita cukup menginstall gcc/g++ standar untuk dapat menggunakan Qt Framework. Sedangkan untuk windows kita dapat menggunakan MinGW.
Saya baru mencoba Qt untuk linux sedangkan untuk windows dan mac saya belum pernah coba sehingga tidak banyak tahu konfigurasi  qt untuk kedua sistem operasi tersebut.
Saya sendiri menggunakan Netbeans versi 6.7 untuk mendevelop Qt, ini dikarenakan saya sudah sangat terbiasa dengan editor yang ada pada NetBeans selain itu code completion di NetBeans masih lebih bagus dibanding Qt Creator maupun Eclipse (versi saya lho :)). Untuk konfigurasi Qt pada NetBeans tidak banyak yang harus dilakukan asalkan Qt telah di set pada environtment variable dan kita dapat mengakses command “qmake” pada console maka Netbeans dapat digunakan dengan baik.

Programming GNOME Using GTK+


Aplikasi Gnome Sederhana Menggunakan libglade dan C / GTK +

Tutorial ini ditujukan bagi orang-orang yang tertarik untuk memulai mengembangkan aplikasi Gnome menggunakan GTK +. Secara khusus, ia menggunakan Desainer Antarmuka Glade untuk membangun antarmuka pengguna dan C / GTK + untuk menulis kode untuk aplikasi Gnome. Aplikasi hanya akan menjadi jendela dengan bilah alat dan bilah status khas. Aplikasi ini akan digunakan dalam tutorial selanjutnya untuk menunjukkan berbagai aspek lain dari pemrograman Gnome.

Mari Memulai

Untuk keperluan tutorial ini, saya akan membangun proyek ini di sistem saya di / home / mcarrick / Projects / gnome3 / . Semua tangkapan layar dan buka / simpan lokasi akan mencerminkan lokasi itu. Ketika membahas hierarki jalur, saya akan membicarakannya relatif terhadap jalur itu. Sebagai contoh, menempatkan kode sumber di direktori / src berarti bahwa itu sebenarnya di direktori / home / mcarrick / Projects / gnome3 / src . Oke? Baik. Jadi Anda seharusnya membuat direktori 'gnome3' di suatu tempat dan direktori 'src' di dalam direktori itu.
Proyek yang akan kami bangun adalah giong untuk disebut 'gnome3', dan hanya aplikasi dasar Gnome. '3' berasal dari kenyataan bahwa saya sudah memiliki gnome1 dan gnome2 dari beberapa tutorial yang lebih lama.

Membangun Antarmuka di Glade

Perancang Antarmuka Glade, atau hanya Glade, adalah aplikasi Gnome yang memungkinkan kita untuk menata letak widget kita secara visual daripada melalui kode C. Apa widget itu? Widget mirip "objek" dalam kosakata berorientasi objek. Hanya sebagian besar widget di GTK + yang terlihat secara fisik. Dalam pemrograman Windows, kita mungkin menyebutnya "kontrol". Tombol adalah widget, label pada tombol itu adalah widget lain, jendela adalah widget, bilah gulir adalah widget, dll. Widget, seperti objek atau kelas, dapat diturunkan dari widget lain. Widget jendela dapat berasal dari widget basis wadah, dll. Hubungan ini dikenal sebagai widget atau hierarki objek.
Jadi mari kita lihat apa yang saya bicarakan. Mulai Glade dan pilih 'Baru' dari menu 'Proyek', lalu klik tombol 'Proyek GNOME Baru'. Sekarang, sebelum kita melangkah lebih jauh, mari kita buat windows kita terlihat. Kita akan belajar apa yang mereka lakukan saat tutorial berjalan. Untuk saat ini, pastikan Anda membuka jendela Pallette, Editor Properti , dan Widget Tree . Anda dapat melakukannya dari menu 'Lihat' di jendela Glade utama.
Kami akan menambahkan Jendela Aplikasi Gnome ke proyek kami dengan mengklik ikon yang terletak di bilah alat Glade di bawah 'Gnome'. Sebuah jendela akan muncul. Itu adalah Jendela Aplikasi Gnome dan jendela utama untuk aplikasi kita. Jendela Aplikasi Gnome sebenarnya adalah widget GnomeApp, bagian dari libgnomeui. Dokumentasi API menyatakan: "Widget GnomeApp dan fungsi terkait menyediakan cara termudah untuk membuat aplikasi GNOME yang hampir lengkap di libgnomeui". Itu sangat benar. Ini adalah cara yang sangat mudah untuk mengatur basis aplikasi Gnome. Sekarang kembali ke berbicara tentang widget dan hierarki objek, hierarki objek untuk widget GnomeApp ditunjukkan di bawah ini.
  GObject
  + ---- GtkObject
   + ---- GtkWidget
    + ---- GtkContainer
     + ---- GtkBin
      + ---- GtkWindow
       + ---- GnomeApp 
Seperti yang bisa kita lihat, GnomeApp diturunkan dari GtkWindow, dan seterusnya pohon. Di atas pohon adalah GObject yang merupakan objek dasar untuk semua objek lainnya. Ini adalah bagian dari glib (inti, fungsi dan utilitas tingkat rendah di dasar Gnome dan GTK). Awalannya adalah 'G'. Mulai sekarang, kaitkan satu 'g' dengan glib. Objek yang dimulai dengan 'G' dan fungsi yang dimulai dengan 'g_' biasanya akan dikaitkan dengan glib. Kemudian, kami memiliki beberapa objek dengan awalan "Gtk". Itu adalah bagian dari GTK. Objek yang dimulai dengan 'Gtk' dan fungsi yang dimulai dengan 'gtk_' biasanya dikaitkan dengan GTK +. Dan akhirnya, GnomeApp memiliki 'Gnome' sebagai awalan. Ini adalah bagian dari perpustakaan Gnome UI. Objek yang dimulai dengan 'Gnome' dan fungsi yang dimulai dengan 'gnome_' dikaitkan dengan pustaka Gnome. Ini akan menjadi sangat jelas ketika Anda mulai menulis kode nanti di tutorial. Kembali ke Glade, kita akan mengganti nama widget GnomeApp menjadi sesuatu yang lebih bagus dan memberikannya judul, yang muncul di "titlebar" jendela. Untuk mengganti nama widget, klik untuk memilih 'app1' di jendela utama Glade atau jendela Tree Widget, dan ubah properti 'Name:' di jendela Properties Glade menjadi 'app_window'. Juga ubah properti 'Judul' di jendela Properti Glade ke 'Gnome Application 3'. Di bawah ini adalah tangkapan layar dari sistem saya.
Sebelum kita melangkah lebih jauh, mari kita simpan file glade (file XML). Klik ikon 'Simpan'. Dalam dialog simpan, kita akan menyimpan file sebagai src / gnome3.glade. Glade dapat menghasilkan kode sumber C atau C ++ yang mengkompilasi untuk membangun aplikasi yang Anda desain, namun metode itu bukan lagi cara ideal untuk menggunakan Glade. Sebagai gantinya, file proyek Glade, file .glade, diuraikan oleh aplikasi saat run-time menggunakan libglade. Ini memungkinkan Anda untuk memodifikasi / memperbarui UI tanpa harus mengkompilasi ulang seluruh program dan memungkinkan program terpisah dari antarmuka. Jadi, saat menyimpan, satu-satunya hal yang benar-benar kami perhatikan adalah nama dan lokasi file 'Proyek'. Kami tidak akan membiarkan Glade membuat kode sehingga parameter lainnya tidak relevan untuk tutorial ini. Tangkapan layar saya di bawah.
Sekarang hanya ada satu hal lagi yang harus dilakukan dalam glade. Kita akan melampirkan penangan ke sinyal 'diklik' tombol bilah alat 'Baru'. Sinyal dapat dianggap sebagai peristiwa yang terjadi. Sebagian besar objek memiliki berbagai sinyal yang mereka "pancarkan" untuk berbagai peristiwa dan mereka juga mewarisi sinyal objek dasar di sana. Anda dapat mengetahui sinyal apa yang dimiliki objek dengan merujuk ke dokumentasi API untuk objek itu. Pawang untuk sinyal, adalah fungsi callback, dan dipanggil ketika sinyal itu "dipancarkan".
Klik tombol 'toolbar_new' untuk memilihnya, dan kemudian beralih ke tab 'Sinyal' di jendela Properties di Glade. Kemudian di bagian bawah jendela, ada kotak berlabel 'Sinyal:'. Klik di kotak untuk menampilkan dialog Select Signal . Anda dapat memilih salah satu sinyal yang terkait dengan widget GtkToolButton atau widget dari mana ia berasal. Pilih sinyal 'diklik' dan klik tombol 'Oke'. Sekarang kembali ke jendela Properties, kotak 'Handler:' telah diisi dengan "on_toolbutton_new_clicked". Kita akan membiarkan itu apa adanya. Klik tombol 'Tambah' di bagian bawah jendela untuk menerapkan perubahan ini. Jendela Anda akan terlihat seperti milik saya di bawah ini. "on_toolbutton_new_clicked" adalah nama fungsi yang perlu kita tulis yang akan dipanggil ketika tombol bilah alat 'Baru' diklik.
 

Menulis Kode

Di sinilah kesenangan dimulai. Buka editor teks Anda dan buat src / main.c. Kita akan melangkah melalui kode ini bagian satu per satu mulai dari atas.
  1: / * file config.h dihasilkan oleh autotools * /
  2: #ifdef HAVE_CONFIG_H
  3: # include & lt; config.h & gt;
  4: #endif <span style = "color: # b59000"> <span style = "color: # 000000"> </span>
 </span> 
Bagian ini memiliki beberapa arahan preprocessor untuk menyertakan file yang disebut config.h. File config.h dihasilkan oleh program yang disebut 'autoheader' yang merupakan bagian dari GNU 'autotools'. Meskipun 'autotools' adalah bagian penting dari pengembangan aplikasi Gnome, itu di luar ruang lingkup dokumen ini.
  6: / * ini akan menahan path ke file glade setelah "make install" * /
  7: #define GLADE_FILE PACKAGE_DATA_DIR "/gnome3/gnome3.glade" <span style = "color: # b59000"> <span style = "color: # 000000"> </span> </span> 
Di sini kita mendefinisikan path ke file XML gnome3.glade kami. PACKAGE_DATA_DIR didefinisikan dalam Makefile yang dihasilkan oleh autotools dan bergantung pada platform. Pada sistem Fedora saya, PACKAGE_DATA_DIR akhirnya menjadi / usr / local / share / dan dengan demikian, setelah mengkompilasi dan menginstal aplikasi ini pada sistem saya (menggunakan perintah make install), file glade untuk aplikasi ini akan berlokasi di / usr / local /share/gnome3/gnome3.glade.
  9: #termasuk & lt; gnome.h & gt;
 10: #include & lt; glade / glade.h & gt; <span style = "color: # b59000"> <span style = "color: # 000000"> </span> </span> 
Ini termasuk harus terlihat cukup jelas, namun, saya ingin menunjukkan satu hal kecil. File header yang disertakan akan memanggil pustaka dari mana mereka berasal. Dalam hal ini, gnome.h akan memanggil gtk.h yang akan memanggil glib.h dll. Jadi tidak perlu menyertakan gtk / gtk.h untuk aplikasi Gnome.
Glade / glade.h termasuk untuk libglade yang memungkinkan kita untuk menghasilkan antarmuka pengguna dari file XML glade.
  12: / * prototipe fungsi panggilan balik * /
 13: static gint delete_event_cb (GtkWidget * w, GdkEventAny * e, data gpointer);
 14: static gint destroy_cb (GtkWidget * w, GdkEventAny * e, data gpointer); <span style = "color: # b59000"> <span style = "color: # 000000"> </span> </span> <span style = "color: # b59000"> <span style = "color: # 000000"> </span> </span> <span style = "color: # b59000"> </span> 
Kedua baris ini menyatakan fungsi "callback", itulah sebabnya saya menambahkan akhiran '_cb' ke nama mereka. Ini akan "terhubung" ke sinyal. Saya akan berhenti di situ untuk saat ini, kita akan melihat ini lagi di kode.
  17: int main (int argc, char * argv [])
 18: {
 19: GtkWidget * app_window;  / * widget jendela aplikasi utama * /
 20: GladeXML * gxml;  / * glade file xml * / 
Ketika kami meletakkan antarmuka kami di bagian sebelumnya, saya menunjukkan konvensi penamaan yang digunakan dengan widget. Kita melihatnya lagi di sini di mana kita mendeklarasikan dua variabel. Pertama, app_window adalah pointer ke tipe GtkWidget. Perhatikan awalan 'Gtk'. Karena itu, tipe GtkWidget adalah bagian dari pustaka GTK. Baris berikutnya kita mendeklarasikan gxml sebagai pointer ke tipe GladeXML. Ini harus menjadi bagian dari pustaka Glade, atau libglade.
Pada awalnya Anda mungkin bertanya mengapa kami mendeklarasikan app_window sebagai GtkWidget dan bukan GnomeApp. Inilah hierarki objek itu lagi. Ingatlah bahwa GnomeApp berasal dari beberapa objek yang kembali ke GtkWidget dan akhirnya kembali ke GObject. Jadi, widget GnomeApp adalah GtkWidget, dan dalam hal ini, itu adalah GObject. GtkWidget adalah objek dasar dari semua widget, dan memiliki fungsi yang sama untuk semua widget. Misalnya ada fungsi untuk menampilkan widget yang disebut gtk_widget_show () yang mengambil pointer ke GtkWidget sebagai parameter. Karena itu kami biasanya mendeklarasikan widget sebagai kelas objek dasar mereka, sebuah GtkWidget.
Jadi sekarang kita memiliki app_window yang akan mereferensikan widget GnomeApp kita (tetapi belum) dan kita memiliki gxml yang mereferensikan objek GladeXML yang akan kita gunakan untuk membangun antarmuka dari file XML Glade.
  22: / * inisialisasi libgnome * /
 23: gnome_program_init (PAKET, VERSI, LIBGNOMEUI_MODULE,
 24: argc, argv,
 25: NULL); <span style = "font-family: monospace"> </span> <span style = "color: # b59000"> <span style = "color: # 000000">
 </span> </span> <span style = "font-family: monospace"> </span> 
Semua aplikasi GTK + dan Gnome harus memanggil fungsi untuk menginisialisasi perpustakaan sebelum fungsi GTK + digunakan. Dalam program GTK + murni, Anda akan melihat gtk_init (). Fungsi ini, gnome_program_init () khusus untuk aplikasi Gnome. Banyak kode yang Anda lihat, terutama dalam tutorial, Anda akan melihat gnome_init () sebagai gantinya - yang sebenarnya terlihat lebih bagus karena tidak memiliki semua argumen yang diteruskan ke sana. Namun, gnome_init () adalah fungsi yang sudah usang dan tidak boleh digunakan. Anda harus membaca tentang gnome_program_init () dalam dokumentasi API. Anda pasti perlu tahu tentang fungsi ini, untuk saat ini, saya hanya akan menjelaskan secara singkat seperti yang berlaku untuk program ini.
PAKET dan VERSI didefinisikan dalam file config.h yang secara otomatis dibuat oleh autotools. Untuk program ini, mereka masing-masing 'gnome3' dan '0,1'. LIBGNOMEUI_MODULE didefinisikan dalam libgnomeui.h (yang telah kami sertakan secara tidak langsung dengan memasukkan gnome.h) dan merupakan modul yang berisi informasi untuk menginisialisasi pustaka gnome. argc dan argv adalah argumen baris perintah aplikasi C / C ++ standar yang kita kenal dan sukai. argc adalah hitungan argumen baris perintah dan argv adalah array string argumen baris perintah. Selanjutnya, fungsi mengambil serangkaian pasangan nama / nilai atribut yang diakhiri oleh NULL. Untuk menjaga hal-hal sederhana dan minimal untuk saat ini, kami hanya melewati penghentian NULL. (Daftar pasangan nama / nilai atribut kosong).
Dalam banyak tutorial dan kode yang Anda lihat menggunakan libglade, Anda akan sering menemukan panggilan ke glade_init () pada saat ini untuk menginisialisasi pustaka Glade (libglade). Ini bukan lagi panggilan yang diperlukan karena versi baru pustaka Glade akan diinisialisasi berdasarkan permintaan.
  27: / * membuat objek GladeXML dan menghubungkan sinyal * /
 28: gxml = glade_xml_new (GLADE_FILE, NULL, NULL);
 29: glade_xml_signal_autoconnect (gxml); <span style = "font-family: monospace">
 </span> <span style = "font-family: monospace"> </span> 
Di sinilah kami menggunakan libglade untuk membangun antarmuka kami dari file XML Glade yang kami buat sebelumnya. Jika Anda terbiasa dengan bahasa OOP (kelas) maka Anda terbiasa dengan instantiate objek yang sering menggunakan kata kunci seperti "baru" (obj = new SomeObject;). C secara teknis bukan bahasa berorientasi objek, namun, GTK + berbasis objek. Ini dicapai menggunakan fungsi. Dengan sebagian besar objek GTK + akan ada serangkaian fungsi untuk membuat yang baru dari objek-objek itu, seperti konstruktor kelas.
Panggilan ke glade_xml_new menciptakan objek GladeXML baru yang dirujuk oleh gxml, dan itu juga menciptakan semua widget dalam file Glade yang kami lewatkan ke fungsi sebagai argumen pertama (kami mendefinisikan GLADE_FILE pada baris 7). Kami melewati NULL sebagai dua parameter lainnya karena kami belum perlu mengkhawatirkannya. Satu digunakan jika kita hanya ingin membangun widget tertentu dalam file glade dan yang lainnya digunakan untuk terjemahan.
Panggilan ke glade_xml_signal_autoconnect () adalah variasi pada fungsi lain, glade_xml_signal_connecr (). Seperti namanya, fungsi "autoconnect" secara otomatis "menghubungkan" sinyal yang ditentukan dalam file glade ke fungsi callback mereka (sinyal "handler") dalam kode C. Ingat ketika membangun antarmuka kami, kami menetapkan bahwa sinyal "diklik" tombol bilah alat harus "ditangani" oleh fungsi on_toolbar_new_clicked (). Panggilan ini membaca informasi itu dari file glade dan "menghubungkan" sinyal itu ke fungsi panggilan balik itu.
  31: / * dapatkan app_window dari file XML glade * /
 32: app_window = glade_xml_get_widget (gxml, "app_window"); 
Sekarang, semuanya bagus dan keren bahwa libglade dapat "membangun" antarmuka kita dari file XML itu, tetapi bagaimana kita mereferensikan widget itu? Jawabannya adalah glade_xml_get_widget (), di mana 2 parameternya adalah objek GladeXML gxml dan nama widget yang ingin kita "dapatkan". Dalam hal ini, kita mendapatkan "app_window" yang merupakan widget GnomeApp kami. Setelah panggilan ini, pointer GtkWidget app_window merujuk widget GnomeApp. Kami akan menggunakan ini beberapa baris nanti.
  34: / * Hubungkan sinyal untuk penghentian aplikasi * /
 35: g_signal_connect (G_OBJECT (app_window), "delete_event",
 36: G_CALLBACK (delete_event_cb), NULL);
 37: 
 38: g_signal_connect (G_OBJECT (app_window), "menghancurkan",
 39: G_CALLBACK (destroy_cb), NULL); 

Parameter ketiga dalam fungsi g_signal_connect () adalah fungsi yang kami sambungkan ke sinyal. Ini adalah fungsi "callback". Pertama-tama kita harus melemparkan fungsi ke dalam penunjuk GCallback menggunakan makro G_CALLBACK.
Dan akhirnya kami memiliki parameter ke-4 yang merupakan data yang akan kami sampaikan ke fungsi panggilan balik kami, dalam hal ini, tidak ada (NULL).
Artinya, ketika sinyal "delete_event" dipancarkan, fungsi kami yang bernama delete_event_cb () akan dipanggil. Ketika sinyal "hancurkan" dipancarkan, fungsi kami destroy_cb () akan dipanggil.
  41: / * tampilkan jendela uta
Kedua garis ini juga "menghubungkan" sinyal ke fungsi yang "menangani" peristiwa itu. Kita dapat mengatur ini di Glade seperti yang kita lakukan dengan sinyal "klik" untuk tombol tool dan membiarkan fungsi glade_xml_autoconnect () menghubungkan sinyal, namun, saya ingin melakukannya di sini dalam kode sebagai demonstrasi bagaimana sinyal terhubung dan menunjukkan beberapa casting macro. Sangat penting untuk memahami konsep sinyal dan fungsi panggilan balik. Ada 4 argumen yang diteruskan ke panggilan fungsi g_signal_connect ().
Pertama, G_OBJECT (app_window), yang merupakan app_window GtkWidget kami dilemparkan ke dalam pointer GObject. G_OBJECT adalah makro yang melakukan casting. Anda akan menemukan makro casting ini tersedia untuk semua casting yang perlu Anda lakukan. G_signal_connect mengambil pointer GObject sebagai argumen pertama, namun app_window kami didefinisikan sebagai pointer GtkWidget, itulah sebabnya kita harus melemparkannya ketika meneruskannya sebagai parameter. Ingatlah bahwa GtkWidget diturunkan dari GObject, itulah sebabnya kita bisa melemparkannya ke dalam pointer GObject. Contoh lain, adalah jika kita memiliki fungsi yang membutuhkan GtkWindow seperti gtk_window_get_title (). Kita bisa memasukkan app_window ke dalam penunjuk objek GtkWindow menggunakan makro GTK_WINDOW. Di sinilah hirarki objek itu sangat keren. Kita dapat menggunakan fungsi-fungsi untuk kelas mana pun dari mana widget diturunkan dengan melemparkan widget poitner ke tipe yang sesuai.
Parameter kedua dalam fungsi g_signal_connect () adalah nama sinyal yang digunakan untuk menghubungkan fungsi. Seperti yang Anda lihat, kami memiliki 2 sinyal yang kami hubungkan dengan fungsi. Yang pertama adalah sinyal "delete_event" yang dipancarkan ketika kami meminta agar jendela ditutup. Mengklik 'x' kecil di bagian atas jendela akan memunculkan sinyal ini. Berikutnya adalah sinyal "hancurkan" yang dipancarkan ketika jendela dihancurkan. Kita akan lihat mengapa ini penting nanti.
ma * /
 42: gtk_widget_show (app_window); 
Sekarang kita siap untuk benar-benar menunjukkan jendela kita. Fungsi gtk_widget_show () menunjukkan widget. Kami cukup memberikannya pointer GtkWidget kami, app_window yang merujuk widget GnomeApp kami - jendela aplikasi utama.
  44: / * memulai loop GTK utama * /
 45: gtk_main ();
 46: 
 47: return 0;
 48:} 
Sekarang, fungsi gtk_main () adalah loop "tanpa akhir". Loop akan memanggil fungsi kami berdasarkan "peristiwa" yang terjadi. Contoh sempurna, adalah sinyal "klik" yang kami siapkan untuk tombol alat. Ketika tombol itu diklik, loop gtk_main () menyerahkan kontrol ke "handler" kami di_toolbutton_new_clicked () setelah itu kontrol dilanjutkan di loop tanpa akhir. Ini adalah bagaimana kami mencapai aplikasi berbasis "event". Untuk keluar dari loop, salah satu fungsi kami harus memanggil gtk_main_quit (), itulah sebabnya kami memiliki panggilan balik untuk sinyal "hancurkan" seperti yang akan kita lihat sebentar lagi.
  50: static gint delete_event_cb (GtkWidget * w, GdkEventAny * e, data gpointer)
 51: { 
 52: / * panggilan balik untuk sinyal "hapus" * /
 53: g_print ("main.c: delete_event_cb () n");
 54: return 0;
 55:}
 56:
 57: static gint destroy_cb (GtkWidget * w, GdkEventAny * e, data gpointer)
 58: {
 59: / * panggilan balik untuk sinyal "hancurkan" * /
 60: g_print ("main.c: destroy_event_cb () n"); 
 61: 
 62: / * keluar dari aplikasi * /
 63: gtk_main_quit ();
 64: return 0;
 65:} 
Kami sebenarnya tidak perlu menangani "delete-event" untuk aplikasi ini, namun, saya menambahkannya untuk menunjukkan kapan peristiwa itu terjadi. Baik delete_event_cb () dan destr_cb () saya memanggil g_print () (yang sangat mirip dengan fungsi printf) untuk mencetak nama fungsi. Ini hanya supaya kita bisa melihat peristiwa ini terjadi. Namun perhatikan, pawang untuk sinyal "hancurkan" memiliki panggilan ke gtk_main_quit (). Ini sangat penting. Ingat bahwa gtk_main () adalah loop tanpa akhir. Jika pengguna menutup jendela itu, itu menghancurkan widget tetapi eksekusi masih dalam loop gtk_main () dan program belum benar-benar dihentikan. Ini akan "muncul" karena jendela hilang, tetapi masih akan berjalan. Dengan menangkap sinyal "hancurkan" kita dapat keluar dari loop itu ketika pengguna menghancurkan (menutup) jendela. Aha, semuanya masuk akal sekarang.
Satu hal lagi yang perlu diperhatikan, adalah sinyal "delete-event" dapat mengembalikan nilai jika Anda ingin mencegahnya terjadi. "Delete-event" terjadi ketika widget (jendela) meminta dihancurkan. Kami dapat menghentikan hal ini terjadi jika, misalnya, kami ingin terlebih dahulu meminta pengguna untuk memastikan mereka ingin berhenti. Dengan kotak dialog dan kode kondisional, kita dapat mengembalikan 0 jika mereka yakin mereka ingin berhenti, dan 1 jika mereka tidak benar-benar ingin berhenti.
  67: void <br style="font-family: monospace" /> 68: on_toolbutton_new_clicked (GtkWidget * w, GdkEventKey * e) <br style="font-family: monospace" /> 69: {<br style = "font -family: monospace "/> 70: / * bilah alat 'Baru' diklik * / <br style="font-family: monospace" /> 71: g_print (" main.c: on_toolbutton_new_clicked () "); <br style = "font-family: monospace" /> 72:} 
 
 SEKIAN DAN TERIMAKASIH:)

Sockets


Apa itu Soket?

Biasanya, server berjalan pada komputer tertentu dan memiliki soket yang terikat ke nomor port tertentu. Server hanya menunggu, mendengarkan soket untuk klien untuk membuat permintaan koneksi.
Di sisi klien: Klien tahu nama host dari mesin yang menjalankan server dan nomor port di mana server mendengarkan. Untuk membuat permintaan koneksi, klien mencoba untuk bertemu dengan server pada mesin dan port server. Klien juga perlu mengidentifikasi dirinya ke server sehingga mengikat ke nomor port lokal yang akan digunakan selama koneksi ini. Ini biasanya ditugaskan oleh sistem.
Permintaan koneksi klien
Jika semuanya berjalan dengan baik, server menerima koneksi. Setelah diterima, server mendapatkan soket baru yang terikat ke port lokal yang sama dan juga memiliki endpoint jarak jauh yang diatur ke alamat dan port klien. Diperlukan soket baru sehingga dapat terus mendengarkan soket asli untuk permintaan koneksi sambil merawat kebutuhan klien yang terhubung.
Koneksi dibuat
Di sisi klien, jika koneksi diterima, soket berhasil dibuat dan klien dapat menggunakan soket untuk berkomunikasi dengan server.
Klien dan server sekarang dapat berkomunikasi dengan menulis ke atau membaca dari soket mereka.

Definisi:
Soket adalah salah satu titik akhir dari tautan komunikasi dua arah antara dua program yang berjalan di jaringan. Soket terikat ke nomor port sehingga lapisan TCP dapat mengidentifikasi aplikasi tempat data akan dikirim.

Endpoint adalah kombinasi dari alamat IP dan nomor port. Setiap koneksi TCP dapat diidentifikasi secara unik oleh dua titik akhir. Dengan begitu Anda dapat memiliki beberapa koneksi antara host dan server.
Paket java.net di platform Java menyediakan kelas, Socket , yang mengimplementasikan satu sisi koneksi dua arah antara program Java Anda dan program lain di jaringan. Kelas Socket berada di atas implementasi yang bergantung pada platform, menyembunyikan detail sistem apa pun dari program Java Anda. Dengan menggunakan kelas java.net.Socket alih-alih mengandalkan kode asli, program Java Anda dapat berkomunikasi melalui jaringan dengan cara yang bebas platform.
Selain itu, java.net menyertakan kelas ServerSocket , yang mengimplementasikan soket yang dapat digunakan server untuk mendengarkan dan menerima koneksi ke klien. Pelajaran ini menunjukkan kepada Anda bagaimana menggunakan kelas Socket dan ServerSocket .
Jika Anda mencoba untuk terhubung ke Web, kelas URL dan kelas terkait ( URLConnection , URLEncoder ) mungkin lebih sesuai daripada kelas soket. Faktanya, URL adalah koneksi tingkat tinggi ke Web dan menggunakan soket sebagai bagian dari implementasi yang mendasarinya. Lihat Bekerja dengan URL untuk informasi tentang menghubungkan ke Web melalui URL.
 

Membaca dari dan Menulis ke Soket

Mari kita lihat contoh sederhana yang menggambarkan bagaimana suatu program dapat membangun koneksi ke program server menggunakan kelas Socket dan kemudian, bagaimana klien dapat mengirim data ke dan menerima data dari server melalui soket. Contoh program mengimplementasikan klien, EchoClient , yang menghubungkan ke server gema. Server gema menerima data dari kliennya dan mengulanginya kembali. Contoh EchoServer mengimplementasikan server gema. (Atau, klien dapat terhubung ke host yang mendukung Protokol Echo .) Contoh EchoClient membuat soket, sehingga mendapatkan koneksi ke server gema. Bunyinya input dari pengguna pada aliran input standar, dan kemudian meneruskan teks itu ke server gema dengan menulis teks ke soket. Server menggaungkan input kembali melalui soket ke klien. Program klien membaca dan menampilkan data yang dikembalikan ke sana dari server. Perhatikan bahwa contoh EchoClient menulis dan membaca dari soketnya, sehingga mengirim data ke dan menerima data dari server gema. Mari kita berjalan melalui program dan menyelidiki bagian-bagian yang menarik. Pernyataan berikut dalam pernyataan try -dengan-sumber daya dalam contoh EchoClient sangat penting. Baris-baris ini membangun koneksi socket antara klien dan server dan membuka PrintWriter dan BufferedReader pada socket:
 String hostName = args [0];
 int portNumber = Integer.parseInt (args [1]);

 coba (
     Socket echoSocket = Socket baru (hostName, portNumber);
     PrintWriter out =
         PrintWriter baru (echoSocket.getOutputStream (), true);
     BufferedReader di =
         BufferedReader baru (
             InputStreamReader baru (echoSocket.getInputStream ()));
     BufferedReader stdIn =
         BufferedReader baru (
             InputStreamReader baru (System.in))
 )
Pernyataan pertama dalam try dengan pernyataan sumber daya membuat objek Socket baru dan echoSocket nama echoSocket . Konstruktor Socket digunakan di sini memerlukan nama komputer dan nomor port yang ingin Anda sambungkan. Program contoh menggunakan argumen baris perintah pertama sebagai nama komputer (nama host) dan argumen baris perintah kedua sebagai nomor port. Ketika Anda menjalankan program ini di komputer Anda, pastikan bahwa nama host yang Anda gunakan adalah nama IP komputer yang memenuhi syarat yang ingin Anda sambungkan. Misalnya, jika server gema Anda berjalan di komputer echoserver.example.com dan sedang mendengarkan pada port nomor 7, pertama jalankan perintah berikut dari komputer echoserver.example.com jika Anda ingin menggunakan contoh EchoServer sebagai gema Anda server:
 java EchoServer 7
Setelah itu, jalankan contoh EchoClient dengan perintah berikut:
 java EchoClient echoserver.example.com 7
Pernyataan kedua dalam pernyataan try -dengan sumber daya mendapatkan aliran output soket dan membuka PrintWriter di atasnya. Demikian pula, pernyataan ketiga mendapatkan aliran input soket dan membuka BufferedReader di atasnya. Contoh ini menggunakan pembaca dan penulis sehingga dapat menulis karakter Unicode melalui soket. Untuk mengirim data melalui soket ke server, contoh EchoClient perlu menulis ke PrintWriter . Untuk mendapatkan respons server, EchoClient membaca dari objek BufferedReader stdIn , yang dibuat dalam pernyataan keempat di try dengan pernyataan sumber daya. Jika Anda belum terbiasa dengan kelas I / O platform Java, Anda mungkin ingin membaca I / O Dasar . Bagian menarik berikutnya dari program ini adalah loop while . Loop membaca garis pada satu waktu dari aliran input standar dan segera mengirimkannya ke server dengan menulisnya ke PrintWriter terhubung ke soket:
 String userInput;
 while ((userInput = stdIn.readLine ())! = null) {
     out.println (userInput);
     System.out.println ("echo:" + in.readLine ());
 }
Pernyataan terakhir di loop while membaca baris informasi dari BufferedReader terhubung ke soket. Metode readLine menunggu hingga server mengulang kembali informasi ke EchoClient . Ketika readline kembali, EchoClient mencetak informasi ke output standar. Loop while berlanjut sampai pengguna mengetik karakter end-of-input. Yaitu, contoh EchoClient membaca input dari pengguna, mengirimkannya ke server Echo, mendapat respons dari server, dan menampilkannya, hingga mencapai akhir input. (Anda dapat mengetik karakter end-of-input dengan menekan Ctrl-C .) Loop while kemudian berakhir, dan runtime Java secara otomatis menutup pembaca dan penulis yang terhubung ke soket dan ke aliran input standar, dan itu menutup soket koneksi ke server. Java runtime menutup sumber daya ini secara otomatis karena mereka dibuat dalam pernyataan try -with-resources. Java runtime menutup sumber daya ini dalam urutan terbalik yang dibuat. (Ini bagus karena aliran yang terhubung ke soket harus ditutup sebelum soket itu sendiri ditutup.) Program klien ini mudah dan sederhana karena server gema mengimplementasikan protokol sederhana. Klien mengirim teks ke server, dan server mengulanginya. Ketika program klien Anda berbicara dengan server yang lebih rumit seperti server HTTP, program klien Anda juga akan lebih rumit. Namun, dasarnya sama dengan yang ada di program ini:
  1. Buka soket.
  2. Buka aliran input dan aliran output ke soket.
  3. Baca dari dan tulis ke aliran sesuai dengan protokol server.
  4. Tutup alirannya.
  5. Tutup soketnya.
Hanya langkah 3 yang berbeda dari klien ke klien, tergantung pada server. Langkah-langkah lain sebagian besar tetap sama.

Menulis Sisi Server dari Soket

Bagian ini menunjukkan kepada Anda bagaimana menulis server dan klien yang menyertainya. Server dalam pasangan klien / server menyajikan lelucon Knock Knock. Mengetuk Mengetuk lelucon disukai oleh anak-anak dan biasanya kendaraan untuk permainan kata-kata buruk. Mereka pergi seperti ini: Server : "Ketuk ketukan!" Klien : "Siapa di sana?" Server : "Dexter." Klien : "Dexter siapa?" Server : "Aula Dexter dengan dahan holly." Klien : "Mengerang." Contoh terdiri dari dua program Java yang berjalan secara independen: program klien dan program server. Program klien diimplementasikan oleh satu kelas, KnockKnockClient , dan sangat mirip dengan contoh EchoClient dari bagian sebelumnya. Program server diimplementasikan oleh dua kelas: KnockKnockServer dan KnockKnockProtocol . KnockKnockServer , yang mirip dengan EchoServer , berisi metode main untuk program server dan melakukan pekerjaan mendengarkan port, membangun koneksi, dan membaca dari dan menulis ke soket. Kelas KnockKnockProtocol menyajikan lelucon. Ini melacak lelucon saat ini, keadaan saat ini (ketukan ketukan terkirim, petunjuk kirim, dan sebagainya), dan mengembalikan berbagai potongan teks lelucon tergantung pada kondisi saat ini. Objek ini mengimplementasikan protokol — bahasa yang telah disetujui oleh klien dan server untuk digunakan untuk berkomunikasi. Bagian berikut ini terlihat secara rinci di setiap kelas di klien dan server dan kemudian menunjukkan kepada Anda bagaimana menjalankannya.

Server Knock Knock

Bagian ini membahas kode yang mengimplementasikan program server Knock Knock, KnockKnockServer . Program server dimulai dengan membuat objek ServerSocket baru untuk mendengarkan pada port tertentu (lihat pernyataan dalam huruf tebal di segmen kode berikut). Saat menjalankan server ini, pilih port yang belum didedikasikan untuk beberapa layanan lain. Sebagai contoh, perintah ini memulai program server KnockKnockServer sehingga ia mendengarkan pada port 4444:
 java KnockKnockServer 4444
Program server membuat objek ServerSocket dalam pernyataan try -dengan-sumber daya:
 int portNumber = Integer.parseInt (args [0]);

 coba ( 
     ServerSocket serverSocket = ServerSocket baru (portNumber);
     Socket clientSocket = serverSocket.accept ();
     PrintWriter out =
         PrintWriter baru (clientSocket.getOutputStream (), true);
     BufferedReader di = BufferedReader baru (
         InputStreamReader baru (clientSocket.getInputStream ()));
 ) { 
 
 ServerSocket adalah kelas java.net yang menyediakan implementasi sistem-independen dari sisi server dari koneksi soket klien / server.  Konstruktor untuk ServerSocket melempar pengecualian jika tidak dapat mendengarkan pada port yang ditentukan (misalnya, port sudah digunakan).  Dalam hal ini, KnockKnockServer tidak punya pilihan selain keluar. 
Jika server berhasil mengikat ke port-nya, maka objek ServerSocket berhasil dibuat dan server melanjutkan ke langkah berikutnya — menerima koneksi dari klien (pernyataan berikutnya dalam pernyataan try -with-resources):
 clientSocket = serverSocket.accept ();
Metode accept menunggu hingga klien memulai dan meminta koneksi pada host dan port server ini. (Mari kita asumsikan bahwa Anda menjalankan program server knockknockserver.example.com di komputer bernama knockknockserver.example.com .) Dalam contoh ini, server menjalankan nomor port yang ditentukan oleh argumen baris perintah pertama. Ketika suatu koneksi diminta dan berhasil dibuat, metode accept mengembalikan objek Socket baru yang terikat ke port lokal yang sama dan memiliki alamat jarak jauh dan port jarak jauh diatur ke klien. Server dapat berkomunikasi dengan klien melalui Socket baru ini dan terus mendengarkan permintaan koneksi klien pada ServerSocket asli. Versi khusus program ini tidak mendengarkan permintaan koneksi klien yang lebih banyak. Namun, versi program yang dimodifikasi disediakan di Mendukung Banyak Klien . Setelah server berhasil membuat koneksi dengan klien, itu berkomunikasi dengan klien menggunakan kode ini:
 
 
 
 coba (
     // ...
     PrintWriter out =
         PrintWriter baru (clientSocket.getOutputStream (), true);
     BufferedReader di = BufferedReader baru (
         InputStreamReader baru (clientSocket.getInputStream ()));
 ) {
     InputLine string, outputLine;
            
     // Memulai percakapan dengan klien 
      KnockKnockProtocol kkp = KnockKnockProtocol baru (); 
      outputLine = kkp.processInput (null); 
      out.println (outputLine);

     while ((inputLine = in.readLine ())! = null) {
         outputLine = kkp.processInput (inputLine);
         out.println (outputLine);
         if (outputLine.equals ("Bye."))
             istirahat;
     }
 Kode ini melakukan hal berikut: 
  1. Mendapat aliran input dan output soket dan membuka pembaca dan penulis di dalamnya.
  2. Memulai komunikasi dengan klien dengan menulis ke soket (ditunjukkan dengan huruf tebal).
  3. Berkomunikasi dengan klien dengan membaca dari dan menulis ke soket (loop while ).
Langkah 1 sudah tidak asing lagi. Langkah 2 ditunjukkan dengan huruf tebal dan bernilai beberapa komentar. Pernyataan tebal di segmen kode di atas memulai percakapan dengan klien. Kode menciptakan objek KnockKnockProtocol — objek yang melacak lelucon saat ini, keadaan saat ini dalam lelucon, dan sebagainya. Setelah KnockKnockProtocol dibuat, kode tersebut memanggil metode processInput untuk mendapatkan pesan pertama yang dikirimkan server ke klien. Untuk contoh ini, hal pertama yang dikatakan server adalah "Knock! Knock!" Selanjutnya, server menulis informasi ke PrintWriter terhubung ke soket klien, sehingga mengirim pesan ke klien. Langkah 3 dikodekan dalam loop while . Selama klien dan server masih memiliki sesuatu untuk dikatakan satu sama lain, server membaca dari dan menulis ke soket, mengirim pesan bolak-balik antara klien dan server. Server memulai percakapan dengan "Knock! Knock!" jadi setelah itu server harus menunggu klien untuk mengatakan "Siapa di sana?" Akibatnya, loop while berulang pada membaca dari aliran input. Metode readLine menunggu hingga klien merespons dengan menulis sesuatu ke aliran outputnya (aliran input server). Ketika klien merespons, server meneruskan respons klien ke objek KnockKnockProtocol dan meminta objek KnockKnockProtocol untuk jawaban yang sesuai. Server segera mengirim balasan ke klien melalui aliran output yang terhubung ke soket, menggunakan panggilan ke println. Jika respons server yang dihasilkan dari objek KnockKnockServer adalah "Sampai jumpa." ini menunjukkan bahwa klien tidak ingin ada lagi lelucon dan loop berhenti. Java runtime secara otomatis menutup aliran input dan output, soket klien, dan soket server karena semuanya telah dibuat dalam pernyataan try -with-resources.

Protokol Knock Knock

Kelas KnockKnockProtocol mengimplementasikan protokol yang digunakan klien dan server untuk berkomunikasi. Kelas ini melacak di mana klien dan server berada dalam percakapan mereka dan menyajikan respons server terhadap pernyataan klien. Objek KnockKnockProtocol berisi teks dari semua lelucon dan memastikan bahwa klien memberikan respons yang tepat terhadap pernyataan server. Tidak akan ada gunanya membuat klien mengatakan "Dexter siapa?" ketika server mengatakan "Ketuk! Ketuk!" Semua pasangan klien / server harus memiliki beberapa protokol yang dengannya mereka berbicara satu sama lain; jika tidak, data yang bolak-balik akan menjadi tidak berarti. Protokol yang digunakan klien dan server Anda sepenuhnya bergantung pada komunikasi yang diperlukan oleh mereka untuk menyelesaikan tugas.

Klien Knock Knock

Kelas KnockKnockClient mengimplementasikan program klien yang berbicara kepada KnockKnockServer . EchoClient didasarkan pada program EchoClient di bagian sebelumnya, Membaca dari dan Menulis ke Soket dan seharusnya sudah tidak asing lagi bagi Anda. Tetapi bagaimanapun juga, kita akan membahas program dan melihat apa yang terjadi di klien dalam konteks apa yang terjadi di server. Ketika Anda memulai program klien, server seharusnya sudah berjalan dan mendengarkan port, menunggu klien untuk meminta koneksi. Jadi, hal pertama yang dilakukan oleh program klien adalah membuka soket yang terhubung ke server yang berjalan pada nama host dan port yang ditentukan:
 String hostName = args [0];
 int portNumber = Integer.parseInt (args [1]);

 coba (
     Socket kkSocket = Socket baru (hostName, portNumber);
     PrintWriter out = PrintWriter baru (kkSocket.getOutputStream (), true);
     BufferedReader di = BufferedReader baru (
         InputStreamReader baru (kkSocket.getInputStream ()));
 )
Saat membuat soketnya, contoh KnockKnockClient menggunakan nama host argumen baris perintah pertama, nama komputer di jaringan Anda yang menjalankan program server KnockKnockServer . Contoh KnockKnockClient menggunakan argumen baris perintah kedua sebagai nomor port saat membuat soketnya. Ini adalah nomor port jarak jauh — nomor port pada komputer server — dan merupakan port yang didengar KnockKnockServer . Sebagai contoh, perintah berikut menjalankan contoh knockknockserver.example.com dengan knockknockserver.example.com sebagai nama komputer yang menjalankan program server KnockKnockServer dan 4444 sebagai nomor port jarak jauh:
 java KnockKnockClient knockknockserver.example.com 4444
Soket klien terikat ke porta lokal yang tersedia — porta pada komputer klien. Ingatlah bahwa server mendapatkan soket baru juga. Jika Anda menjalankan contoh KnockKnockClient dengan argumen baris perintah pada contoh sebelumnya, maka soket ini terikat ke nomor port lokal 4444 di komputer tempat Anda menjalankan contoh KnockKnockClient . Soket server dan soket klien terhubung. Selanjutnya adalah loop while yang mengimplementasikan komunikasi antara klien dan server. Server berbicara terlebih dahulu, sehingga klien harus mendengarkan terlebih dahulu. Klien melakukan ini dengan membaca dari aliran input yang terpasang ke soket. Jika server berbicara, ia mengatakan "Sampai jumpa." dan klien keluar dari loop. Jika tidak, klien menampilkan teks ke output standar dan kemudian membaca respons dari pengguna, yang mengetik ke input standar. Setelah pengguna mengetik carriage return, klien mengirim teks ke server melalui aliran output yang terpasang ke soket.
 while ((fromServer = in.readLine ())! = null) {
     System.out.println ("Server:" + fromServer);
     if (fromServer.equals ("Bye."))
         istirahat;

     fromUser = stdIn.readLine ();
     if (fromUser! = null) {
         System.out.println ("Klien:" + fromUser);
         out.println (fromUser);
     }
 }
Komunikasi berakhir ketika server bertanya apakah klien ingin mendengar lelucon lain, klien mengatakan tidak, dan server mengatakan "Sampai jumpa." Klien secara otomatis menutup input dan output stream dan socketnya karena dibuat dalam pernyataan try -with-resources.

Menjalankan Program

Anda harus memulai program server terlebih dahulu. Untuk melakukan ini, jalankan program server menggunakan Java interpreter, sama seperti yang Anda lakukan pada aplikasi Java lainnya. Tentukan sebagai argumen baris perintah nomor port tempat program server mendengarkan:
 java KnockKnockServer 4444
Selanjutnya, jalankan program klien. Perhatikan bahwa Anda dapat menjalankan klien di komputer mana saja di jaringan Anda; tidak harus berjalan di komputer yang sama dengan server. Tentukan sebagai argumen baris perintah nama host dan nomor port komputer yang menjalankan program server KnockKnockServer :
 java KnockKnockClient knockknockserver.example.com 4444
Jika Anda terlalu cepat, Anda dapat memulai klien sebelum server memiliki kesempatan untuk menginisialisasi sendiri dan mulai mendengarkan pada port. Jika ini terjadi, Anda akan melihat jejak tumpukan dari klien. Jika ini terjadi, mulai ulang klien. Jika Anda mencoba untuk memulai klien kedua saat klien pertama terhubung ke server, klien kedua hanya hang. Bagian selanjutnya, Mendukung Banyak Klien , berbicara tentang mendukung banyak klien. Ketika Anda berhasil mendapatkan koneksi antara klien dan server, Anda akan melihat teks berikut ini ditampilkan di layar Anda:
 Server: Ketuk!  Ketukan!
Sekarang, Anda harus merespons dengan:
 Siapa disana?
Klien menggemakan apa yang Anda ketikkan dan mengirim teks ke server. Server merespons dengan baris pertama dari salah satu lelucon Knock Knock dalam repertoarnya. Sekarang layar Anda harus berisi ini (teks yang Anda ketik dicetak tebal):
 Server: Ketuk!  Ketukan!
 Siapa disana?
 Klien: Siapa disana?
 Server: Turnip
Sekarang, Anda merespons dengan:
 Lobak siapa?
Sekali lagi, klien menggemakan apa yang Anda ketikkan dan mengirim teks ke server. Server merespons dengan garis pukulan. Sekarang layar Anda harus berisi ini:
 Server: Ketuk!  Ketukan!
 Siapa disana?
 Klien: Siapa disana?
 Server: Turnip
 Lobak siapa?
 Klien: Turnip siapa?
 Server: Turnip panasnya, dingin di sini!  Ingin yang lain?  (y / n)   
Jika Anda ingin mendengar lelucon lain, ketik y ; jika tidak, ketik n . Jika Anda mengetik y , server mulai lagi dengan "Knock! Knock!" Jika Anda mengetik n , server mengatakan "Sampai jumpa." sehingga menyebabkan klien dan server untuk keluar. Jika suatu saat Anda membuat kesalahan pengetikan, objek KnockKnockServer menangkapnya dan server merespons dengan pesan yang mirip dengan ini:
 Server: Anda seharusnya mengatakan "Siapa di sana?"!
Server kemudian memulai lelucon lagi:
 Server: Coba lagi.  Ketukan!  Ketukan!
Perhatikan bahwa objek KnockKnockProtocol khusus tentang pengejaan dan tanda baca tetapi bukan tentang huruf besar.

Mendukung Banyak Klien

Untuk membuat contoh KnockKnockServer sederhana, kami mendesainnya untuk mendengarkan dan menangani satu permintaan koneksi. Namun, beberapa permintaan klien dapat masuk ke port yang sama dan, akibatnya, ke ServerSocket sama. Permintaan koneksi klien diantrekan di port, sehingga server harus menerima koneksi secara berurutan. Namun, server dapat melayani mereka secara bersamaan melalui penggunaan utas — satu utas per setiap koneksi klien. Alur dasar logika di server seperti ini adalah ini:
 while (true) {
     menerima koneksi;
     buat utas untuk berurusan dengan klien;
 } 
 
 
Sekian Dan Terimakasih:)