JavaRush /Blog Java /Random-MS /Kelas Socket dan ServerSocket, atau “Hello, pelayan? Bole...
Sergey Simonov
Tahap
Санкт-Петербург

Kelas Socket dan ServerSocket, atau “Hello, pelayan? Bolehkah awak mendengar saya?"

Diterbitkan dalam kumpulan
Pengenalan: “Ada komputer di atas meja, di belakangnya ada pengekod...” Kelas Socket dan ServerSocket, atau “Hello, pelayan?  Bolehkah awak mendengar saya?"  - 1 Pada suatu masa dahulu, salah seorang rakan sekelas saya menyiarkan satu lagi hasil kajiannya tentang Java, dalam bentuk tangkapan skrin program baharu. Program ini ialah sembang berbilang pengguna. Pada masa itu saya baru memulakan perjalanan saya sendiri dalam menguasai pengaturcaraan dalam bahasa ini, tetapi saya pasti menyatakan kepada diri saya sendiri bahawa "Saya mahukannya!" Masa berlalu dan, setelah selesai mengerjakan projek seterusnya sebagai sebahagian daripada mendalami pengetahuan pengaturcaraan saya, saya teringat kejadian itu dan memutuskan sudah tiba masanya. Entah bagaimana saya sudah mula menggali topik ini semata-mata kerana ingin tahu, tetapi dalam buku teks Java utama saya (ia adalah manual lengkap Schildt) hanya 20 halaman disediakan untuk pakej java.net. Ini boleh difahami - buku itu sudah sangat besar. Terdapat jadual kaedah dan pembina kelas utama, tetapi itu sahaja. Langkah seterusnya, sudah tentu, Google yang maha kuasa: berjuta-juta pelbagai artikel di mana perkara yang sama dibentangkan - dua atau tiga perkataan tentang soket, dan contoh siap sedia. Pendekatan klasik (sekurang-kurangnya dalam gaya pengajian saya) adalah untuk terlebih dahulu memahami alat yang saya perlukan untuk bekerja, apakah alat itu, mengapa ia diperlukan, dan hanya kemudian, jika penyelesaian kepada masalah itu tidak jelas, tinjau dengan yang sedia. -membuat penyenaraian, membuka nat dan bolt. Tetapi saya mengetahui apa itu dan akhirnya menulis sembang berbilang pengguna. Secara luaran, ternyata seperti ini: Kelas Socket dan ServerSocket, atau “Hello, pelayan?  Bolehkah awak mendengar saya?"  - 2Di ​​sini saya akan cuba memberi anda pemahaman tentang asas aplikasi pelayan pelanggan berdasarkan soket Java menggunakan contoh reka bentuk sembang. Dalam kursus Javarash anda akan melakukan sembang. Ia akan berada pada tahap yang sama sekali berbeza, cantik, besar, pelbagai fungsi. Tetapi pertama sekali, anda sentiasa perlu meletakkan asas, jadi di sini kita perlu memikirkan apa yang mendasari bahagian tersebut. (Jika anda mendapati sebarang kekurangan atau kesilapan, tulis di PM atau di komen di bawah artikel). Mari kita mulakan. Ketua Satu: "Rumah yang..." Untuk menerangkan cara sambungan rangkaian berlaku antara pelayan dan satu pelanggan, mari kita ambil contoh klasik bangunan apartmen sekarang. Katakan pelanggan perlu mewujudkan sambungan dengan pelayan tertentu. Apakah yang perlu diketahui oleh pencari tentang objek carian? Ya, alamatnya. Pelayan bukan entiti ajaib di awan, dan oleh itu ia mesti terletak pada mesin tertentu. Sebagai analogi dengan sebuah rumah, di mana pertemuan dua pihak yang dipersetujui harus diadakan. Dan untuk mencari satu sama lain di bangunan apartmen, satu alamat bangunan tidak mencukupi; anda mesti menunjukkan bilangan apartmen di mana mesyuarat akan berlangsung. Begitu juga, pada satu komputer boleh terdapat beberapa pelayan sekaligus, dan agar pelanggan menghubungi yang tertentu, dia juga perlu menentukan nombor port yang melaluinya sambungan akan berlaku. Jadi, alamat dan nombor port. Alamat bermaksud pengecam mesin dalam ruang Internet. Ia boleh menjadi nama domain, contohnya, "javarush.ru" , atau IP biasa. Pelabuhan- nombor unik yang dikaitkan dengan soket tertentu (istilah ini akan dibincangkan kemudian), dengan kata lain, ia diduduki oleh perkhidmatan tertentu supaya ia boleh digunakan untuk menghubunginya. Jadi agar sekurang-kurangnya dua objek bertemu di wilayah satu (pelayan), pemilik kawasan (pelayan) mesti menduduki apartmen (pelabuhan) tertentu di atasnya (kereta), dan yang kedua mesti mencari tempat pertemuan mengetahui. alamat rumah (domain atau ip ), dan nombor pangsapuri (port). Ketua Dua: Meet Socket Antara konsep dan istilah yang berkaitan dengan bekerja pada rangkaian, satu yang sangat penting ialah Socket. Ia menandakan titik di mana sambungan berlaku. Ringkasnya, soket menghubungkan dua program pada rangkaian. Kelas Socketmelaksanakan idea soket. Pelanggan dan pelayan akan berkomunikasi melalui saluran input/outputnya: Kelas Socket dan ServerSocket, atau “Hello, pelayan?  Bolehkah awak mendengar saya?"  - 3 Kelas ini diisytiharkan pada bahagian klien, dan pelayan menciptanya semula, menerima isyarat sambungan. Beginilah cara komunikasi dalam talian berfungsi. Sebagai permulaan, berikut ialah pembina kelas yang mungkin Socket:
Socket(String Name_хоста, int порт) throws UnknownHostException, IOException
Socket(InetAddress IP-address, int порт) throws UnknownHostException
“host_name” - membayangkan nod rangkaian tertentu, alamat IP. Jika kelas soket tidak dapat menukarnya kepada alamat sebenar yang sedia ada, maka pengecualian akan dibuang UnknownHostException. Pelabuhan adalah pelabuhan. Jika 0 dinyatakan sebagai nombor port, sistem itu sendiri akan memperuntukkan port percuma. Pengecualian juga mungkin berlaku jika sambungan terputus IOException. Perlu diingatkan bahawa jenis alamat dalam pembina kedua ialah InetAddress. Ia datang untuk menyelamatkan, sebagai contoh, apabila anda perlu menentukan nama domain sebagai alamat. Selain itu, apabila domain bermaksud beberapa alamat IP, InetAddressanda boleh menggunakannya untuk mendapatkan tatasusunan daripadanya. Walau bagaimanapun, ia juga berfungsi dengan IP. Anda juga boleh mendapatkan nama hos, tatasusunan bait yang membentuk alamat IP, dsb. Kami akan menyentuhnya sedikit lagi, tetapi anda perlu pergi ke dokumentasi rasmi untuk butiran penuh. Apabila objek jenis dimulakan Socket, klien yang menjadi miliknya mengumumkan pada rangkaian bahawa ia ingin menyambung ke pelayan pada alamat dan nombor port tertentu. Di bawah ialah kaedah kelas yang paling kerap digunakan Socket: InetAddress getInetAddress()– mengembalikan objek yang mengandungi data tentang soket. Jika soket tidak disambungkan - null int getPort()- mengembalikan port di mana sambungan ke pelayan berlaku int getLocalPort()- mengembalikan port ke mana soket terikat. Hakikatnya ialah pelanggan dan pelayan boleh "berkomunikasi" pada satu port, tetapi port yang diikatnya boleh berbeza sama sekali boolean isConnected()- kembali benar jika sambungan diwujudkan void connect(SocketAddress address)- menunjukkan sambungan baru boolean isClosed()- kembali benar, jika soket ditutup boolean isBound()- mengembalikan benar, jika soket sebenarnya terikat pada alamat, kelas Socketmelaksanakan antara muka AutoCloseable, jadi ia boleh digunakan dalam try-with-resources. Walau bagaimanapun, anda juga boleh menutup soket dengan cara klasik, menggunakan close(). Ketua Tiga: dan ini ialah ServerSocket Katakan kami mengisytiharkan, dalam bentuk kelas Socket, permintaan sambungan pada bahagian klien. Bagaimanakah pelayan akan meneka keinginan kita? Untuk ini, pelayan mempunyai kelas seperti ServerSocket, dan kaedah accept() di dalamnya. Pembinanya dibentangkan di bawah:
ServerSocket() throws IOException
ServerSocket(int порт) throws IOException
ServerSocket(int порт, int максимум_подключений) throws IOException
ServerSocket(int порт, int максимум_подключений, InetAddress локальный_address) throws IOException
Apabila mengisytiharkan, ServerSocket anda tidak perlu menentukan alamat sambungan, kerana komunikasi berlaku pada mesin pelayan. Hanya dengan hos berbilang saluran anda perlu menentukan IP mana soket pelayan terikat. Ketua Tiga.Satu: Pelayan Yang Mengatakan Tidak Memandangkan menyediakan program dengan lebih banyak sumber daripada yang diperlukan adalah mahal dan tidak munasabah, oleh itu dalam pembina ServerSocketanda diminta untuk mengisytiharkan sambungan maksimum yang diterima oleh pelayan semasa operasi. Jika ia tidak dinyatakan, maka secara lalai nombor ini akan dianggap sama dengan 50. Ya, secara teori kita boleh menganggap bahawa ServerSocketini adalah soket yang sama, hanya untuk pelayan. Tetapi ia memainkan peranan yang sama sekali berbeza daripada kelas Socket. Ia hanya diperlukan pada peringkat penciptaan sambungan. Setelah mencipta objek jenis, ServerSocketanda perlu mengetahui bahawa seseorang ingin menyambung ke pelayan. Kaedah accept() disambungkan di sini. Sasaran menunggu sehingga seseorang mahu menyambung kepadanya, dan apabila ini berlaku ia mengembalikan objek jenis Socket, iaitu, soket klien yang dicipta semula. Dan kini bahawa soket pelanggan telah dibuat di bahagian pelayan, komunikasi dua hala boleh dimulakan. Mencipta objek jenis Socketpada bahagian klien dan menciptanya semula menggunakan ServerSocketbahagian pelayan adalah minimum yang diperlukan untuk sambungan. Ketua Empat: Surat kepada Santa Claus Вопрос: Bagaimana sebenarnya klien dan pelayan berkomunikasi? Ответ:Melalui aliran I/O. Apa yang kita sudah ada? Soket dengan alamat pelayan dan nombor port pelanggan, dan perkara yang sama, terima kasih kepada accept(), pada bahagian pelayan. Jadi adalah munasabah untuk menganggap bahawa mereka akan berkomunikasi melalui soket. Untuk melakukan ini, terdapat dua kaedah yang memberikan akses kepada aliran InputStreamdan OutputStreamobjek jenis Socket. Di sini mereka:
InputStream getInputStream()
OutputStream getOutputStream()
Memandangkan membaca dan menulis bait kosong tidak begitu cekap, strim boleh dibungkus dalam kelas penyesuai, ditimbal atau tidak. Sebagai contoh:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
Untuk komunikasi menjadi dua arah, operasi sedemikian mesti dilakukan pada kedua-dua belah pihak. Kini anda boleh menghantar sesuatu menggunakan masuk, dan menerima sesuatu menggunakan keluar, dan sebaliknya. Sebenarnya, ini boleh dikatakan satu-satunya fungsi kelas Socket. Dan ya, jangan lupa tentang kaedah flush() BufferedWriter- ia mengepam kandungan penimbal. Jika ini tidak dilakukan, maklumat tidak akan dihantar dan, oleh itu, tidak akan diterima. Urutan penerima juga menunggu penunjuk akhir talian – “\n”, jika tidak, mesej tidak akan diterima, kerana sebenarnya mesej itu tidak lengkap dan tidak lengkap. Jika ini kelihatan menyusahkan anda, jangan risau, anda sentiasa boleh menggunakan class PrintWriter, yang perlu digulung, tentukan benar sebagai hujah kedua, dan kemudian muncul dari penimbal akan berlaku secara automatik:
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
Selain itu, tidak perlu menunjukkan penghujung baris; kelas ini melakukannya untuk anda. Tetapi adakah rentetan I/O adalah had yang boleh dilakukan oleh soket? Tidak, adakah anda mahu menghantar objek melalui aliran soket? Demi Allah. Sirikan mereka dan anda boleh pergi:
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Ketua Lima: Komunikasi sebenar melalui Internet Oleh kerana untuk menyambung melalui rangkaian sebenar dengan alamat IP sebenar anda perlu mempunyai pelayan yang lengkap, dan sejak:
  1. Sembang masa depan kami, sebagai utiliti, tidak mempunyai kebolehan sedemikian. Ia hanya boleh mewujudkan sambungan dan menerima/menghantar mesej. Iaitu, ia tidak mempunyai keupayaan pelayan sebenar.
  2. Pelayan kami, yang mengandungi hanya data soket dan aliran I/O, tidak boleh berfungsi sebagai pelayan WEB atau FTP sebenar, maka dengan ini sahaja kami tidak akan dapat menyambung melalui Internet.
Selain itu, kami baru mula membangunkan program ini, yang bermaksud bahawa ia tidak cukup stabil untuk berfungsi dengan segera dengan rangkaian sebenar, jadi kami akan menggunakan hos tempatan sebagai alamat sambungan. Iaitu, secara teori, pelanggan dan pelayan masih tidak akan disambungkan dalam apa jua cara kecuali melalui soket, tetapi untuk penyahpepijatan program mereka akan berada pada mesin yang sama, tanpa hubungan sebenar melalui rangkaian. Untuk menunjukkan dalam pembina Socketbahawa alamat adalah setempat, terdapat 2 cara:
  1. Tulis "localhost" sebagai hujah alamat, yang bermaksud stub tempatan. “127.0.0.1” juga sesuai untuk ini - ini hanyalah bentuk digital rintisan.
  2. Menggunakan InetAddress:
    1. InetAddress.getByName(null)- mata nol kepada localhost
    2. InetAddress.getByName("localhost")
    3. InetAddress.getByName("127.0.0.1")
Untuk kesederhanaan, kami akan menggunakan "localhost" jenis String. Tetapi semua pilihan lain juga boleh dilaksanakan. Ketua Enam: Sudah tiba masanya untuk perbualan Jadi, kami sudah mempunyai semua yang kami perlukan untuk melaksanakan sesi perbualan dengan pelayan. Apa yang tinggal ialah meletakkannya bersama-sama: Penyenaraian berikut menunjukkan cara klien menyambung ke pelayan, menghantar satu mesej, dan pelayan pula mengesahkan bahawa ia menerima mesej menggunakannya sebagai hujah dalam: "Pelayan. java"
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);
        }
    }
"Client.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);
        }

    }
}
Sudah tentu, anda harus memulakan pelayan terlebih dahulu, kerana apa yang akan disambungkan oleh pelanggan pada permulaan jika tiada sesuatu yang akan menyambungkannya? :) Outputnya akan menjadi seperti ini: /* Adakah anda ingin mengatakan sesuatu? Masukkan di sini: Hello, pelayan? Bolehkah anda mendengar saya? Hello, ini adalah Pelayan! Saya mengesahkan, anda menulis: Hello, pelayan? Bolehkah anda mendengar saya? Pelanggan telah ditutup... */ Hore! Kami mengajar pelayan untuk berkomunikasi dengan pelanggan! Supaya komunikasi tidak berlaku dalam dua replika, tetapi seberapa banyak yang anda suka, cukup bungkus pembacaan dan penulisan benang dalam gelung sementara (benar) dan tandakan untuk keluar itu, menurut mesej tertentu, sebagai contoh, "keluar" , kitaran telah terganggu dan program akan tamat. Ketua Tujuh: Berbilang pengguna adalah lebih baik. Hakikat bahawa pelayan boleh mendengar kami adalah bagus, tetapi adalah lebih baik jika kami boleh berkomunikasi dengan seseorang daripada jenis kami sendiri. Saya akan melampirkan semua sumber pada akhir artikel, jadi di sini saya akan menunjukkan tidak selalunya besar, tetapi kepingan kod penting yang akan memungkinkan, jika digunakan dengan betul, untuk mencipta sembang berbilang pengguna. Jadi, kami mahu dapat berkomunikasi dengan beberapa pelanggan lain melalui pelayan. Bagaimana hendak melakukannya? Jelas sekali, kerana program klien mempunyai kaedahnya sendiri main, ini bermakna ia boleh dilancarkan secara berasingan daripada pelayan dan selari dengan pelanggan lain. Apa yang diberikan ini kepada kita? Entah bagaimana, adalah perlu bahawa dengan setiap sambungan baru pelayan tidak segera pergi ke komunikasi, tetapi menulis sambungan ini ke dalam beberapa jenis senarai dan terus menunggu sambungan baru, dan beberapa jenis perkhidmatan tambahan terlibat dalam komunikasi dengan tertentu. pelanggan. Dan pelanggan mesti menulis kepada pelayan dan menunggu jawapan secara berasingan antara satu sama lain. Benang datang untuk menyelamatkan. Katakan kita mempunyai kelas yang bertanggungjawab untuk mengingati sambungan baharu: Ia sepatutnya mempunyai yang berikut dinyatakan:
  1. Nombor port.
  2. Senarai di mana ia menulis sambungan baharu.
  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();
        }
    }
}
Okay, kini setiap soket yang dicipta semula tidak akan hilang, tetapi akan disimpan pada pelayan. Selanjutnya. Setiap pelanggan memerlukan seseorang untuk mendengar. Mari kita buat benang dengan fungsi pelayan 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, dalam pembina benang pelayan, soket mesti dimulakan di mana benang akan berkomunikasi dengan klien tertentu. Juga benang I/O, dan segala-galanya yang anda perlukan untuk memulakan benang terus dari pembina. Baiklah, tetapi apa yang berlaku apabila urutan pelayan membaca mesej daripada pelanggan? Hantar balik hanya kepada pelanggan anda? Tidak begitu berkesan. Kami sedang membuat sembang berbilang pengguna, jadi kami memerlukan setiap pelanggan yang disambungkan untuk menerima apa yang ditulis oleh seseorang. Anda perlu menggunakan senarai semua urutan pelayan yang dikaitkan dengan pelanggan mereka dan menghantar setiap mesej yang dihantar ke urutan tertentu supaya ia menghantarnya kepada pelanggannya:
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 pelanggan akan tahu apa yang dikatakan oleh salah seorang daripada mereka! Jika anda tidak mahu mesej dihantar kepada orang yang menghantarnya (dia sudah tahu apa yang dia tulis!), hanya apabila melelaran melalui benang, nyatakan bahawa apabila memproses objek, gelung thisakan beralih ke elemen seterusnya tanpa melakukan sebarang tindakan ke atasnya. Atau, jika anda lebih suka, hantar mesej kepada pelanggan yang menyatakan bahawa mesej telah berjaya diterima dan dihantar. Semuanya jelas dengan pelayan sekarang. Mari kita beralih kepada pelanggan, atau lebih tepat kepada pelanggan! Segala-galanya adalah sama di sana, dengan analogi dengan pelanggan dari bab terakhir, hanya apabila mencipta contoh yang anda perlukan, seperti yang ditunjukkan dalam bab ini dengan pelayan, untuk mencipta semua yang diperlukan dalam pembina. Tetapi bagaimana jika, apabila mencipta pelanggan, dia belum sempat memasukkan apa-apa, tetapi sesuatu telah dihantar kepadanya? (Contohnya, sejarah surat-menyurat mereka yang sudah berhubung dengan sembang sebelum dia). Jadi kitaran di mana mesej yang dihantar akan diproses mesti diasingkan daripada kitaran yang mesej dibaca dari konsol dan dihantar ke pelayan untuk dimajukan kepada orang lain. Benang datang untuk menyelamatkan lagi. Tidak ada gunanya mencipta pelanggan sebagai utas. Adalah lebih mudah untuk membuat benang dengan kaedah gelung dalam larian yang membaca mesej, 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) {

            }

        }
    }
}
Dalam pembina klien anda hanya perlu memulakan utas ini. Bagaimana untuk menutup sumber pelanggan dengan betul jika dia ingin pergi? Adakah saya perlu menutup sumber benang pelayan? Untuk melakukan ini, kemungkinan besar anda perlu mencipta kaedah berasingan yang dipanggil apabila keluar dari gelung mesej. Di sana anda perlu menutup soket dan aliran I/O. Isyarat tamat sesi yang sama untuk pelanggan tertentu mesti dihantar ke utas pelayannya, yang mesti melakukan perkara yang sama dengan soketnya dan mengeluarkan dirinya daripada senarai utas dalam kelas pelayan utama. Ketua Lapan: Tiada had untuk kesempurnaan Anda boleh mencipta ciri baharu tanpa henti untuk menambah baik projek anda. Tetapi apakah sebenarnya yang perlu dipindahkan kepada pelanggan yang baru disambungkan? Saya rasa sepuluh peristiwa terakhir berlaku sebelum kedatangannya. Untuk melakukan ini, anda perlu membuat kelas di mana tindakan terakhir dengan mana-mana utas pelayan akan dimasukkan ke dalam senarai yang diisytiharkan, dan jika senarai itu sudah penuh (iaitu, sudah ada 10), padam yang pertama dan tambah yang terakhir datang. Agar kandungan senarai ini diterima oleh sambungan baharu, semasa membuat utas pelayan, dalam aliran output, anda perlu menghantarnya kepada klien. Bagaimana hendak melakukannya? Sebagai contoh, seperti ini:
public void printStory(BufferedWriter writer) {
// ...
}
Benang pelayan telah pun mencipta strim dan boleh menghantar strim output sebagai hujah. Seterusnya, anda hanya perlu lulus semua yang perlu dipindahkan kepada pelanggan baharu dalam kitaran carian. Kesimpulan: Ini hanyalah asas, dan kemungkinan besar seni bina sembang ini tidak akan berfungsi apabila membuat aplikasi sebenar. Program ini dicipta untuk tujuan pendidikan dan berdasarkannya saya menunjukkan bagaimana anda boleh membuat pelanggan berkomunikasi dengan pelayan (dan sebaliknya), bagaimana untuk melakukan ini untuk beberapa sambungan, dan, sudah tentu, bagaimana ini dianjurkan pada soket. Sumber disusun semula di bawah, dan kod sumber program yang sedang dianalisis juga dilampirkan. Ini adalah pengalaman pertama saya menulis artikel) Terima kasih atas perhatian anda :)
  1. Berfikir dalam Java Enterprise, oleh Bruce Eckel et. Al. 2003
  2. Java 8, Panduan Lengkap, Herbert Schildt, edisi ke-9, 2017 (Bab 22)
  3. Pengaturcaraan soket dalam artikel Java tentang soket
  4. Soket dalam dokumentasi rasmi
  5. ServerSocket dalam dokumentasi rasmi
  6. sumber di GitHub
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION