Pemrograman soket di Java: Sebuah tutorial

Tutorial ini adalah pengantar pemrograman soket di Java, dimulai dengan contoh klien-server sederhana yang mendemonstrasikan fitur dasar Java I / O. Anda akan diperkenalkan ke java.io paket asli  dan NIO, java.nioAPI I / O ( ) non-pemblokiran yang diperkenalkan di Java 1.4. Terakhir, Anda akan melihat contoh yang mendemonstrasikan jaringan Java seperti yang diimplementasikan mulai dari Java 7, di NIO.2.

Pemrograman soket bermuara pada dua sistem yang berkomunikasi satu sama lain. Umumnya, komunikasi jaringan hadir dalam dua bentuk: Transport Control Protocol (TCP) dan User Datagram Protocol (UDP). TCP dan UDP digunakan untuk tujuan yang berbeda dan keduanya memiliki batasan unik:

  • TCP adalah protokol yang relatif sederhana dan andal yang memungkinkan klien untuk membuat koneksi ke server dan dua sistem untuk berkomunikasi. Di TCP, setiap entitas mengetahui bahwa muatan komunikasinya telah diterima.
  • UDP adalah protokol tanpa koneksi dan bagus untuk skenario di mana Anda tidak perlu setiap paket tiba di tujuannya, seperti streaming media.

Untuk menghargai perbedaan antara TCP dan UDP, pertimbangkan apa yang akan terjadi jika Anda melakukan streaming video dari situs web favorit Anda dan frame tersebut turun. Apakah Anda lebih suka klien memperlambat film Anda untuk menerima frame yang hilang atau Anda lebih suka video terus diputar? Protokol streaming video biasanya memanfaatkan UDP. Karena TCP menjamin pengiriman, itu adalah protokol pilihan untuk HTTP, FTP, SMTP, POP3, dan sebagainya.

Dalam tutorial ini, saya memperkenalkan Anda pada pemrograman soket di Java. Saya menyajikan serangkaian contoh klien-server yang mendemonstrasikan fitur dari kerangka I / O Java asli, kemudian secara bertahap maju untuk menggunakan fitur yang diperkenalkan di NIO.2.

Soket Java jadul

Dalam implementasi sebelum NIO, kode soket klien TCP Java ditangani oleh java.net.Socketkelas. Kode berikut membuka koneksi ke server:

 Socket socket = Socket baru (server, port); 

Setelah socketinstance kami terhubung ke server, kami dapat mulai mendapatkan aliran input dan output ke server. Arus input digunakan untuk membaca data dari server sedangkan arus keluaran digunakan untuk menulis data ke server. Kita dapat menjalankan metode berikut untuk mendapatkan aliran input dan output:

InputStream in = socket.getInputStream (); OutputStream out = socket.getOutputStream ();

Karena ini adalah aliran biasa, aliran yang sama yang akan kita gunakan untuk membaca dari dan menulis ke file, kita dapat mengonversinya menjadi bentuk yang paling sesuai dengan kasus penggunaan kita. Misalnya, kita dapat membungkus OutputStreamdengan a PrintStreamsehingga kita dapat dengan mudah menulis teks dengan metode seperti println(). Untuk contoh lain, kita bisa membungkusnya InputStreamdengan a BufferedReader, via an InputStreamReader, agar mudah membaca teks dengan metode seperti readLine().

download Unduh kode sumber Kode sumber untuk "Pemrograman soket di Java: Tutorial." Dibuat oleh Steven Haines untuk JavaWorld.

Contoh klien soket Java

Mari bekerja melalui contoh singkat yang mengeksekusi HTTP GET terhadap server HTTP. HTTP lebih canggih daripada izin contoh kita, tetapi kita dapat menulis kode klien untuk menangani kasus paling sederhana: meminta sumber daya dari server dan server mengembalikan respons dan menutup aliran. Kasus ini membutuhkan langkah-langkah berikut:

  1. Buat soket ke server web yang mendengarkan pada port 80.
  2. Dapatkan PrintStreamke server dan kirim permintaan GET PATH HTTP/1.0, di mana PATHsumber daya yang diminta di server. Misalnya, jika kita ingin membuka root situs web maka jalurnya adalah /.
  3. Dapatkan InputStreamke server, bungkus dengan a BufferedReaderdan baca tanggapan baris demi baris.

Kode 1 menunjukkan kode sumber untuk contoh ini.

Daftar 1. SimpleSocketClientExample.java

paket com.geekcap.javaworld.simplesocketclient; import java.io.BufferedReader; impor java.io.InputStreamReader; impor java.io.PrintStream; import java.net.Socket; kelas publik SimpleSocketClientExample {public static void main (String [] args) {if (args.length <2) {System.out.println ("Usage: SimpleSocketClientExample"); System.exit (0); } Server string = args [0]; Jalur string = args [1]; System.out.println ("Memuat konten URL:" + server); coba {// Hubungkan ke server Socket socket = new Socket (server, 80); // Buat aliran input dan output untuk membaca dan menulis ke server PrintStream out = new PrintStream (socket.getOutputStream ()); BufferedReader di = BufferedReader baru (InputStreamReader baru (socket.getInputStream ())); // Ikuti protokol HTTP GET HTTP / 1.0 diikuti dengan baris kosong keluar.println ("GET" + path + "HTTP / 1.0"); out.println (); // Baca data dari server sampai kita selesai membaca dokumen String line = in.readLine (); while (line! = null) {System.out.println (line); line = in.readLine (); } // Tutup aliran kami di.close (); out.close (); socket.close (); } catch (Exception e) {e.printStackTrace (); }}}

Kode 1 menerima dua argumen baris perintah: server yang akan disambungkan (dengan asumsi kita terhubung ke server pada port 80) dan sumber daya yang akan diambil. Ini menciptakan Socketyang menunjuk ke server dan secara eksplisit menentukan port 80. Kemudian menjalankan perintah:

DAPATKAN PATH HTTP / 1.0 

Sebagai contoh:

GET / HTTP / 1.0 

Apa yang baru saja terjadi?

Saat Anda mengambil halaman web dari server web, seperti www.google.com, klien HTTP menggunakan server DNS untuk menemukan alamat server: ini dimulai dengan menanyakan server domain tingkat atas untuk comdomain di mana server nama domain otoritatif untuk www.google.com. Kemudian ia meminta server nama-domain untuk alamat IP (atau alamat) www.google.com. Selanjutnya, ini membuka soket ke server itu pada port 80. (Atau, jika Anda ingin menentukan port yang berbeda, Anda dapat melakukannya dengan menambahkan titik dua diikuti dengan nomor port, misalnya:. :8080) Akhirnya, klien HTTP mengeksekusi metode HTTP yang ditentukan, seperti GET, POST, PUT, DELETE, HEAD, atau OPTI/ONS. Setiap metode memiliki sintaksnya sendiri. Seperti yang ditunjukkan pada potongan kode di atas, GETmetode ini membutuhkan jalur yang diikuti olehHTTP/version numberdan garis kosong. Jika kami ingin menambahkan header HTTP, kami dapat melakukannya sebelum memasuki baris baru.

Dalam Listing 1, kami mengambil OutputStreamdan membungkusnya PrintStreamsehingga kami dapat lebih mudah menjalankan perintah berbasis teks kami. Kode kita memperoleh InputStream, membungkusnya dengan InputStreamReader, yang mengubahnya menjadi a Reader, dan kemudian membungkusnya dengan a BufferedReader. Kami menggunakan PrintStreamuntuk menjalankan GETmetode kami dan kemudian menggunakan BufferedReaderuntuk membaca tanggapan baris demi baris sampai kami menerima nulltanggapan, yang menunjukkan bahwa soket telah ditutup.

Sekarang jalankan kelas ini dan berikan argumen berikut:

java com.geekcap.javaworld.simplesocketclient.SimpleSocketClientExample www.javaworld.com / 

Anda akan melihat keluaran yang mirip dengan yang di bawah ini:

Memuat konten URL: www.javaworld.com HTTP / 1.1 200 OK Tanggal: Minggu, 21 Sep 2014 22:20:13 GMT Server: Apache X-Gas_TTL: 10 Cache-Control: max-age = 10 X-GasHost: gas2 .usw X-Memasak-Dengan: Bensin-Lokal X-Bensin-Umur: 8 Panjang Isi: 168 Terakhir-Dimodifikasi: Sel, 24 Jan 2012 00:09:09 GMT Etag: "60001b-a8-4b73af4bf3340" Jenis-Isi : text / html Bervariasi: Sambungan Terima-Penyandiaksaraan: tutup Halaman Uji Bensin

Keberhasilan

Output ini menunjukkan halaman pengujian di situs JavaWorld. Itu menjawab kembali bahwa itu berbicara HTTP versi 1.1 dan tanggapannya adalah 200 OK.

Contoh server soket Java

Kami telah membahas sisi klien dan untungnya aspek komunikasi dari sisi server juga mudah. Dari perspektif yang sederhana, prosesnya adalah sebagai berikut:

  1. Buat ServerSocket, tentukan port untuk mendengarkan.
  2. Invoke yang ServerSocket's accept()metode untuk mendengarkan pada port dikonfigurasi untuk koneksi client.
  3. Ketika klien terhubung ke server, accept()metode mengembalikan Socketmelalui mana server dapat berkomunikasi dengan klien. Ini adalah Socketkelas yang sama yang kami gunakan untuk klien kami, jadi prosesnya sama: dapatkan InputStreamuntuk dibaca dari klien dan OutputStreamtulis ke klien.
  4. Jika server Anda perlu skalabel, Anda harus meneruskan Socketke utas lain untuk diproses sehingga server Anda dapat terus mendengarkan koneksi tambahan.
  5. Sebut ServerSocket's accept()metode lagi untuk mendengarkan koneksi lain.

As you'll soon see, NIO's handling of this scenario would be a bit different. For now, though, we can directly create a ServerSocket by passing it a port to listen on (more about ServerSocketFactorys in the next section):

 ServerSocket serverSocket = new ServerSocket( port ); 

And now we can accept incoming connections via the accept() method:

 Socket socket = serverSocket.accept(); // Handle the connection ... 

Multithreaded programming with Java sockets

Listing 2, below, puts all of the server code so far together into a slightly more robust example that uses threads to handle multiple requests. The server shown is an echo server, meaning that it echoes back any message it receives.

While the example in Listing 2 isn't complicated it does anticipate some of what's coming up in the next section on NIO. Pay special attention to the amount of threading code we have to write in order to build a server that can handle multiple simultaneous requests.

Listing 2. SimpleSocketServer.java

package com.geekcap.javaworld.simplesocketclient; import java.io.BufferedReader; import java.io.I/OException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class SimpleSocketServer extends Thread { private ServerSocket serverSocket; private int port; private boolean running = false; public SimpleSocketServer( int port ) { this.port = port; } public void startServer() { try { serverSocket = new ServerSocket( port ); this.start(); } catch (I/OException e) { e.printStackTrace(); } } public void stopServer() { running = false; this.interrupt(); } @Override public void run() { running = true; while( running ) { try { System.out.println( "Listening for a connection" ); // Call accept() to receive the next connection Socket socket = serverSocket.accept(); // Pass the socket to the RequestHandler thread for processing RequestHandler requestHandler = new RequestHandler( socket ); requestHandler.start(); } catch (I/OException e) { e.printStackTrace(); } } } public static void main( String[] args ) { if( args.length == 0 ) { System.out.println( "Usage: SimpleSocketServer " ); System.exit( 0 ); } int port = Integer.parseInt( args[ 0 ] ); System.out.println( "Start server on port: " + port ); SimpleSocketServer server = new SimpleSocketServer( port ); server.startServer(); // Automatically shutdown in 1 minute try { Thread.sleep( 60000 ); } catch( Exception e ) { e.printStackTrace(); } server.stopServer(); } } class RequestHandler extends Thread { private Socket socket; RequestHandler( Socket socket ) { this.socket = socket; } @Override public void run() { try { System.out.println( "Received a connection" ); // Get input and output streams BufferedReader in = new BufferedReader( new InputStreamReader( socket.getInputStream() ) ); PrintWriter out = new PrintWriter( socket.getOutputStream() ); // Write out our header to the client out.println( "Echo Server 1.0" ); out.flush(); // Echo lines back to the client until the client closes the connection or we receive an empty line String line = in.readLine(); while( line != null && line.length() > 0 ) { out.println( "Echo: " + line ); out.flush(); line = in.readLine(); } // Close our connection in.close(); out.close(); socket.close(); System.out.println( "Connection closed" ); } catch( Exception e ) { e.printStackTrace(); } } }