لماذا تعتبر برمجة الشبكات في Java قوة خارقة؟
تخيل: أنت تكتب كودًا، وفجأة يبدأ برنامجك في التواصل مع برنامج آخر على الجانب الآخر من العالم. يرسل الطلبات، ويتلقى الردود، ويزامن البيانات - وكل ذلك في أجزاء من الثانية. هذا ليس سحرًا، إنها برمجة الشبكات في Java. إذا كنت ترغب في إنشاء ليس فقط أدوات محلية، بل أنظمة موزعة حقيقية، ودردشات، وألعاب عبر الإنترنت، أو خدمات REST - فلا مفر من برمجة الشبكات.
توفر Java مجموعة أدوات قوية لذلك: من Socket و ServerSocket الكلاسيكية إلى Java NIO عالي الأداء (الإدخال/الإخراج غير المحظور) و WebSockets الحديثة. في هذه المقالة، سنستعرض جميع الأساليب الثلاثة، ونكتب كودًا حيًا، ونفهم كيفية اختيار الأداة المناسبة لمهمتك. هيا بنا!
1. المقابس الكلاسيكية: دردشة TCP "مرتجلة"
لنبدأ بالأساسيات. المقابس في Java هي نقطة الدخول إلى عالم TCP/IP. يقوم الخادم بإنشاء ServerSocket، ويستمع إلى المنفذ، ويقبل الاتصالات. يقوم العميل بإنشاء Socket ويتصل بالخادم. الأمر بسيط للغاية.
كتابة خادم صدى بسيط
يقبل الخادم رسالة من العميل ويعيد إرسالها (صدى). الكود بسيط، لكنه يوضح الجوهر:
import java.io.*;import java.net.*;
public class EchoServer { public static void main(String[] args) { try (ServerSocket serverSocket = new ServerSocket(12345)) { System.out.println("الخادم قيد التشغيل على المنفذ 12345..."); Socket clientSocket = serverSocket.accept(); // يتم حظره حتى الاتصال System.out.println("العميل متصل: " + clientSocket.getInetAddress());
BufferedReader in = new BufferedReader( new InputStreamReader(clientSocket.getInputStream())); PrintWriter out = new PrintWriter( clientSocket.getOutputStream(), true);
String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println("تم الاستلام: " + inputLine); out.println("الصدى: " + inputLine); if (inputLine.equalsIgnoreCase("exit")) break; } } catch (IOException e) { e.printStackTrace(); } }}إليك العميل الذي يرسل الرسائل:
import java.io.*;import java.net.*;
public class EchoClient { public static void main(String[] args) throws IOException { Socket socket = new Socket("localhost", 12345); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); BufferedReader stdIn = new BufferedReader( new InputStreamReader(System.in));
String userInput; System.out.println("أدخل رسالة (exit للخروج):"); while ((userInput = stdIn.readLine()) != null) { out.println(userInput); System.out.println("رد الخادم: " + in.readLine()); if (userInput.equalsIgnoreCase("exit")) break; } socket.close(); }}ما المهم هنا؟ الطريقتان accept() و readLine() تحجبان الخيط. هذا يعني أنه بينما يتواصل عميل واحد، ينتظر الآخرون. الحل هو تعدد الخيوط.
خادم متعدد الخيوط: معالجة العديد من العملاء
لكل اتصال جديد، نقوم بإنشاء خيط منفصل. نستخدم تجمع الخيوط ExecutorService لتجنب إنشاء آلاف الخيوط دون داع:
import java.io.*;import java.net.*;import java.util.concurrent.*;
public class MultiThreadedServer { private static final int PORT = 12345; private static final ExecutorService threadPool = Executors.newFixe