JavaRush /Java Blogu /Random-AZ /Socket və ServerSocket sinifləri və ya “Salam, server? Mə...
Sergey Simonov
Səviyyə
Санкт-Петербург

Socket və ServerSocket sinifləri və ya “Salam, server? Məni eşidirsən?"

Qrupda dərc edilmişdir
Giriş: “Stolun üstündə kompüter var idi, onun arxasında kodlayıcı var idi...” Socket və ServerSocket sinifləri və ya “Salam, server?  Məni eşidirsən?"  - 1 Bir vaxtlar sinif yoldaşlarımdan biri Java dilini öyrənməsinin növbəti nəticəsini yeni proqramın skrinşotu şəklində dərc etmişdi. Bu proqram çox istifadəçi söhbəti idi. O zaman mən bu dildə proqramlaşdırmaya yiyələnmək üçün öz səyahətimə yenicə başlayırdım, amma öz-özümə mütləq qeyd etdim ki, “Mən bunu istəyirəm!” Vaxt keçdi və proqramlaşdırma biliklərimin dərinləşdirilməsinin bir hissəsi olaraq növbəti layihə üzərində işi bitirdikdən sonra həmin hadisəni xatırladım və qərara gəldim ki, artıq vaxtdır. Nə isə, mən artıq sırf maraq üçün bu mövzunu qazmağa başladım, lakin mənim əsas Java dərsliyimdə (bu, Schildt-in tam təlimatı idi) java.net paketi üçün cəmi 20 səhifə verilmişdi. Bu başa düşüləndir - kitab artıq çox böyükdür. Əsas siniflərin metodları və konstruktorları cədvəlləri var idi, amma hamısı budur. Növbəti addım, əlbəttə ki, qüdrətli Google-dur: eyni şeyin təqdim olunduğu saysız-hesabsız müxtəlif məqalələr - rozetkalar haqqında iki və ya üç söz və hazır bir nümunə. Klassik yanaşma (ən azı mənim təhsil tərzimdə) əvvəlcə iş alətlərindən nəyə ehtiyacım olduğunu, onların nə olduğunu, nə üçün lazım olduğunu başa düşmək və yalnız bundan sonra problemin həlli aydın deyilsə, hazır siyahılar, qoz-fındıq və boltların açılması. Ancaq nə olduğunu anladım və nəticədə çox istifadəçi söhbəti yazdım. Zahirən belə bir şey ortaya çıxdı: Socket və ServerSocket sinifləri və ya “Salam, server?  Məni eşidirsən?"  - 2Burada söhbət dizaynı nümunəsindən istifadə edərək Java soketlərinə əsaslanan müştəri-server proqramlarının əsaslarını başa düşməyə çalışacağam. Javarash kursunda siz söhbət edəcəksiniz. Tamamilə fərqli səviyyədə, gözəl, böyük, çoxfunksiyalı olacaq. Ancaq hər şeydən əvvəl, həmişə təməl qoymalısınız, buna görə də burada belə bir bölmənin altında nə dayandığını anlamaq lazımdır. (Hər hansı bir çatışmazlıq və ya səhv tapsanız, PM-də və ya məqalənin altındakı şərhdə yazın). Başlayaq. Birinci Rəhbər: “Ev ki...” Bir server və bir müştəri arasında şəbəkə bağlantısının necə baş verdiyini izah etmək üçün, indi yaşayış binasının klassik nümunəsini götürək. Deyək ki, müştəri hansısa şəkildə müəyyən bir serverlə əlaqə yaratmalıdır. Axtarışçı axtarış obyekti haqqında nə bilməlidir? Bəli, ünvan. Server buludda sehrli bir varlıq deyil və buna görə də müəyyən bir maşında yerləşməlidir. Razılaşdırılmış iki tərəfin görüşünün keçirilməli olduğu bir evə bənzətmə. Bir yaşayış binasında bir-birinizi tapmaq üçün binanın bir ünvanı kifayət deyil, görüşün keçiriləcəyi mənzilin nömrəsini göstərməlisiniz. Eyni şəkildə, bir kompüterdə eyni anda bir neçə server ola bilər və müştərinin müəyyən bir serverlə əlaqə saxlaması üçün o, həmçinin əlaqənin baş verəcəyi port nömrəsini də göstərməlidir. Beləliklə, ünvan və port nömrəsi. Ünvan İnternet məkanında maşının identifikatoru deməkdir. Bu, bir domen adı ola bilər, məsələn, "javarush.ru" və ya adi bir IP. Liman- müəyyən bir rozetkanın əlaqəli olduğu unikal nömrə (bu termin daha sonra müzakirə olunacaq), başqa sözlə, müəyyən bir xidmət tərəfindən işğal edilir ki, onunla əlaqə qurmaq üçün istifadə olunsun. Beləliklə, bir (server) ərazisində ən azı iki obyektin görüşməsi üçün ərazinin (serverin) sahibi onun (avtomobil) üzərində müəyyən bir mənzil (port) tutmalı, ikincisi isə görüş yerini bilməlidir. evin ünvanı (domen və ya ip ) və mənzil nömrəsi (port). İkinci Baş: Soketlə tanış olun Şəbəkədə işləmək ilə bağlı anlayışlar və terminlər arasında çox vacib biri Soketdir. Bu əlaqənin baş verdiyi nöqtəni bildirir. Sadəcə olaraq, bir rozetka şəbəkədəki iki proqramı birləşdirir. Sinif Socketrozetka ideyasını həyata keçirir. Müştəri və server öz giriş/çıxış kanalları vasitəsilə əlaqə saxlayacaqlar: Socket və ServerSocket sinifləri və ya “Salam, server?  Məni eşidirsən?"  - 3 Bu sinif müştəri tərəfində elan edilir və server əlaqə siqnalı alaraq onu yenidən yaradır. Onlayn ünsiyyət belə işləyir. Başlamaq üçün burada mümkün sinif konstruktorları var Socket:
Socket(String Name_хоста, int порт) throws UnknownHostException, IOException
Socket(InetAddress IP-address, int порт) throws UnknownHostException
"host_name" - müəyyən bir şəbəkə node, IP ünvanı nəzərdə tutur. Socket sinfi onu real, mövcud ünvana çevirə bilmədisə, istisna atılacaq UnknownHostException. Liman bir limandır. Əgər port nömrəsi kimi 0 göstərilibsə, sistem özü pulsuz port ayıracaq. Bağlantı kəsildikdə də istisna baş verə bilər IOException. Qeyd etmək lazımdır ki, ikinci konstruktorda ünvan növü InetAddress. Bu, məsələn, bir ünvan kimi bir domen adını göstərməyiniz lazım olduqda xilasetmə üçün gəlir. Həmçinin, bir domen bir neçə IP ünvanı nəzərdə tutduqda, InetAddressonlardan bir sıra əldə etmək üçün istifadə edə bilərsiniz. Bununla belə, IP ilə də işləyir. Siz həmçinin host adını, IP ünvanını təşkil edən bayt massivini və s. Biz buna bir az daha toxunacağıq, lakin tam təfərrüatlar üçün rəsmi sənədlərə müraciət etməli olacaqsınız. Tipli bir obyekt işə salındıqda Socket, aid olduğu müştəri şəbəkədə müəyyən bir ünvan və port nömrəsi ilə serverə qoşulmaq istədiyini elan edir. Aşağıda sinifin ən çox istifadə olunan üsulları verilmişdir Socket: InetAddress getInetAddress()– rozetka haqqında məlumatları ehtiva edən obyekti qaytarır. Soket bağlı deyilsə - null int getPort()- serverə qoşulmanın baş verdiyi portu qaytarır int getLocalPort()- rozetkanın bağlı olduğu portu qaytarır. Fakt budur ki, müştəri və server bir portda "ünsiyyət" edə bilər, lakin onların bağlandığı portlar tamamilə fərqli ola bilər boolean isConnected()- əlaqə qurularsa doğru qaytarır void connect(SocketAddress address)- yeni bir əlaqəni göstərir boolean isClosed()- rozetka bağlanarsa doğru qaytarır boolean isBound()- doğru qaytarır, əgər rozetka faktiki olaraq bir ünvana bağlıdırsa, sinif Socketinterfeysi həyata keçirir AutoCloseable, ona görə də ondan istifadə oluna bilər try-with-resources. Bununla belə, siz həmçinin close() funksiyasından istifadə edərək rozetkanı klassik şəkildə bağlaya bilərsiniz. Rəhbər Üç: və bu ServerSocketdir Tutaq ki, biz bir sinif şəklində Socketmüştəri tərəfində əlaqə sorğusu elan etdik. Server bizim istəyimizi necə təxmin edəcək? Bunun üçün serverdə kimi bir sinif ServerSocketvə onun içində accept() metodu var. Onun konstruktorları aşağıda təqdim olunur:
ServerSocket() throws IOException
ServerSocket(int порт) throws IOException
ServerSocket(int порт, int максимум_подключений) throws IOException
ServerSocket(int порт, int максимум_подключений, InetAddress локальный_address) throws IOException
Elan edərkən ServerSocket əlaqə ünvanını göstərməyə ehtiyac yoxdur, çünki rabitə server maşınında baş verir. Yalnız çoxkanallı host ilə server yuvasının hansı IP-yə bağlı olduğunu təyin etməlisiniz. Head Three.One: Xeyr Deyən Server Proqramı ona lazım olduğundan daha çox resursla təmin etmək həm baha başa gəldiyindən, həm də əsassız olduğundan, konstruktorda ServerSocketsizdən əməliyyat zamanı server tərəfindən qəbul edilən maksimum bağlantıları bəyan etməyiniz xahiş olunur. Əgər göstərilməyibsə, o zaman standart olaraq bu rəqəm 50-yə bərabər hesab olunacaq. Bəli, nəzəri olaraq ServerSocketbunun eyni yuva olduğunu, yalnız server üçün olduğunu güman edə bilərik. Amma sinifdən tamamilə fərqli bir rol oynayır Socket. Bu, yalnız əlaqə yaratma mərhələsində lazımdır. Bir növ obyekt yaratdıqdan sonra ServerSocketkiminsə serverə qoşulmaq istədiyini öyrənməlisiniz. accept() metodu burada bağlıdır. Hədəf kimsə ona qoşulmaq istəyənə qədər gözləyir və bu baş verdikdə tipli obyekti Socket, yəni yenidən yaradılmış müştəri yuvasını qaytarır. İndi isə server tərəfində müştəri yuvası yaradılıb, ikitərəfli rabitə başlaya bilər. SocketMüştəri tərəfində tip obyektinin yaradılması və ServerSocketserver tərəfindən istifadə edərək yenidən yaradılması əlaqə üçün minimum tələb olunur. Dördüncü Rəhbər: Şaxta babaya məktub Вопрос: Müştəri və server tam olaraq necə ünsiyyət qurur? Ответ:I/O axınları vasitəsilə. Artıq nəyimiz var? Serverin ünvanı və müştərinin port nömrəsi olan rozetka və server tərəfində qəbul () sayəsində eyni şey. Beləliklə, onların bir rozetka vasitəsilə əlaqə quracağını güman etmək ağlabatandır. InputStreamBunu etmək üçün axınlara və OutputStreamtipli obyektlərə giriş imkanı verən iki üsul var Socket. Budur onlar:
InputStream getInputStream()
OutputStream getOutputStream()
Çılpaq baytları oxumaq və yazmaq o qədər də səmərəli olmadığı üçün axınlar buferlənmiş və ya olmayan adapter siniflərinə bükülə bilər. Misal üçün:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
Rabitənin iki istiqamətli olması üçün bu cür əməliyyatlar hər iki tərəfdən yerinə yetirilməlidir. İndi siz daxildən istifadə edərək bir şey göndərə və istifadə edərək nəyisə qəbul edə bilərsiniz və əksinə. Əslində bu, praktiki olaraq sinfin yeganə funksiyasıdır Socket. Bəli, flush() metodunu unutma BufferedWriter- buferin məzmununu təmizləyir. Bu edilmədikdə, məlumat ötürülməyəcək və buna görə də qəbul edilməyəcəkdir. Qəbul edən ip də xəttin sonu göstəricisini gözləyir – “\n”, əks halda mesaj qəbul edilməyəcək, çünki əslində mesaj tamamlanmayıb və tamamlanmayıb. Əgər bu sizin üçün əlverişsiz görünürsə, narahat olmayın, siz həmişə sinifdən istifadə edə bilərsiniz PrintWriter, onu bitirmək, ikinci arqument kimi doğru göstərin və sonra buferdən çıxma avtomatik olaraq baş verəcək:
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
Həmçinin, xəttin sonunu göstərməyə ehtiyac yoxdur, bu sinif bunu sizin üçün edir. Bəs string I/O rozetkanın edə biləcəyi limitdirmi? Xeyr, siz socket axınları vasitəsilə obyektlər göndərmək istəyirsiniz? Allah xatirinə. Onları sıralayın və getməyə hazırsınız:
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Beşinci Rəhbər: İnternet vasitəsilə real ünsiyyət Çünki real IP ünvanı ilə real şəbəkə vasitəsilə əlaqə yaratmaq üçün tam hüquqlu bir serverə sahib olmaq lazımdır, çünki:
  1. Gələcək söhbətimiz, bir kommunal olaraq, belə qabiliyyətlərə malik deyil. O, yalnız əlaqə yarada və mesaj qəbul edə/göndərə bilər. Yəni onun real server imkanları yoxdur.
  2. Yalnız soket məlumatlarını və I/O axınlarını ehtiva edən serverimiz real WEB və ya FTP serveri kimi işləyə bilməz, onda yalnız bununla biz İnternet üzərindən qoşula bilməyəcəyik.
Bundan əlavə, biz proqramı yenicə inkişaf etdirməyə başlayırıq, bu o deməkdir ki, o, real şəbəkə ilə dərhal işləmək üçün kifayət qədər sabit deyil, ona görə də yerli hostdan əlaqə ünvanı kimi istifadə edəcəyik. Yəni nəzəri olaraq, müştəri və server hələ də bir rozetkadan başqa heç bir şəkildə bağlanmayacaq, lakin proqramı sazlamaq üçün şəbəkə üzərində real əlaqə olmadan eyni maşında olacaqlar. Konstruktorda Socketünvanın yerli olduğunu göstərmək üçün 2 yol var:
  1. Ünvan arqumenti kimi “localhost” yazın, yəni yerli stub. Bunun üçün "127.0.0.1" də uyğundur - bu, sadəcə bir stubun rəqəmsal formasıdır.
  2. InetAddress istifadə edərək:
    1. InetAddress.getByName(null)- localhost üçün null nöqtələri
    2. InetAddress.getByName("localhost")
    3. InetAddress.getByName("127.0.0.1")
Sadəlik üçün String tipli "localhost" istifadə edəcəyik. Ancaq bütün digər variantlar da işləkdir. Baş Altı: Söhbət vaxtıdır Beləliklə, serverlə söhbət seansını həyata keçirmək üçün bizdə artıq hər şey var. Qalan yalnız onu bir yerə yığmaqdır: Aşağıdakı siyahı müştərinin serverə necə qoşulduğunu, ona bir mesaj göndərdiyini göstərir və server də öz növbəsində ondan arqument kimi istifadə edərək mesajı qəbul etdiyini təsdiqləyir: "Server. 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);
        }

    }
}
Əlbəttə ki, əvvəlcə serveri işə salmalısınız, çünki onu birləşdirəcək heç bir şey yoxdursa, müştəri başlanğıcda nəyə qoşulacaq? :) Çıxış belə olacaq: /* Nəsə demək istəyirdiniz? Bura daxil edin: Salam, server? Məni eşidirsən? Salam, bu Serverdir! Təsdiq edirəm, yazdınız: Salam, server? Məni eşidirsən? Müştəri bağlandı... */ Hurray! Biz serverə müştəri ilə ünsiyyət qurmağı öyrətdik! Rabitə iki replikada deyil, istədiyiniz qədər baş versin, sadəcə olaraq iplərin oxunmasını və yazılmasını bir müddət (doğru) döngəyə sarın və çıxış üçün müəyyən bir mesaja görə, məsələn, "çıxış" olduğunu göstərin. , dövriyyə kəsildi və proqram bitəcəkdi. Rəhbər Yeddi: Çox istifadəçi daha yaxşıdır.Serverin bizi eşidə bilməsi yaxşıdır, lakin öz növümüzdən biri ilə ünsiyyət qura bilsək, daha yaxşı olardı. Məqalənin sonunda bütün mənbələri əlavə edəcəyəm, buna görə də burada həmişə böyük deyil, düzgün istifadə olunarsa, çox istifadəçi söhbəti yaratmağa imkan verəcək vacib kod hissələrini göstərəcəyəm. Beləliklə, server vasitəsilə başqa bir müştəri ilə əlaqə saxlamaq istəyirik. Bunu necə etmək olar? Aydındır ki, müştəri proqramının öz metodu olduğundan main, bu o deməkdir ki, o, serverdən ayrı və digər müştərilərlə paralel olaraq işə salına bilər. Bu bizə nə verir? Nə isə lazımdır ki, hər yeni qoşulma ilə server dərhal əlaqəyə keçməsin, bu əlaqəni bir növ siyahıya yazıb yeni əlaqəni gözləməyə davam etsin və bir növ köməkçi xidmət müəyyən bir əlaqə ilə əlaqə saxlasın. müştəri. Və müştərilər serverə yazmalı və bir-birindən asılı olmayaraq cavab gözləməlidirlər. İplər köməyə gəlir. Deyək ki, yeni əlaqələri yadda saxlamaq üçün məsul bir sinifimiz var: O, aşağıdakıları göstərməlidir:
  1. Port nömrəsi.
  2. Yeni əlaqəni yazdığı siyahı.
  3. ServerSocket, tək (!) nüsxədə.
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();
        }
    }
}
Tamam, indi hər bir yenidən yaradılmış soket itirilməyəcək, lakin serverdə saxlanacaq. Daha. Hər bir müştərinin qulaq asmağa ehtiyacı var. Son fəsildən server funksiyaları ilə mövzu yaradaq.
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) {}
    }
}
Beləliklə, server ipinin konstruktorunda, ipin müəyyən bir müştəri ilə əlaqə quracağı bir yuva işə salınmalıdır. Həmçinin I/O mövzuları və ipi birbaşa konstruktordan başlamaq üçün lazım olan hər şey. Yaxşı, amma server mövzusu müştəridən gələn mesajı oxuyanda nə baş verir? Yalnız müştərinizə geri göndərilsin? Çox təsirli deyil. Biz çox istifadəçi söhbəti edirik, ona görə də bir nəfərin yazdıqlarını qəbul etmək üçün hər bir bağlı müştəriyə ehtiyacımız var. Müştəriləri ilə əlaqəli bütün server başlıqlarının siyahısından istifadə etməli və hər bir mesajı müəyyən bir başlığa göndərməlisiniz ki, onu müştəriyə göndərsin:
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) {}
}
İndi bütün müştərilər onlardan birinin nə dediyini biləcəklər! Mesajın onu göndərən şəxsə göndərilməsini istəmirsinizsə (o, artıq nə yazdığını bilir!), sadəcə iplər arasında təkrarlanan zaman, obyekti emal edərkən, dövrənin yerinə thisyetirmədən növbəti elementə keçəcəyini qeyd edin. üzərində hər hansı bir hərəkət. Və ya istəsəniz, müştəriyə mesajın uğurla alındığını və göndərildiyini bildirən bir mesaj göndərin. İndi serverdə hər şey aydındır. Keçək müştəriyə, daha doğrusu müştərilərə! Orada hər şey eynidir, son fəsildəki müştəri ilə bənzətmə ilə, yalnız bir nümunə yaratarkən, bu fəsildə serverlə göstərildiyi kimi, konstruktorda lazım olan hər şeyi yaratmaq lazımdır. Bəs müştəri yaradanda hələ heç nə daxil etməyə vaxtı olmayıbsa, amma ona artıq bir şey göndərilibsə? (Məsələn, ondan əvvəl artıq çata qoşulmuş şəxslərin yazışma tarixi). Beləliklə, göndərilən mesajların işlənəcəyi dövrlər mesajların konsoldan oxunduğu və başqalarına yönləndirmək üçün serverə göndərildiyi dövrlərdən ayrılmalıdır. İplər yenidən köməyə gəlir. Bir ip kimi müştəri yaratmağın mənası yoxdur. Mesajları oxuyan və eyni zamanda bənzətmə ilə yazan run metodunda bir döngə ilə bir ip düzəltmək daha rahatdır:
// 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) {

            }

        }
    }
}
Müştəri konstruktorunda sadəcə bu mövzuları başlamaq lazımdır. Müştərinin ayrılmaq istəsə resurslarını necə düzgün bağlamaq olar? Mən server mövzu resurslarını bağlamalıyam? Bunu etmək üçün, çox güman ki, mesaj döngəsindən çıxarkən çağırılan ayrıca bir üsul yaratmalısınız. Orada rozetkanı və giriş/çıxış axınlarını bağlamalı olacaqsınız. Müəyyən bir müştəri üçün eyni sessiyanın bitmə siqnalı onun server ipinə göndərilməlidir, o da öz yuvası ilə eyni şeyi etməli və özünü əsas server sinifindəki iplər siyahısından çıxarmalıdır. Səkkizinci Baş: Mükəmməllik üçün heç bir məhdudiyyət yoxdur Siz layihənizi təkmilləşdirmək üçün sonsuz olaraq yeni funksiyalar icad edə bilərsiniz. Bəs yeni qoşulmuş müştəriyə tam olaraq nə köçürülməlidir? Düşünürəm ki, son on hadisə onun gəlişindən əvvəl baş verib. Bunu etmək üçün, elan edilmiş siyahıya hər hansı bir server mövzusu ilə son hərəkətin daxil ediləcəyi bir sinif yaratmalısınız və siyahı artıq doludursa (yəni artıq 10-dur), birincisini silin və əlavə edin. sonuncu gələn. Bu siyahının məzmununun yeni bir əlaqə ilə qəbul edilməsi üçün, çıxış axınında bir server mövzusu yaratarkən, onları müştəriyə göndərməlisiniz. Bunu necə etmək olar? Məsələn, bu kimi:
public void printStory(BufferedWriter writer) {
// ...
}
Server mövzusu artıq axınlar yaratmışdır və çıxış axınını arqument kimi ötürə bilər. Sonra, yalnız bir axtarış dövründə yeni müştəriyə köçürülməsi lazım olan hər şeyi keçirməlisiniz. Nəticə: Bu sadəcə əsaslardır və çox güman ki, bu söhbət arxitekturası real proqram yaratarkən işləməyəcək. Bu proqram təhsil məqsədləri üçün yaradılmışdır və onun əsasında müştərini serverlə necə əlaqə qura biləcəyinizi (və əksinə), bunu bir neçə əlaqə üçün necə edəcəyinizi və əlbəttə ki, bunun rozetkalarda necə təşkil olunduğunu göstərdim. Mənbələr aşağıda yenidən sıralanır və təhlil edilən proqramın mənbə kodu da əlavə olunur. Bu mənim ilk məqalə yazmaq təcrübəmdir) Diqqətiniz üçün təşəkkürlər :)
  1. Java Müəssisəsində Düşünmək, Bruce Eckel et. Al. 2003
  2. Java 8, Tam Bələdçi, Herbert Şildt, 9-cu nəşr, 2017 (Fəsil 22)
  3. Java-da soket proqramlaşdırma soketlər haqqında məqalə
  4. Rəsmi sənədlərdə rozetka
  5. Rəsmi sənədlərdə ServerSocket
  6. GitHub-dakı mənbələr
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION