JavaRush /Java блогы /Random-KK /Socket және ServerSocket сыныптары немесе «Сәлеметсіз бе,...
Sergey Simonov
Деңгей
Санкт-Петербург

Socket және ServerSocket сыныптары немесе «Сәлеметсіз бе, сервер? Сен мені естіп тұрсың ба?»

Топта жарияланған
Кіріспе: «Үстел үстінде компьютер тұрды, оның артында шифрлаушы бар еді...» Socket және ServerSocket сыныптары немесе «Сәлеметсіз бе, server?  Сен мені естіп тұрсың ба?»  - 1 Бірде менің сыныптастарымның бірі Java тілін зерттеудің кезекті нәтижесін жаңа бағдарламаның скриншоты түрінде жариялады. Бұл бағдарлама көп қолданушыға арналған чат болды. Ол кезде мен осы тілде бағдарламалауды меңгеру бойынша өзімнің жеке саяхатымды енді ғана бастадым, бірақ мен өз-өзіме «Мен мұны қалаймын!» деп нақты атап өттім. Уақыт өтті және бағдарламалау бойынша білімімді тереңдету аясында келесі жобамен жұмысты аяқтағаннан кейін мен бұл оқиға есіме түсіп, уақыт келді деп шештім. Қалай болғанда да, мен бұл тақырыпты тек қызығушылық үшін зерттей бастадым, бірақ менің негізгі Java оқулығымда (бұл Шилдттің толық нұсқаулығы болды) java.net пакеті үшін тек 20 бет берілген. Бұл түсінікті - кітап қазірдің өзінде өте үлкен. Негізгі сыныптардың әдістері мен конструкторларының кестелері болды, бірақ бәрі. Келесі қадам, әрине, құдіретті Google: бірдей нәрсе ұсынылған әртүрлі мақалалар - розеткалар туралы екі-үш сөз және дайын мысал. Классикалық тәсіл (кем дегенде менің оқу стилімде) - алдымен жұмыс үшін қандай құралдар қажет екенін, олардың не екенін, не үшін қажет екенін түсіну, содан кейін ғана, егер мәселенің шешімі анық болмаса, дайын құралмен айналысу керек. - гайкалар мен болттарды бұрап, тізімдер жасады. Бірақ мен не екенін түсіндім және соңында көп пайдаланушы чатын жаздым. Сырттай қарағанда, бұл келесідей болды: Socket және ServerSocket сыныптары немесе «Сәлеметсіз бе, server?  Сен мені естіп тұрсың ба?»  - 2Мұнда мен сізге чат дизайнының мысалын қолдана отырып, Java ұяшықтарына негізделген клиент-server қосымшаларының негіздері туралы түсінік беруге тырысамын. Javarash курсында сіз чат жасайсыз. Ол мүлдем басқа деңгейде, әдемі, үлкен, көп функциялы болады. Бірақ, ең алдымен, сіз әрқашан негіз қалауыңыз керек, сондықтан мұнда мұндай бөлімнің негізінде не жатқанын анықтау керек. (Егер сіз қандай да бір кемшіліктер мен қателерді тапсаңыз, ПМ-ге немесе мақаланың астындағы түсініктемеге жазыңыз). Бастайық. Бірінші басшы: «Бұл үй...» Сервер мен бір клиент арасында желілік қосылым қалай болатынын түсіндіру үшін көп пәтерлі үйдің қазіргі классикалық мысалын алайық. Клиент қандай да бір түрде белгілі бір serverмен байланыс орнатуы керек делік. Іздеуші іздеу an objectісі туралы не білуі керек? Иә, мекенжай. Сервер бұлттағы сиқырлы нысан емес, сондықтан ол белгілі бір құрылғыда орналасуы керек. Келісілген екі тараптың кездесуі өтетін үйге ұқсас. Ал көпқабатты үйде бір-бірін табу үшін ғимараттың бір мекенжайы жеткіліксіз, жиналыс өтетін пәтердің нөмірін көрсету керек. Сол сияқты, бір компьютерде бірден бірнеше server болуы мүмкін және клиент белгілі бір serverмен байланысуы үшін ол қосылым болатын порт нөмірін де көрсетуі керек. Сонымен, мекенжай және порт нөмірі. Адрес интернет кеңістігіндегі машина идентификаторын білдіреді. Бұл домендік атау болуы мүмкін, мысалы, «javarush.ru» , немесе кәдімгі IP. Порт- белгілі бір розетка байланыстырылған бірегей нөмір (бұл термин кейінірек талқыланады), басқаша айтқанда, онымен байланысу үшін пайдалануға болатындай белгілі бір қызмет алады. Демек, бір (server) аумағында кемінде екі нысан кездесуі үшін ауданның (serverдің) иесі ондағы (автомобиль) белгілі бір пәтерде (портта) тұруы керек, ал екіншісі кездесу орнын біле отырып табуы керек. үйдің мекенжайы (домен немесе ip ) және пәтер нөмірі (порт). Екінші басшы: танысу ұясы Желіде жұмыс істеуге қатысты ұғымдар мен терминдердің бірі өте маңыздысы - Socket. Ол байланыс болатын нүктені білдіреді. Қарапайым тілмен айтқанда, розетка желідегі екі бағдарламаны қосады. Сынып Socketрозетка идеясын жүзеге асырады. Клиент пен server оның енгізу/шығару арналары арқылы байланысады: Socket және ServerSocket сыныптары немесе «Сәлеметсіз бе, server?  Сен мені естіп тұрсың ба?»  - 3 Бұл класс клиент жағында жарияланады және server қосылу сигналын қабылдай отырып, оны қайта жасайды. Желідегі байланыс осылай жұмыс істейді. Бастау үшін, мұнда ықтимал класс конструкторлары Socket:
Socket(String Name_хоста, int порт) throws UnknownHostException, IOException
Socket(InetAddress IP-address, int порт) throws UnknownHostException
«хост_аты» - белгілі бір желі түйінін, IP мекенжайын білдіреді. Егер розетка сыныбы оны нақты, бар мекенжайға түрлендіре алмаса, онда ерекше жағдай шығарылады UnknownHostException. Порт - бұл порт. Егер порт нөмірі ретінде 0 көрсетілсе, жүйенің өзі бос портты бөледі. Қосылым жоғалған жағдайда ерекше жағдай да орын алуы мүмкін IOException. Айта кету керек, екінші конструктордағы мекенжай түрі InetAddress. Ол, мысалы, мекенжай ретінде домендік атауды көрсету қажет болғанда көмекке келеді. Сондай-ақ, домен бірнеше IP мекенжайларын білдірсе, InetAddressолардың массивін алу үшін оларды пайдалануға болады. Дегенмен, ол IP-мен де жұмыс істейді. Сондай-ақ хост атауын, IP мекенжайын құрайтын byte массивін және т.б. алуға болады. Біз бұл туралы біразырақ тоқталамыз, бірақ толық мәліметтерді алу үшін ресми құжаттамаға өтуіңіз керек. Түрдегі нысан инициализацияланған кезде Socket, ол тиесілі клиент желіде белгілі бір мекенжай мен порт нөмірі бойынша serverге қосылғысы келетінін хабарлайды. Төменде класстың ең жиі қолданылатын әдістері берілген Socket: InetAddress getInetAddress()– розетка туралы деректері бар нысанды қайтарады. Егер розетка қосылмаған болса - null int getPort()- serverге қосылу орын алатын портты қайтарады int getLocalPort()- ұяшық байланыстырылған портты қайтарады. Клиент пен server бір портта «байланысуға» қабілетті, бірақ олар байланыстырылған порттар мүлдем басқаша болуы мүмкін boolean isConnected()- қосылым орнатылған болса, шын мәнін қайтарады void connect(SocketAddress address)- жаңа қосылымды көрсетеді boolean isClosed()- розетка жабық болса, шын мәнін қайтарады. boolean isBound()- шын мәнін қайтарады, егер розетка нақты мекенжайға байланысты болса, сынып Socketинтерфейсті жүзеге асырады AutoCloseable, сондықтан оны try-with-resources. Дегенмен, сіз классикалық жолмен, close() арқылы розетканы жабуға болады. Үшінші басшы: және бұл ServerSocketSocket Біз клиенттік жағында класс түрінде қосылу сұрауын жарияладық делік . Сервер біздің тілегімізді қалай анықтайды? Бұл үшін serverде класс сияқты ServerSocketжәне онда accept() әдісі бар. Оның конструкторлары төменде берілген:
ServerSocket() throws IOException
ServerSocket(int порт) throws IOException
ServerSocket(int порт, int максимум_подключений) throws IOException
ServerSocket(int порт, int максимум_подключений, InetAddress локальный_address) throws IOException
Декларациялау кезінде ServerSocket қосылым addressін көрсетудің қажеті жоқ, өйткені байланыс serverлік машинада жүреді. Тек көп арналы хостпен server ұясы қай IP-ге байланыстырылғанын көрсету керек. Үшінші басты: «Жоқ» деп жауап беретін server Бағдарламаны қажетінен көбірек ресурстармен қамтамасыз ету қымбат әрі негізсіз болғандықтан, конструкторда ServerSocketжұмыс кезінде server қабылдаған максималды қосылымдарды жариялау сұралады. Егер ол көрсетілмесе, әдепкі бойынша бұл сан 50-ге тең деп есептеледі. Иә, теориялық тұрғыдан біз ServerSocketбұл тек server үшін бірдей ұяшық деп болжауға болады. Бірақ ол сыныпқа қарағанда мүлдем басқа рөл атқарады Socket. Ол тек қосылымды құру кезеңінде қажет. Түр нысанын жасағаннан кейін ServerSocketбіреудің serverге қосылғысы келетінін білу керек. Мұнда accept() әдісі қосылған. Мақсат біреу оған қосылғысы келгенше күтеді және бұл орын алған кезде ол түрдегі нысанды Socket, яғни қайта жасалған клиент ұясын қайтарады. Енді клиенттік розетка server жағында жасалғаннан кейін екі жақты байланыс басталуы мүмкін. SocketКлиент жағында тип нысанын жасау және оны ServerSocketserver жағын пайдаланып қайта жасау қосылым үшін қажетті ең аз. Төртінші басшы: Аяз атаға хат Вопрос: Клиент пен server нақты қалай байланысады? Ответ:Енгізу/шығару ағындары арқылы. Бізде қазірдің өзінде не бар? Сервер мекенжайы мен клиенттің порт нөмірі бар розетка және server жағындағы accept() арқасында бірдей нәрсе. Сондықтан олар розетка арқылы байланысады деп болжауға болады. InputStreamОл үшін ағындар мен OutputStreamтүрдегі нысандарға рұқсат беретін екі әдіс бар Socket. Міне олар:
InputStream getInputStream()
OutputStream getOutputStream()
Жалаң byteты оқу және жазу тиімді емес болғандықтан, ағындарды буферленген немесе жоқ адаптер кластарына орауға болады. Мысалы:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
Байланыс екі жақты болуы үшін мұндай операциялар екі жақта да орындалуы керек. Енді сіз in көмегімен бірдеңені жібере аласыз және out арқылы бірдеңені ала аласыз және керісінше. Шын мәнінде, бұл сыныптың іс жүзінде жалғыз функциясы Socket. Иә, flush() әдісі туралы ұмытпаңыз BufferedWriter- ол буфердің мазмұнын тазартады. Бұл жасалмаса, ақпарат берілмейді, демек, қабылданбайды. Қабылдау ағыны да жолдың соңы көрсеткішін күтеді – “\n”, әйтпесе хабарлама қабылданбайды, өйткені шын мәнінде хабарлама аяқталмаған және аяқталмаған. Егер бұл сізге ыңғайсыз болып көрінсе, уайымдамаңыз, сіз әрқашан классты пайдалана аласыз PrintWriter, оны өшіру керек, екінші аргумент ретінде true көрсетіңіз, содан кейін буферден шығу автоматты түрде орын алады:
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
Сондай-ақ, жолдың соңын көрсетудің қажеті жоқ, бұл класс мұны сіз үшін жасайды. Бірақ енгізу/шығару жолы розетка жасай алатын шектеу болып табылады ма? Жоқ, нысандарды розетка ағындары арқылы жібергіңіз келе ме? Құдай үшін. Оларды сериялаңыз және сіз баруға дайынсыз:
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Бесінші басшы: Интернет арқылы нақты байланыс Шынайы IP мекенжайы бар нақты желі арқылы қосылу үшін сізде толыққанды server болуы керек, өйткені:
  1. Біздің болашақ чатымыз утorта ретінде мұндай қабілеттерге ие емес. Ол тек байланыс орната алады және хабарды қабылдай алады/жібере алады. Яғни, оның нақты server мүмкіндіктері жоқ.
  2. Тек розетка деректері мен енгізу/шығару ағындарын қамтитын serverіміз нақты WEB немесе FTP serverі ретінде жұмыс істей алмайды, сонда ғана біз Интернет арқылы қосыла алмаймыз.
Сонымен қатар, біз бағдарламаны әзірлеуге енді кірісіп жатырмыз, яғни ол нақты желімен бірден жұмыс істеу үшін тұрақты емес, сондықтан қосылым мекенжайы ретінде жергілікті хостты қолданамыз. Яғни, теорияда клиент пен server розеткадан басқа ешқандай жолмен қосылмайды, бірақ бағдарламаны жөндеу үшін олар желі арқылы нақты байланыссыз бір машинада болады. Конструкторда Socketмекенжайдың жергілікті екенін көрсету үшін 2 әдіс бар:
  1. Мекенжай аргументі ретінде «localhost» деп жазыңыз, бұл жергілікті түбірді білдіреді. Бұл үшін «127.0.0.1» де қолайлы - бұл жай ғана сандық нысаны.
  2. InetAddress пайдалану:
    1. InetAddress.getByName(null)- жергілікті хостқа нөл нүктелері
    2. InetAddress.getByName("localhost")
    3. InetAddress.getByName("127.0.0.1")
Қарапайымдылық үшін String түріндегі «localhost» қолданамыз. Бірақ барлық басқа опциялар да жұмыс істейді. Алтыншы топ: Әңгімелесу уақыты келді Сонымен, бізде serverмен сөйлесу сеансын жүзеге асыру үшін қажет нәрсенің бәрі бар. Оны біріктіру ғана қалады: Келесі тізім клиенттің serverге қалай қосылатынын, оған бір хабарлама жіберетінін көрсетеді, ал 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);
        }

    }
}
Әрине, алдымен serverді іске қосу керек, себебі оны қосатын ештеңе болмаса, клиент іске қосу кезінде немен қосылады? :) Шығару келесідей болады: /* Бірдеңе айтқыңыз келді ме? Оны мына жерге енгізіңіз: Сәлеметсіз бе, server? Сен мені естіп тұрсың ба? Сәлем, бұл server! Мен растаймын, сіз жаздыңыз: Сәлеметсіз бе, server? Сен мені естіп тұрсың ба? Клиент жабылды... */ Ура! Біз serverге клиентпен байланысуды үйреттік! Байланыс екі репликада емес, қалағаныңызша болуы үшін ағындарды оқу мен жазуды уақытша (шын) циклге орап, шығу үшін белгілі бір хабарламаға сәйкес, мысалы, «шығу» екенін көрсетіңіз. , цикл үзілді және бағдарлама аяқталады. Жетінші басшы: Көп қолданушы жақсырақ.Сервердің бізді ести алатыны жақсы, бірақ біз өзімізге ұқсас біреумен сөйлесе алсақ, әлдеқайда жақсы болар еді. Мен мақаланың соңында барлық дереккөздерді қосамын, сондықтан мұнда мен әрқашан үлкен емес, бірақ дұрыс пайдаланылған жағдайда көп пайдаланушы чатын құруға мүмкіндік беретін маңызды code бөліктерін көрсетемін. Сонымен, біз server арқылы басқа клиентпен байланысу мүмкіндігін алғымыз келеді. Бұны қалай істейді? Әлбетте, клиенттік бағдарламаның өз әдісі болғандықтан main, бұл оны serverден бөлек және басқа клиенттермен қатар іске қосуға болатындығын білдіреді. Бұл бізге не береді? Қалай болғанда да, әрбір жаңа қосылыммен server байланысқа бірден өтпей, бұл қосылымды қандай да бір тізімге жазып, жаңа қосылымды күтуді жалғастыруы керек, ал қандай да бір көмекші қызмет белгілі бір байланыспен айналысады. клиент. Ал клиенттер serverге хат жазып, бір-бірінен тәуелсіз жауап күтуі керек. Жіптер көмекке келеді. Бізде жаңа қосылымдарды есте сақтауға жауапты сынып бар делік: Ол келесідей болуы керек:
  1. Порт нөмірі.
  2. Жаңа қосылымды жазатын тізім.
  3. Және ServerSocket, бір (!) көшірмеде.
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();
        }
    }
}
Жарайды, енді әрбір қайта жасалған розетка жоғалмайды, бірақ serverде сақталады. Әрі қарай. Әрбір тұтынушы тыңдайтын біреуді қажет етеді. Соңғы тараудан server функциялары бар ағынды жасайық.
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) {}
    }
}
Сонымен, server ағынының конструкторында ағын нақты клиентпен байланысатын ұяшықты инициализациялау керек. Сондай-ақ енгізу/шығару ағындары және ағынды тікелей конструктордан бастау үшін қажет барлық нәрсе. Жарайды, бірақ server ағыны клиенттен хабарламаны оқығанда не болады? Тек клиентке қайтару керек пе? Өте тиімді емес. Біз көп пайдаланушы чатын жасап жатырмыз, сондықтан бізге әрбір қосылған клиент бір адамның жазғанын алуы керек. Сіз олардың клиенттерімен байланыстырылған барлық server ағындарының тізімін пайдалануыңыз керек және оны клиентке жіберетіндей белгілі бір ағынға жіберілген әрбір хабарды жіберуіңіз керек:
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) {}
}
Енді барлық клиенттер олардың біреуінің не айтқанын білетін болады! Хабарламаны жіберген адамға жіберілуін қаламасаңыз (ол не жазғанын біледі!), жай ағындар арқылы қайталау кезінде, an objectіні өңдеу кезінде цикл орындамай thisкелесі элементке ауысатынын көрсетіңіз. оған қатысты кез келген әрекеттер. Немесе, қаласаңыз, клиентке хабарлама сәтті қабылданғаны және жіберілгені туралы хабарлама жіберіңіз. Серверде бәрі түсінікті. Клиентке, дәлірек айтсақ, клиенттерге көшейік! Мұнда бәрі бірдей, соңғы тараудағы клиентке ұқсас, тек дананы жасау кезінде, serverмен осы тарауда көрсетілгендей, конструкторда қажет нәрсенің бәрін жасау керек. Ал егер клиентті құру кезінде ол әлі ештеңе енгізуге үлгермесе, бірақ оған бірдеңе жіберілген болса ше? (Мысалы, оған дейін чатқа қосылғандардың хат алмасу тарихы). Сондықтан жіберілген хабарламалар өңделетін циклдар консольден хабарлар оқылатын және басқаларға қайта жіберу үшін serverге жіберілетін циклдерден бөлінуі керек. Жіптер қайтадан көмекке келеді. Клиентті ағын ретінде жасаудың мағынасы жоқ. Хабарларды оқитын, сондай-ақ ұқсастығы бойынша жазатын іске қосу әдісінде циклі бар ағынды жасау ыңғайлырақ:
// 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) {

            }

        }
    }
}
Клиент конструкторында осы ағындарды бастау керек. Клиент кеткісі келсе оның ресурстарын қалай дұрыс жабу керек? Сервер ағынының ресурстарын жабу керек пе? Бұл әрекетті орындау үшін сізге хабарлар циклінен шыққан кезде шақырылатын бөлек әдісті жасау қажет болуы мүмкін. Онда розетка мен енгізу/шығару ағындарын жабу керек. Белгілі бір клиент үшін бірдей сеанстың аяқталу сигналы оның server ағынына жіберілуі керек, ол өз ұясымен бірдей әрекетті орындауы және өзін негізгі server класындағы ағындар тізімінен алып тастауы керек. Сегізінші бас: Кемелділікке шектеу жоқ Жобаңызды жақсарту үшін шексіз жаңа мүмкіндіктерді ойлап табуға болады. Бірақ жаңадан қосылған клиентке нақты не беру керек? Менің ойымша, соңғы он оқиға ол келгенге дейін болды. Мұны істеу үшін сіз кез келген server ағынымен соңғы әрекет жарияланған тізімге енгізілетін сыныпты жасауыңыз керек және тізім әлдеқашан толы болса (яғни, 10 бар), біріншісін жойып, қосыңыз соңғы келген. Бұл тізімнің мазмұнын жаңа қосылым арқылы алу үшін server ағынын жасау кезінде шығыс ағынында оларды клиентке жіберу керек. Бұны қалай істейді? Мысалы, келесідей:
public void printStory(BufferedWriter writer) {
// ...
}
Сервер ағыны әлдеқашан ағындарды жасаған және шығыс ағынын аргумент ретінде бере алады. Әрі қарай, іздеу циклінде жаңа клиентке көшіру қажет барлық нәрсені беру керек. Қорытынды: Бұл жай ғана негіз және бұл чат архитектурасы нақты қосымшаны жасау кезінде жұмыс істемеуі мүмкін. Бұл бағдарлама білім беру мақсатында жасалған және оның негізінде мен клиентті serverмен қалай байланысуға болатынын (және керісінше), мұны бірнеше қосылымдар үшін қалай жасауға болатынын және, әрине, розеткаларда қалай ұйымдастырылатынын көрсеттім. Дереккөздер төменде қайта реттелген және талданатын бағдарламаның бастапқы codeы да қоса берілген. Бұл менің мақала жазудағы алғашқы тәжірибем) Назарларыңызға рахмет :)
  1. Java Enterprise-де ойлау, Брюс Эккел және т. Әл. 2003
  2. Java 8, Толық нұсқаулық, Герберт Шилдт, 9-шы басылым, 2017 (22-тарау)
  3. Java тіліндегі розеткаларды бағдарламалау розеткалар туралы мақала
  4. Ресми құжаттамадағы розетка
  5. Ресми құжаттамадағы ServerSocket
  6. GitHub сайтындағы дереккөздер
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION