JavaRush /Java Blog /Random-ID /Kelas Socket dan ServerSocket, atau “Halo, server? Bisaka...
Sergey Simonov
Level 36
Санкт-Петербург

Kelas Socket dan ServerSocket, atau “Halo, server? Bisakah kamu mendengarku?"

Dipublikasikan di grup Random-ID
Pendahuluan: “Di atas meja ada komputer, di belakangnya ada encoder…” Kelas Socket dan ServerSocket, atau “Halo, server?  Bisakah kamu mendengarku?"  - 1 Suatu ketika, salah satu teman sekelas saya memposting lagi hasil belajar Java, berupa screenshot sebuah program baru. Program ini adalah obrolan multi-pengguna. Saat itu saya baru saja memulai perjalanan saya sendiri dalam menguasai pemrograman dalam bahasa ini, tapi saya pasti berkata pada diri sendiri bahwa “Saya menginginkannya!” Waktu berlalu dan, setelah selesai mengerjakan proyek berikutnya sebagai bagian dari memperdalam pengetahuan pemrograman saya, saya teringat kejadian itu dan memutuskan sudah waktunya. Entah bagaimana saya sudah mulai menggali topik ini semata-mata karena penasaran, tetapi dalam buku teks Java utama saya (itu adalah manual lengkap Schildt) hanya disediakan 20 halaman untuk paket java.net. Ini bisa dimengerti - bukunya sudah sangat besar. Ada tabel metode dan konstruktor kelas utama, tapi itu saja. Langkah selanjutnya, tentu saja, adalah Google yang maha kuasa: berjuta-juta artikel berbeda yang menyajikan hal yang sama - dua atau tiga kata tentang soket, dan contoh yang sudah jadi. Pendekatan klasik (setidaknya dalam gaya belajar saya) adalah pertama-tama memahami apa yang saya perlukan dari alat-alat untuk bekerja, apa itu alat-alat itu, mengapa alat-alat itu dibutuhkan, dan baru kemudian, jika solusi untuk masalah tersebut tidak jelas, pilihlah daftar yang sudah jadi, buka mur dan bautnya. Tapi saya menemukan apa itu dan akhirnya menulis obrolan multi-pengguna. Secara lahiriah ternyata seperti ini: Kelas Socket dan ServerSocket, atau “Halo, server?  Bisakah kamu mendengarku?"  - 2Disini saya akan mencoba memberikan pemahaman tentang dasar-dasar aplikasi client-server berbasis soket Java menggunakan contoh desain chat. Dalam kursus Javarash Anda akan melakukan chatting. Ini akan berada pada level yang sangat berbeda, indah, besar, multifungsi. Namun pertama-tama, Anda selalu perlu meletakkan fondasinya, jadi di sini kita perlu mencari tahu apa yang mendasari bagian tersebut. (Jika Anda menemukan kekurangan atau kesalahan, tulis di PM atau di komentar di bawah artikel). Mari kita mulai. Kepala Satu: “Rumah yang...” Untuk menjelaskan bagaimana koneksi jaringan terjadi antara server dan satu klien, mari kita ambil contoh klasik sebuah gedung apartemen. Katakanlah klien perlu membuat koneksi dengan server tertentu. Apa yang perlu diketahui pencari tentang objek pencarian? Ya, alamatnya. Server bukanlah entitas ajaib di cloud, dan oleh karena itu harus berlokasi di mesin tertentu. Dianalogikan dengan sebuah rumah, dimana harus diadakan pertemuan dua pihak yang telah disepakati. Dan untuk bertemu satu sama lain di sebuah gedung apartemen, satu alamat gedung saja tidak cukup, Anda harus menunjukkan nomor apartemen tempat pertemuan akan berlangsung. Demikian pula, dalam satu komputer mungkin terdapat beberapa server sekaligus, dan agar klien dapat menghubungi server tertentu, ia juga perlu menentukan nomor port yang akan digunakan untuk menghubungkan. Jadi, alamat dan nomor portnya. Alamat berarti pengidentifikasi mesin di ruang Internet. Ini bisa berupa nama domain, misalnya "javarush.ru" , atau IP biasa. Pelabuhan- nomor unik yang terhubung dengan soket tertentu (istilah ini akan dibahas nanti), dengan kata lain ditempati oleh layanan tertentu sehingga dapat digunakan untuk menghubunginya. Jadi agar paling sedikit dua objek dapat bertemu di wilayah yang satu (server), pemilik area (server) harus menempati apartemen (pelabuhan) tertentu di atasnya (mobil), dan yang kedua harus menemukan tempat pertemuan tersebut dengan mengetahui alamat rumah (domain atau ip), dan nomor apartemen (port). Bagian Kedua: Perkenalan dengan Socket Di antara konsep dan istilah yang berkaitan dengan bekerja di jaringan, salah satu yang sangat penting adalah Socket. Ini menunjukkan titik di mana koneksi terjadi. Sederhananya, sebuah soket menghubungkan dua program dalam satu jaringan. Kelas Socketmengimplementasikan gagasan soket. Klien dan server akan berkomunikasi melalui saluran input/outputnya: Kelas Socket dan ServerSocket, atau “Halo, server?  Bisakah kamu mendengarku?"  - 3 Kelas ini dideklarasikan di sisi klien, dan server membuatnya kembali, menerima sinyal koneksi. Beginilah cara kerja komunikasi online. Untuk memulainya, berikut adalah kemungkinan konstruktor kelas Socket:
Socket(String Name_хоста, int порт) throws UnknownHostException, IOException
Socket(InetAddress IP-address, int порт) throws UnknownHostException
"host_name" - menyiratkan node jaringan tertentu, alamat IP. Jika kelas soket tidak dapat mengubahnya menjadi alamat asli yang sudah ada, maka pengecualian akan diberikan UnknownHostException. Pelabuhan adalah sebuah pelabuhan. Jika 0 ditentukan sebagai nomor port, sistem itu sendiri akan mengalokasikan port bebas. Pengecualian juga dapat terjadi jika koneksi terputus IOException. Perlu diperhatikan bahwa tipe alamat pada konstruktor kedua adalah InetAddress. Ini berguna, misalnya, ketika Anda perlu menentukan nama domain sebagai alamat. Selain itu, jika sebuah domain berarti beberapa alamat IP, InetAddressAnda dapat menggunakannya untuk mendapatkan serangkaian alamat tersebut. Namun, ini juga berfungsi dengan IP. Anda juga bisa mendapatkan nama host, array byte yang membentuk alamat IP, dll. Kami akan membahasnya lebih jauh, tetapi Anda harus membuka dokumentasi resmi untuk detail selengkapnya. Ketika suatu objek bertipe diinisialisasi Socket, klien yang memilikinya mengumumkan di jaringan bahwa ia ingin terhubung ke server pada alamat dan nomor port tertentu. Di bawah ini adalah metode kelas yang paling sering digunakan Socket: InetAddress getInetAddress()– mengembalikan objek yang berisi data tentang soket. Jika soket tidak terhubung - null int getPort()- mengembalikan port tempat koneksi ke server terjadi int getLocalPort()- mengembalikan port tempat soket terikat. Faktanya adalah bahwa klien dan server dapat "berkomunikasi" pada port yang sama, tetapi port yang mereka ikat bisa sangat berbeda boolean isConnected()- mengembalikan nilai true jika koneksi dibuat void connect(SocketAddress address)- menunjukkan koneksi baru boolean isClosed()- mengembalikan nilai true jika soket ditutup boolean isBound()- mengembalikan nilai true, jika soket benar-benar terikat ke suatu alamat, kelas Socketakan mengimplementasikan antarmuka AutoCloseable, sehingga dapat digunakan dalam try-with-resources. Namun, Anda juga dapat menutup soket dengan cara klasik, menggunakan close(). Kepala Tiga: dan ini adalah ServerSocket Katakanlah kita mendeklarasikan, dalam bentuk kelas Socket, permintaan koneksi di sisi klien. Bagaimana server menebak keinginan kita? Untuk ini, server memiliki kelas seperti ServerSocket, dan metode terima() di dalamnya. Konstruktornya disajikan di bawah ini:
ServerSocket() throws IOException
ServerSocket(int порт) throws IOException
ServerSocket(int порт, int максимум_подключений) throws IOException
ServerSocket(int порт, int максимум_подключений, InetAddress локальный_address) throws IOException
Saat mendeklarasikan, ServerSocket Anda tidak perlu menentukan alamat koneksi, karena komunikasi terjadi di mesin server. Hanya dengan host multi-saluran Anda perlu menentukan IP mana yang terikat pada soket server. Head Three.One: Server Yang Mengatakan Tidak Karena menyediakan program dengan sumber daya lebih dari yang dibutuhkan adalah mahal dan tidak masuk akal, oleh karena itu di konstruktor ServerSocketAnda diminta untuk menyatakan koneksi maksimum yang diterima oleh server selama operasi. Jika tidak ditentukan, maka secara default angka ini akan dianggap sama dengan 50. Ya, secara teori kita bisa berasumsi bahwa ServerSocketini adalah soket yang sama, hanya untuk server. Tapi itu memainkan peran yang sangat berbeda dari kelas Socket. Ini hanya diperlukan pada tahap pembuatan koneksi. Setelah membuat objek tipe, ServerSocketAnda perlu mengetahui bahwa seseorang ingin terhubung ke server. Metode Accept() terhubung di sini. Target menunggu hingga seseorang ingin menyambungkannya, dan ketika ini terjadi, ia mengembalikan objek bertipe Socket, yaitu soket klien yang dibuat ulang. Dan sekarang soket klien telah dibuat di sisi server, komunikasi dua arah dapat dimulai. Membuat objek tipe Socketdi sisi klien dan membuatnya kembali menggunakan ServerSocketsisi server adalah kebutuhan minimum untuk koneksi. Babak Empat: Surat kepada Sinterklas Вопрос: Bagaimana tepatnya klien dan server berkomunikasi? Ответ:Melalui aliran I/O. Apa yang sudah kita miliki? Soket dengan alamat server dan nomor port klien, dan hal yang sama, berkat terima(), di sisi server. Jadi masuk akal untuk berasumsi bahwa mereka akan berkomunikasi melalui soket. Untuk melakukan ini, ada dua metode yang memberikan akses ke aliran InputStreamdan OutputStreamobjek bertipe Socket. Di sini mereka:
InputStream getInputStream()
OutputStream getOutputStream()
Karena membaca dan menulis byte kosong tidak seefisien itu, aliran dapat dibungkus dalam kelas adaptor, di-buffer atau tidak. Misalnya:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
Agar komunikasi menjadi dua arah, operasi tersebut harus dilakukan di kedua sisi. Sekarang Anda dapat mengirim sesuatu menggunakan masuk, dan menerima sesuatu menggunakan keluar, dan sebaliknya. Sebenarnya, ini adalah satu-satunya fungsi class Socket. Dan ya, jangan lupa tentang metode flush() BufferedWriter- ini akan membilas isi buffer. Jika hal ini tidak dilakukan, informasi tidak akan terkirim dan oleh karena itu, tidak akan diterima. Thread penerima juga menunggu indikator akhir baris – “\n”, jika tidak, pesan tidak akan diterima, karena sebenarnya pesan tersebut belum selesai dan belum lengkap. Jika ini terasa tidak nyaman bagi Anda, jangan khawatir, Anda selalu dapat menggunakan kelas PrintWriteryang perlu diselesaikan, tentukan true sebagai argumen kedua, dan kemudian popping dari buffer akan terjadi secara otomatis:
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
Selain itu, tidak perlu menunjukkan akhir baris; kelas ini melakukannya untuk Anda. Tetapi apakah string I/O merupakan batas dari apa yang dapat dilakukan oleh sebuah soket? Tidak, apakah Anda ingin mengirim objek melalui aliran soket? Demi Tuhan. Buat serialnya dan Anda siap melakukannya:
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Kepala Lima: Komunikasi nyata melalui Internet Karena untuk terhubung melalui jaringan nyata dengan alamat IP asli Anda harus memiliki server yang lengkap, dan karena:
  1. Obrolan masa depan kita, sebagai utilitas, tidak memiliki kemampuan seperti itu. Itu hanya dapat membuat koneksi dan menerima/mengirim pesan. Artinya, ia tidak memiliki kemampuan server yang sebenarnya.
  2. Server kami, yang hanya berisi data soket dan aliran I/O, tidak dapat berfungsi sebagai server WEB atau FTP yang sebenarnya, maka hanya dengan ini kami tidak akan dapat terhubung melalui Internet.
Selain itu, kami baru mulai mengembangkan programnya, yang berarti program tersebut belum cukup stabil untuk langsung bekerja dengan jaringan nyata, jadi kami akan menggunakan host lokal sebagai alamat koneksi. Artinya, secara teori, klien dan server tetap tidak akan terhubung dengan cara apa pun kecuali melalui soket, tetapi untuk men-debug program, keduanya akan berada di mesin yang sama, tanpa kontak nyata melalui jaringan. Untuk menunjukkan di konstruktor Socketbahwa alamatnya lokal, ada 2 cara:
  1. Tulis “localhost” sebagai argumen alamat, yang berarti rintisan lokal. "127.0.0.1" juga cocok untuk ini - ini hanyalah bentuk digital dari sebuah rintisan.
  2. Menggunakan InetAddress:
    1. InetAddress.getByName(null)- null menunjuk ke localhost
    2. InetAddress.getByName("localhost")
    3. InetAddress.getByName("127.0.0.1")
Untuk mempermudah, kita akan menggunakan "localhost" bertipe String. Tapi semua opsi lain juga bisa diterapkan. Kepala Enam: Saatnya untuk melakukan percakapan Jadi, kami sudah memiliki semua yang kami perlukan untuk mengimplementasikan sesi percakapan dengan server. Yang tersisa hanyalah menyatukannya: Daftar berikut menunjukkan bagaimana klien terhubung ke server, mengirimkannya satu pesan, dan server, pada gilirannya, mengonfirmasi bahwa ia menerima pesan tersebut dengan menggunakannya sebagai argumen di dalamnya: "Server. Jawa"
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    private static Socket clientSocket; // socket for communication
    private static ServerSocket server; // server socket
    private static BufferedReader in; // socket read stream
    private static BufferedWriter out; // socket write stream

    public static void main(String[] args) {
        try {
            try  {
                server = new ServerSocket(4004); // server socket listening on port 4004
                System.out.println("Server is running!"); // server would be nice
                // announce your launch
                clientSocket = server.accept(); // accept() will wait until
                //someone won't want to connect
                try { // after establishing a connection and recreating the socket for communication with the client, you can go
                    // to create I/O streams.
                    // now we can receive messages
                    in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                    // and send
                    out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

                    String word = in.readLine(); // wait for the client to write something to us
                    System.out.println(word);
                    // without hesitation responds to the client
                    out.write("Hi, this is the Server! I confirm you wrote: " + word + "\n");
                    out.flush(); // push everything out of the buffer

                } finally { // in any case, the socket will be closed
                    clientSocket.close();
                    // streams would also be nice to close
                    in.close();
                    out.close();
                }
            } finally {
                System.out.println("Server closed!");
                    server.close();
            }
        } catch (IOException e) {
            System.err.println(e);
        }
    }
"Klien.java"
import java.io.*;
import java.net.Socket;

public class Client {

    private static Socket clientSocket; // socket for communication
    private static BufferedReader reader; // we need a reader that reads from the console, otherwise
    // do we know what the client wants to say?
    private static BufferedReader in; // socket read stream
    private static BufferedWriter out; // socket write stream

    public static void main(String[] args) {
        try {
            try {
                // address - local host, port - 4004, same as the server
                clientSocket = new Socket("localhost", 4004); // with this line we request
                // the server has access to the connection
                reader = new BufferedReader(new InputStreamReader(System.in));
                // read messages from the server
                in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                // write there
                out = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

                System.out.println("Did you have something to say? Enter it here:");
                // if the connection happened and the threads were successfully created - we can
                // work further and offer the client something to enter
                // if not, an exception will be thrown
                String word = reader.readLine(); // wait for the client to do something
                // will not write to the console
                out.write(word + "\n"); // send a message to the server
                out.flush();
                String serverWord = in.readLine(); // wait for the server to say
                System.out.println(serverWord); // received - display
            } finally { // in any case, you need to close the socket and streams
                System.out.println("The client has been closed...");
                clientSocket.close();
                in.close();
                out.close();
            }
        } catch (IOException e) {
            System.err.println(e);
        }

    }
}
Tentu saja, Anda harus memulai server terlebih dahulu, karena klien akan terhubung ke apa saat startup jika tidak ada sesuatu yang dapat menghubungkannya? :) Outputnya akan seperti ini: /* Apakah Anda ingin mengatakan sesuatu? Masukkan di sini: Halo, server? Bisakah kamu mendengarku? Halo, ini Servernya! Saya konfirmasi, Anda menulis: Halo, server? Bisakah kamu mendengarku? Klien sudah tutup... */ Hore! Kami mengajari server untuk berkomunikasi dengan klien! Agar komunikasi terjadi bukan dalam dua replika, tetapi sebanyak yang Anda suka, cukup bungkus pembacaan dan penulisan utas dalam loop while (benar) dan tunjukkan untuk keluarnya, sesuai dengan pesan tertentu, misalnya “keluar” , siklus terputus, dan program akan berakhir. Kepala Tujuh: Multi-pengguna lebih baik. Fakta bahwa server dapat mendengar kita itu bagus, tetapi akan jauh lebih baik jika kita dapat berkomunikasi dengan seseorang dari jenis kita sendiri. Saya akan melampirkan semua sumber di akhir artikel, jadi di sini saya tidak akan selalu menunjukkan potongan kode yang besar, tetapi penting yang memungkinkan, jika digunakan dengan benar, untuk membuat obrolan multi-pengguna. Jadi, kami ingin dapat berkomunikasi dengan klien lain melalui server. Bagaimana cara melakukannya? Jelas sekali, karena program klien memiliki metodenya sendiri main, artinya program tersebut dapat diluncurkan secara terpisah dari server dan secara paralel dengan klien lain. Apa manfaatnya bagi kita? Entah bagaimana, dengan setiap koneksi baru, server tidak segera melakukan komunikasi, tetapi menulis koneksi ini ke dalam semacam daftar dan mulai menunggu koneksi baru, dan semacam layanan tambahan terlibat dalam komunikasi dengan koneksi tertentu. klien. Dan klien harus menulis ke server dan menunggu respons secara independen satu sama lain. Utas datang untuk menyelamatkan. Katakanlah kita mempunyai kelas yang bertanggung jawab untuk mengingat koneksi baru: Kelas tersebut harus memiliki spesifikasi berikut:
  1. Nomor pelabuhan.
  2. Daftar di mana ia menulis koneksi baru.
  3. Dan ServerSocket, dalam satu salinan (!).
public class Server {

    public static final int PORT = 8080;
    public static LinkedList<ServerSomthing> serverList = new LinkedList<>(); // list of all threads

    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(PORT);
            try {
            while (true) {
                // Blocks until a new connection is made:
                Socket socket = server.accept();
                try {
                    serverList.add(new ServerSomthing(socket)); // add a new connection to the list
                } catch (IOException e) {
                    // If it fails, the socket is closed,
                    // otherwise, the thread will close it when it exits:
                    socket.close();
                }
            }
        } finally {
            server.close();
        }
    }
}
Oke, sekarang setiap soket yang dibuat ulang tidak akan hilang, tetapi akan disimpan di server. Lebih jauh. Setiap pelanggan membutuhkan seseorang untuk mendengarkan. Mari buat thread dengan fungsi server dari bab terakhir.
class ServerSomthing extends Thread {

    private Socket socket; // socket through which the server communicates with the client,
    // except for it - the client and server are not connected in any way
    private BufferedReader in; // socket read stream
    private BufferedWriter out; // socket write stream

    public ServerSomthing(Socket socket) throws IOException {
        this.socket = socket;
        // если потоку ввода/вывода приведут к генерированию исключения, оно пробросится дальше
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        start(); // call run()
    }
    @Override
    public void run() {
        String word;
        try {

            while (true) {
                word = in.readLine();
                if(word.equals("stop")) {
                    break;                }
                for (ServerSomthing vr : Server.serverList) {
                    vr.send(word); // send the received message with
                   // bound client to everyone else including him
                }
            }

        } catch (IOException e) {
        }
    }

    private void send(String msg) {
        try {
            out.write(msg + "\n");
            out.flush();
        } catch (IOException ignored) {}
    }
}
Jadi, di konstruktor thread server, soket harus diinisialisasi melalui mana thread akan berkomunikasi dengan klien tertentu. Juga thread I/O, dan semua hal lain yang Anda perlukan untuk memulai thread langsung dari konstruktor. Oke, tapi apa yang terjadi ketika thread server membaca pesan dari klien? Kirim kembali hanya ke klien Anda? Tidak terlalu efektif. Kami membuat obrolan multi-pengguna, jadi kami memerlukan setiap klien yang terhubung untuk menerima apa yang ditulis oleh satu orang. Anda perlu menggunakan daftar semua thread server yang terkait dengan klien mereka dan mengirim setiap pesan yang dikirim ke thread tertentu sehingga mengirimkannya ke kliennya:
for (ServerSomthing vr : Server.serverList) {
    vr.send(word); // send the received message
    // from the linked client to everyone else, including him
}
private void send(String msg) {
    try {
        out.write(msg + "\n");
        out.flush();
    } catch (IOException ignored) {}
}
Sekarang semua klien akan tahu apa yang dikatakan salah satu dari mereka! Jika Anda tidak ingin pesan dikirim ke orang yang mengirimnya (dia sudah tahu apa yang dia tulis!), cukup saat melakukan iterasi melalui utas, tentukan bahwa saat memproses suatu objek, loop thisakan berpindah ke elemen berikutnya tanpa melakukan tindakan apa pun di dalamnya. Atau, jika Anda mau, kirim pesan ke klien yang menyatakan bahwa pesan berhasil diterima dan dikirim. Semuanya jelas dengan server sekarang. Mari beralih ke klien, atau lebih tepatnya ke klien! Semuanya sama di sana, dengan analogi dengan klien dari bab terakhir, hanya saat membuat sebuah instance, seperti yang ditunjukkan dalam bab ini dengan server, Anda perlu membuat semua yang diperlukan di konstruktor. Namun bagaimana jika saat membuat klien, dia belum sempat memasukkan apa pun, tetapi sesuatu sudah dikirimkan kepadanya? (Misalnya, riwayat korespondensi orang-orang yang sudah terhubung ke obrolan sebelumnya). Jadi siklus di mana pesan terkirim akan diproses harus dipisahkan dari siklus di mana pesan dibaca dari konsol dan dikirim ke server untuk diteruskan ke orang lain. Thread datang untuk menyelamatkan lagi. Tidak ada gunanya membuat klien sebagai thread. Lebih mudah untuk membuat utas dengan loop dalam metode run yang membaca pesan, dan juga, dengan analogi, menulis:
// thread reading messages from the server
private class ReadMsg extends Thread {
    @Override
    public void run() {

        String str;
        try {
            while (true) {
                str = in.readLine(); // waiting for a message from the server
                if (str.equals("stop")) {

                    break; // exit the loop if it's "stop"
                }
                            }
        } catch (IOException e) {

        }
    }
}
// thread sending messages coming from the console to the server
public class WriteMsg extends Thread {

    @Override
    public void run() {
        while (true) {
            String userWord;
            try {
               userWord = inputUser.readLine(); // messages from the console
                if (userWord.equals("stop")) {
                    out.write("stop" + "\n");
                    break; // exit the loop if it's "stop"
                } else {
                    out.write(userWord + "\n"); // send to the server
                }
                out.flush(); // clean up
            } catch (IOException e) {

            }

        }
    }
}
Di konstruktor klien Anda hanya perlu memulai utas ini. Bagaimana cara menutup sumber daya klien dengan benar jika dia ingin keluar? Apakah saya perlu menutup sumber daya thread server? Untuk melakukan ini, kemungkinan besar Anda perlu membuat metode terpisah yang dipanggil saat keluar dari loop pesan. Di sana Anda harus menutup soket dan aliran I/O. Sinyal akhir sesi yang sama untuk klien tertentu harus dikirim ke thread servernya, yang harus melakukan hal yang sama dengan soketnya dan menghapus dirinya sendiri dari daftar thread di kelas server utama. Kepala Delapan: Tidak ada batasan untuk kesempurnaan Anda dapat terus-menerus menciptakan fitur-fitur baru untuk meningkatkan proyek Anda. Tapi apa sebenarnya yang harus ditransfer ke klien yang baru terhubung? Saya pikir sepuluh peristiwa terakhir terjadi sebelum kedatangannya. Untuk melakukan ini, Anda perlu membuat kelas di mana tindakan terakhir dengan thread server mana pun akan dimasukkan ke dalam daftar yang dideklarasikan, dan jika daftar sudah penuh (yaitu sudah ada 10), hapus yang pertama dan tambahkan yang terakhir datang. Agar konten daftar ini dapat diterima oleh koneksi baru, saat membuat utas server, di aliran keluaran, Anda perlu mengirimkannya ke klien. Bagaimana cara melakukannya? Misalnya seperti ini:
public void printStory(BufferedWriter writer) {
// ...
}
Utas server telah membuat aliran dan dapat meneruskan aliran keluaran sebagai argumen. Selanjutnya, Anda hanya perlu meneruskan semua yang perlu ditransfer ke klien baru dalam siklus pencarian. Kesimpulan: Ini hanyalah dasar-dasarnya, dan kemungkinan besar arsitektur obrolan ini tidak akan berfungsi saat membuat aplikasi sebenarnya. Program ini dibuat untuk tujuan pendidikan dan atas dasar itu saya menunjukkan bagaimana Anda dapat membuat klien berkomunikasi dengan server (dan sebaliknya), bagaimana melakukan ini untuk beberapa koneksi, dan, tentu saja, bagaimana hal ini diatur pada soket. Sumber-sumbernya disusun ulang di bawah ini, dan kode sumber program yang dianalisis juga dilampirkan. Ini adalah pengalaman pertama saya menulis artikel) Terima kasih atas perhatiannya :)
  1. Berpikir dalam Java Enterprise, oleh Bruce Eckel et. Al. 2003
  2. Java 8, Panduan Lengkap, Herbert Schildt, edisi ke-9, 2017 (Bab 22)
  3. Pemrograman soket di artikel Java tentang soket
  4. Soket di dokumentasi resmi
  5. ServerSocket dalam dokumentasi resmi
  6. sumber di GitHub
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION