JavaRush /وبلاگ جاوا /Random-FA /کلاس های Socket و ServerSocket یا “Hello, server? صدای من...
Sergey Simonov
مرحله
Санкт-Петербург

کلاس های Socket و ServerSocket یا “Hello, server? صدای من را می شنوی؟"

در گروه منتشر شد
مقدمه: "روی میز یک کامپیوتر بود، پشت آن یک رمزگذار..." کلاس های Socket و ServerSocket یا “Hello, server?  صدای من را می شنوی؟"  - 1 روزی روزگاری یکی از همکلاسی هایم نتیجه دیگری از مطالعه خود در جاوا را در قالب اسکرین شات یک برنامه جدید منتشر کرد. این برنامه یک چت چند کاربره بود. در آن زمان من تازه سفر خود را در تسلط بر برنامه نویسی به این زبان آغاز می کردم، اما قطعاً به خودم اشاره کردم که "من آن را می خواهم!" زمان گذشت و پس از پایان کار بر روی پروژه بعدی به عنوان بخشی از تعمیق دانش برنامه نویسی، آن حادثه را به خاطر آوردم و تصمیم گرفتم که زمان آن فرا رسیده است. به نوعی من از قبل صرفاً از روی کنجکاوی شروع به کندوکاو در این موضوع کردم، اما در کتاب درسی اصلی جاوا من (این کتاب راهنمای کامل Schildt بود) فقط 20 صفحه برای بسته java.net ارائه شده است. این قابل درک است - کتاب در حال حاضر بسیار بزرگ است. جدول‌هایی از متدها و سازنده‌های کلاس‌های اصلی وجود داشت، اما همین. البته قدم بعدی گوگل قادر متعال است: هزاران مقاله مختلف که در آن موارد مشابه ارائه شده است - دو یا سه کلمه در مورد سوکت ها و یک مثال آماده. رویکرد کلاسیک (حداقل در سبک مطالعه من) این است که ابتدا بفهمم به چه ابزاری برای کار نیاز دارم، چه ابزارهایی هستند، چرا به آنها نیاز دارند، و تنها پس از آن، اگر راه حل مشکل واضح نیست، با ابزار آماده سرهم بندی کنیم. لیست های ساخته شده، باز کردن پیچ و مهره ها. اما من فهمیدم چیست و در نهایت یک چت چند کاربره نوشتم. از نظر ظاهری، چیزی شبیه به این شد: کلاس های Socket و ServerSocket یا “Hello, server?  صدای من را می شنوی؟"  - 2در اینجا سعی خواهم کرد با استفاده از مثال طراحی چت، درکی از اصول برنامه های کاربردی سرویس گیرنده-سرور مبتنی بر سوکت های جاوا به شما ارائه دهم. در دوره جاوراش چت انجام می دهید. در سطحی کاملاً متفاوت، زیبا، بزرگ، چند منظوره خواهد بود. اما اول از همه، شما همیشه باید پایه و اساس را بگذارید، بنابراین در اینجا باید بفهمیم که چه چیزی زیربنای چنین بخشی است. (در صورت مشاهده هر گونه کاستی یا خطایی، در PM یا در نظر زیر مقاله بنویسید). شروع کنیم. Head One: "خانه ای که..." برای توضیح اینکه چگونه یک اتصال شبکه بین یک سرور و یک مشتری رخ می دهد، اجازه دهید مثال کلاسیک اکنون یک ساختمان آپارتمان را در نظر بگیریم. فرض کنید یک کلاینت باید به نحوی با یک سرور خاص ارتباط برقرار کند. جستجوگر باید در مورد شی جستجو چه بداند؟ بله آدرس سرور یک موجود جادویی در فضای ابری نیست و بنابراین باید روی یک ماشین خاص قرار گیرد. به قیاس خانه ای که در آن جلسه دو طرف توافق شده باید برگزار شود. و برای یافتن یکدیگر در یک ساختمان آپارتمان، یک آدرس ساختمان کافی نیست، باید شماره آپارتمانی که جلسه در آن برگزار می شود را مشخص کنید. به همین ترتیب، در یک رایانه می‌تواند چندین سرور به طور همزمان وجود داشته باشد، و برای اینکه مشتری بتواند با یک سرور خاص تماس بگیرد، باید شماره پورتی را نیز مشخص کند که از طریق آن اتصال انجام می‌شود. بنابراین، آدرس و شماره پورت. آدرس به معنای شناسه ماشین در فضای اینترنت است. این می تواند یک نام دامنه، به عنوان مثال، "javarush.ru" یا یک IP معمولی باشد. بندر- یک شماره منحصر به فرد که یک سوکت خاص با آن مرتبط است (این اصطلاح بعداً مورد بحث قرار خواهد گرفت)، به عبارت دیگر، توسط یک سرویس خاص اشغال شده است تا بتوان از آن برای تماس با آن استفاده کرد. بنابراین برای اینکه حداقل دو شی در قلمرو یکی (سرور) به هم برسند، صاحب منطقه (سرور) باید یک آپارتمان خاص (بندر) روی آن (خودرو) اشغال کند و دومی باید محل ملاقات را با دانستن آن بیابد. آدرس خانه (دامنه یا IP) و شماره آپارتمان (پورت). Head Two: Meet Socket در میان مفاهیم و اصطلاحات مربوط به کار در شبکه، یکی از موارد بسیار مهم Socket است. نشان دهنده نقطه ای است که از طریق آن اتصال رخ می دهد. به زبان ساده، یک سوکت دو برنامه را در یک شبکه به هم متصل می کند. کلاس Socketایده سوکت را اجرا می کند. کلاینت و سرور از طریق کانال های ورودی/خروجی خود ارتباط برقرار می کنند: کلاس های Socket و ServerSocket یا “Hello, server?  صدای من را می شنوی؟"  - 3 این کلاس در سمت کلاینت اعلام می شود و سرور آن را دوباره ایجاد می کند و سیگنال اتصال را دریافت می کند. ارتباط آنلاین به این صورت است. برای شروع، در اینجا سازندگان کلاس ممکن است Socket:
Socket(String Name_хоста, int порт) throws UnknownHostException, IOException
Socket(InetAddress IP-address, int порт) throws UnknownHostException
"host_name" - دلالت بر یک گره شبکه خاص، آدرس IP دارد. اگر کلاس سوکت نتواند آن را به یک آدرس واقعی و موجود تبدیل کند، یک استثنا ایجاد می شود UnknownHostException. بندر یک بندر است. اگر 0 به عنوان شماره پورت مشخص شود، خود سیستم یک پورت رایگان را اختصاص می دهد. اگر اتصال قطع شود، ممکن است یک استثنا نیز رخ دهد IOException. لازم به ذکر است که نوع آدرس در سازنده دوم InetAddress. به عنوان مثال، زمانی که شما نیاز دارید یک نام دامنه را به عنوان یک آدرس مشخص کنید، کمک می کند. همچنین، زمانی که دامنه به معنای چندین آدرس IP باشد، InetAddressمی توانید از آنها برای دریافت آرایه ای از آنها استفاده کنید. با این حال، با IP نیز کار می کند. همچنین می توانید نام میزبان، آرایه بایتی که آدرس IP را تشکیل می دهد و غیره را دریافت کنید. ما کمی بیشتر به آن خواهیم پرداخت، اما برای جزئیات کامل باید به اسناد رسمی بروید. هنگامی که یک شی از نوع مقدار دهی اولیه می شود Socket، کلاینتی که به آن تعلق دارد در شبکه اعلام می کند که می خواهد با یک آدرس و شماره پورت خاص به سرور متصل شود. در زیر متدهای پرکاربرد کلاس آمده است Socket: InetAddress getInetAddress()- یک شی حاوی داده‌های مربوط به سوکت را برمی‌گرداند. اگر سوکت متصل نباشد - null int getPort()- پورتی را که اتصال به سرور روی آن انجام می شود برمی گرداند int getLocalPort()- پورتی را که سوکت به آن متصل است برمی گرداند. واقعیت این است که کلاینت و سرور می توانند در یک پورت "ارتباط" کنند، اما پورت هایی که به آنها متصل می شوند می توانند کاملاً متفاوت باشند boolean isConnected()- در صورت برقراری اتصال درست است void connect(SocketAddress address)- نشان دهنده اتصال جدید است boolean isClosed()- درست است، اگر سوکت بسته باشد. boolean isBound()- true را برمی گرداند، اگر سوکت در واقع به یک آدرس محدود شده باشد، کلاس Socketرابط را پیاده سازی می کند AutoCloseable، بنابراین می توان از آن در آدرس استفاده کرد try-with-resources. با این حال، شما همچنین می توانید یک سوکت را به روش کلاسیک، با استفاده از close() ببندید. سر سه: و این یک ServerSocket است. فرض کنید ما در قالب یک کلاس Socket، یک درخواست اتصال در سمت کلاینت اعلام کردیم. سرور چگونه آرزوی ما را حدس می زند؟ برای این کار، سرور یک کلاس مانند ServerSocket، و متد accept() در آن دارد. سازنده های آن در زیر ارائه شده است:
ServerSocket() throws IOException
ServerSocket(int порт) throws IOException
ServerSocket(int порт, int максимум_подключений) throws IOException
ServerSocket(int порт, int максимум_подключений, InetAddress локальный_address) throws IOException
هنگام اعلام، ServerSocket نیازی به تعیین آدرس اتصال ندارید، زیرا ارتباط در ماشین سرور انجام می شود. فقط با یک هاست چند کاناله باید مشخص کنید سوکت سرور به کدام IP متصل است. Head Three.One: سروری که نه می گوید از آنجایی که ارائه یک برنامه با منابع بیشتر از نیازش پرهزینه و غیر منطقی است، بنابراین در سازنده از ServerSocketشما خواسته می شود حداکثر اتصالات پذیرفته شده توسط سرور را در حین کار اعلام کنید. اگر مشخص نشده باشد، به طور پیش فرض این عدد برابر با 50 در نظر گرفته می شود. بله، در تئوری می توانیم فرض کنیم که ServerSocketاین همان سوکت است، فقط برای سرور. اما نقش کاملاً متفاوتی نسبت به کلاس دارد Socket. فقط در مرحله ایجاد اتصال مورد نیاز است. پس از ایجاد یک شیء نوع، ServerSocketباید متوجه شوید که شخصی می خواهد به سرور متصل شود. متد ()accept در اینجا متصل است. هدف منتظر می ماند تا کسی بخواهد به آن متصل شود، و زمانی که این اتفاق می افتد، یک شی از نوع Socket، یعنی یک سوکت مشتری بازسازی شده را برمی گرداند. و اکنون که سوکت مشتری در سمت سرور ایجاد شده است، ارتباط دو طرفه می تواند آغاز شود. ایجاد یک نوع شی Socketدر سمت کلاینت و ایجاد مجدد آن با استفاده از ServerSocketسمت سرور حداقل مورد نیاز برای اتصال است. سر چهار: نامه به بابا نوئل Вопрос: دقیقاً چگونه مشتری و سرور با هم ارتباط برقرار می کنند؟ Ответ:از طریق جریان های I/O. ما قبلاً چه چیزی داریم؟ یک سوکت با آدرس سرور و شماره پورت مشتری، و همین مورد، به لطف ()، در سمت سرور. بنابراین منطقی است که فرض کنیم آنها از طریق یک سوکت ارتباط برقرار می کنند. برای انجام این کار، دو روش وجود دارد که امکان دسترسی به جریان ها InputStreamو OutputStreamاشیاء از نوع را فراهم می کند Socket. آن ها اینجا هستند:
InputStream getInputStream()
OutputStream getOutputStream()
از آنجایی که خواندن و نوشتن بایت‌های خالی چندان کارآمد نیست، جریان‌ها را می‌توان در کلاس‌های آداپتور، چه بافر یا بدون بافر، قرار داد. مثلا:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
برای اینکه ارتباط دو طرفه باشد، چنین عملیاتی باید در هر دو طرف انجام شود. اکنون می توانید چیزی را با استفاده از in ارسال کنید، و چیزی را با استفاده از خارج دریافت کنید و بالعکس. در واقع، این عملا تنها عملکرد کلاس است 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 واقعی باید یک سرور کامل داشته باشید و از آنجایی که:
  1. چت آینده ما، به عنوان یک ابزار، چنین توانایی هایی را ندارد. فقط می تواند اتصال برقرار کند و پیامی را دریافت/ارسال کند. یعنی قابلیت های سرور واقعی را ندارد.
  2. سرور ما، که فقط حاوی داده‌های سوکت و جریان‌های ورودی/خروجی است، نمی‌تواند به‌عنوان یک سرور وب یا FTP واقعی کار کند، پس فقط با این کار نمی‌توانیم از طریق اینترنت وصل شویم.
و علاوه بر این، ما تازه شروع به توسعه برنامه کرده ایم، به این معنی که به اندازه کافی پایدار نیست که بلافاصله با یک شبکه واقعی کار کند، بنابراین از میزبان محلی به عنوان آدرس اتصال استفاده می کنیم. یعنی در تئوری، کلاینت و سرور هنوز به هیچ وجه به جز از طریق یک سوکت متصل نخواهند شد، اما برای اشکال زدایی برنامه روی یک دستگاه خواهند بود، بدون تماس واقعی از طریق شبکه. برای اینکه در سازنده نشان دهیم 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")
برای سادگی، از "localhost" از نوع String استفاده می کنیم. اما همه گزینه های دیگر نیز قابل اجرا هستند. سر شش: زمان مکالمه فرا رسیده است ، بنابراین، ما از قبل هر آنچه را که برای اجرای یک جلسه مکالمه با سرور نیاز داریم، در اختیار داریم. تنها چیزی که باقی می ماند این است که آن را کنار هم قرار دهیم: فهرست زیر نشان می دهد که چگونه مشتری به سرور متصل می شود، یک پیام برای آن ارسال می کند و سرور نیز به نوبه خود تأیید می کند که پیام را با استفاده از آن به عنوان آرگومان در خود دریافت کرده است: "سرور. جاوا"
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);
        }

    }
}
البته، ابتدا باید سرور را راه اندازی کنید، زیرا در هنگام راه اندازی کلاینت به چه چیزی متصل می شود که آن را متصل کند؟ :) خروجی به این صورت خواهد بود: /* چیزی می خواستی بگویی؟ آن را اینجا وارد کنید: سلام سرور؟ صدای من را می شنوی؟ سلام، این سرور است! تایید میکنم نوشتی: سلام سرور؟ صدای من را می شنوی؟ مشتری بسته شد... */ هورا! ما به سرور آموزش دادیم که با مشتری ارتباط برقرار کند! به طوری که ارتباط نه در دو نسخه، بلکه به تعداد دلخواه انجام می شود، به سادگی خواندن و نوشتن موضوعات را در یک حلقه while (true) بپیچید و برای خروج نشان دهید که طبق یک پیام خاص، به عنوان مثال، "خروج" ، چرخه قطع شد و برنامه به پایان رسید. Head Seven: چند کاربر بهتر است این که سرور می تواند صدای ما را بشنود خوب است، اما اگر بتوانیم با یکی از هم نوعان خود ارتباط برقرار کنیم بسیار بهتر است. من تمام منابع را در پایان مقاله ضمیمه خواهم کرد، بنابراین در اینجا کدهایی نه همیشه بزرگ، اما مهم را نشان خواهم داد که در صورت استفاده صحیح، امکان ساخت یک چت چند کاربره را فراهم می کند. بنابراین، ما می خواهیم بتوانیم از طریق سرور با مشتری دیگری ارتباط برقرار کنیم. چگونه انجامش بدهیم؟ بدیهی است که از آنجایی که برنامه کلاینت روش خاص خود را دارد main، به این معنی است که می توان آن را جدا از سرور و به موازات سایر کلاینت ها راه اندازی کرد. این چه چیزی به ما می دهد؟ به نحوی لازم است که با هر اتصال جدید سرور بلافاصله به سمت ارتباط نرود، بلکه این اتصال را در لیستی بنویسد و منتظر یک اتصال جدید باشد و نوعی سرویس کمکی درگیر ارتباط با یک خاص باشد. مشتری. و کلاینت ها باید به سرور بنویسند و مستقل از یکدیگر منتظر پاسخ باشند. نخ ها به کمک می آیند. فرض کنید کلاسی داریم که مسئول به خاطر سپردن اتصالات جدید است: باید موارد زیر را مشخص کند:
  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();
        }
    }
}
خوب، اکنون هر سوکت بازسازی شده از بین نمی رود، بلکه در سرور ذخیره می شود. به علاوه. هر مشتری به کسی نیاز دارد که گوش کند. بیایید یک موضوع با توابع سرور از فصل آخر ایجاد کنیم.
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) {}
    }
}
بنابراین، در سازنده thread سرور، یک سوکت باید مقداردهی اولیه شود که از طریق آن موضوع با یک مشتری خاص ارتباط برقرار کند. همچنین رشته های ورودی/خروجی و هر چیز دیگری که برای شروع یک رشته به طور مستقیم از سازنده نیاز دارید. بسیار خوب، اما چه اتفاقی می‌افتد وقتی تاپیک سرور پیامی را از مشتری می‌خواند؟ فقط به مشتری شما ارسال شود؟ خیلی موثر نیست ما در حال ایجاد یک چت چند کاربره هستیم، بنابراین به هر مشتری متصل نیاز داریم تا آنچه را که یک نفر نوشته است دریافت کند. شما باید از لیست تمام رشته های سرور مرتبط با مشتریان خود استفاده کنید و هر پیام ارسال شده را به یک رشته خاص ارسال کنید تا آن را به مشتری خود ارسال کند:
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) {}
}
اکنون همه مشتریان می دانند که یکی از آنها چه گفته است! اگر نمی خواهید پیام برای شخصی که آن را ارسال کرده است ارسال شود (او از قبل می داند که چه چیزی نوشته است!)، به سادگی هنگام تکرار از طریق رشته ها، مشخص کنید که هنگام پردازش یک شی، حلقه thisبدون انجام به عنصر بعدی منتقل شود. هر گونه اقدام بر روی آن یا در صورت تمایل، پیامی به مشتری مبنی بر اینکه پیام با موفقیت دریافت و ارسال شده است ارسال کنید. در حال حاضر همه چیز با سرور مشخص است. بیایید به سمت مشتری یا بهتر است بگوییم مشتریان! همه چیز در آنجا یکسان است، به قیاس با کلاینت از فصل آخر، فقط هنگام ایجاد یک نمونه، همانطور که در این فصل با سرور نشان داده شد، نیاز دارید تا همه چیز لازم را در سازنده ایجاد کنید. اما اگر هنگام ایجاد مشتری، او هنوز وقت نداشته باشد چیزی وارد کند، اما قبلاً چیزی برای او ارسال شده باشد، چه؟ (مثلاً سابقه مکاتبات کسانی که قبلاً به چت قبل از او وصل شده اند). بنابراین چرخه‌هایی که در آن پیام‌های ارسالی پردازش می‌شوند باید از چرخه‌هایی که در آن پیام‌ها از کنسول خوانده می‌شوند و برای ارسال به سایرین به سرور ارسال می‌شوند، جدا شوند. نخ ها دوباره به کمک می آیند. هیچ فایده ای برای ایجاد یک مشتری به عنوان یک موضوع وجود ندارد. راحت تر است که یک رشته با یک حلقه در روش اجرا ایجاد کنید که پیام ها را می خواند و همچنین به طور قیاسی می نویسد:
// 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) {

            }

        }
    }
}
در سازنده کلاینت فقط باید این رشته ها را شروع کنید. اگر مشتری می خواهد آن را ترک کند، چگونه می توان منابع مشتری را به درستی بست؟ آیا باید منابع موضوع سرور را ببندم؟ برای انجام این کار، به احتمال زیاد باید یک متد جداگانه ایجاد کنید که هنگام خروج از حلقه پیام فراخوانی می شود. در آنجا باید سوکت و جریان های ورودی/خروجی را ببندید. همان سیگنال پایان جلسه برای یک کلاینت خاص باید به رشته سرور آن ارسال شود، که باید همین کار را با سوکت خود انجام دهد و خود را از لیست رشته های کلاس سرور اصلی حذف کند. Head Eight: هیچ محدودیتی برای کمال وجود ندارد شما می توانید بی نهایت ویژگی های جدیدی را برای بهبود پروژه خود اختراع کنید. اما دقیقاً چه چیزی باید به یک مشتری تازه متصل منتقل شود؟ فکر می کنم ده اتفاق آخر قبل از آمدنش رخ داده است. برای انجام این کار، باید کلاسی ایجاد کنید که در آن آخرین اکشن با هر رشته سرور در لیست اعلام شده وارد شود و اگر لیست از قبل پر است (یعنی قبلاً 10 عدد وجود دارد)، اولین مورد را حذف کنید و اضافه کنید. آخرینی که آمد برای اینکه محتویات این لیست توسط یک اتصال جدید دریافت شود، هنگام ایجاد یک موضوع سرور، در جریان خروجی، باید آنها را برای مشتری ارسال کنید. چگونه انجامش بدهیم؟ به عنوان مثال، مانند این:
public void printStory(BufferedWriter writer) {
// ...
}
رشته سرور قبلاً جریان‌هایی ایجاد کرده است و می‌تواند جریان خروجی را به عنوان آرگومان ارسال کند. در مرحله بعد، فقط باید همه چیزهایی را که باید در یک چرخه جستجو به مشتری جدید منتقل کنید، منتقل کنید. نتیجه گیری: این فقط اصول اولیه است و به احتمال زیاد این معماری چت هنگام ایجاد یک برنامه واقعی کار نخواهد کرد. این برنامه برای اهداف آموزشی ایجاد شده است و من بر اساس آن نشان دادم که چگونه می توانید مشتری را با سرور ارتباط برقرار کنید (و بالعکس)، چگونه این کار را برای چندین اتصال انجام دهید و البته نحوه سازماندهی آن در سوکت ها. منابع در زیر بازآرایی شده اند و کد منبع برنامه در حال تجزیه و تحلیل نیز پیوست شده است. این اولین تجربه من از نوشتن مقاله است) از توجه شما متشکرم :)
  1. تفکر در جاوا اینترپرایز، اثر بروس اکل و همکاران. ال. 2003
  2. جاوا 8، راهنمای قطعی، هربرت شیلد، ویرایش نهم، 2017 (فصل 22)
  3. مقاله برنامه نویسی سوکت در جاوا در مورد سوکت ها
  4. سوکت در اسناد رسمی
  5. ServerSocket در اسناد رسمی
  6. منابع در GitHub
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION