ජාවා Socket Programming: Client-Server මූලිකාංග - SL Dev Guide

ජාවා Socket Programming: Client-Server මූලිකාංග - SL Dev Guide

ආයුබෝවන් යාළුවනේ! කොහොමද ඉතින් ඔයාලට? අද අපි කතා කරන්න යන්නේ ටිකක් රසවත්, ඒ වගේම ඕනෑම Software Engineer කෙනෙක් දැනගෙන ඉන්නම ඕන ටොපික් එකක් ගැන. ඔයාලා හැමදාම පාවිච්චි කරන WhatsApp, Viber, Facebook Messenger වගේ Apps වැඩ කරන්නේ කොහොමද කියලා කවදාවත් හිතුවද? එහෙම නැත්නම් ඔන්ලයින් ගේම්ස් ගහද්දි ඔයාලා එකිනෙකා එක්ක කනෙක්ට් වෙලා සෙල්ලම් කරන්නේ කොහොමද? මේ හැමදේකම පිටිපස්සේ තියෙන ප්‍රධානම ටෙක්නොලොජි එකක් තමයි Network Programming කියන්නේ. විශේෂයෙන්ම, Client-Server Communication වල හදවත වගේ වැඩ කරන Socket Programming.

අද අපි Java වලින් Socket Programming කරන්නේ කොහොමද කියලා කතා කරමු. TCP/IP Sockets කියන්නේ මොනවද, Client එකක් Server එකක් හදාගෙන මේ දෙක අතරේ data යවාගන්නේ සහ ලබාගන්නේ කොහොමද කියලා, පොඩි practical example එකක් එක්කම අපි බලමු. මේක ඔයාලගේ දැනුමට ගොඩක් වටිනවා කියලා මට sure. එහෙනම් වැඩේට බහිමුද?

Socket Programming කියන්නේ මොකක්ද මේ?

සරලවම කිව්වොත්, Socket Programming කියන්නේ එක program එකකට තව program එකක් එක්ක, එකම computer එකේ තියෙනවා නම් හෝ වෙනස් computer දෙකක තියෙනවා නම් වුණත්, network එකක් හරහා කතා බහ කරන්න (data exchange කරන්න) පුළුවන් කරන ක්‍රමවේදයක්. මේක හරියට ඔයාලා ෆෝන් එකකින් කෙනෙක්ට කතා කරනවා වගේ වැඩක්. කතා කරන කෙනාට phone number එකක් තියෙනවා (ඒක තමයි IP address එක). ඒ phone number එකට කතා කරලා, එහා පැත්තේ ඉන්න නිශ්චිත කෙනාට (ඒක තමයි Port Number එක) කතා කරනවා වගේ වැඩක්.

Network එකකදී, IP address එකකින් computer එකක් identify කරනවා. හැබැයි ඒ computer එක ඇතුලේ programs ගොඩක් run වෙනවනේ. ඒවායින් නිශ්චිත program එකක් identify කරන්නේ Port Number එකකින්. ඉතින්, Socket එකක් කියන්නේ IP address එකයි Port Number එකයි දෙකම එකතු වෙලා network connection එකකට තියෙන endpoint එකක්. මේ Sockets තමයි data එහා මෙහා කරන්න පාර හදලා දෙන්නේ.

අපි අද කතා කරන්නේ TCP/IP Sockets ගැන. TCP (Transmission Control Protocol) කියන්නේ data යවද්දි අතරමං වෙන්නේ නැතුව, පිළිවෙළට, විශ්වාසදායක විදිහට යනවා කියලා sure කරන protocol එකක්. ඒක නිසා, Client-Server applications වලට TCP Sockets තමයි ගොඩක් වෙලාවට පාවිච්චි කරන්නේ.

Java වල TCP/IP Sockets - Clientයි Serverයි

Java වලදී TCP/IP Socket Programming කරන්න අපිට ප්‍රධාන Classes දෙකක් තියෙනවා:

  1. ServerSocket: මේක Server Side එකේදී පාවිච්චි කරනවා. Server එක network එකේ යම්කිසි Port Number එකක client connections එනකම් බලාගෙන ඉන්නවා. client කෙනෙක් connect වුණාම, ServerSocket එක අලුත් Socket object එකක් හදලා ඒ client එක්ක කතා බහ කරන්න පාර හදනවා.
  2. Socket: මේක Client Side එකේදී වගේම Server Side එක client එක්ක කතා බහ කරන්න use කරනවා. Client එකක් Socket object එකක් හදලා Server එකේ IP address එකටයි Port Number එකටයි connect වෙනවා. Connection එක හැදුනාට පස්සේ, client එකටයි server එකටයි මේ Socket එක හරහා data යවන්න පුළුවන්.

මේ දෙකේ communication එක වෙන්නේ Streams හරහා. Socket object එකෙන් අපිට InputStream එකක් (data කියවන්න) සහ OutputStream එකක් (data ලියන්න) ගන්න පුළුවන්. මේ Streams තමයි text, files, pictures වගේ මොනවා හරි data එකක් network එක හරහා එහා මෙහා කරන්න උදව් කරන්නේ.

අපි කෝඩ් කරලා බලමුද? - Server Side

හරි, දැන් අපි පොඩි Server application එකක් ලියලා බලමු. මේ Server එක කරන්නේ client කෙනෙක් connect වෙනකන් බලන් ඉඳලා, connect වුණාම එයාගෙන් message එකක් අරගෙන, ඒකට reply එකක් දෙන එකයි.


import java.io.*;
import java.net.*;

public class SimpleServer {
    public static void main(String[] args) {
        int port = 6000; // අපේ Server එකට Port එකක් දාමු
        ServerSocket serverSocket = null;
        Socket clientSocket = null; // Client connection එක Handle කරන්න

        try {
            // ServerSocket එක හදනවා, Port එක specify කරලා
            serverSocket = new ServerSocket(port);
            System.out.println("Server started on port " + port + ". Waiting for clients...");

            // client කෙනෙක් connect වෙනකම් බලාගෙන ඉන්නවා
            // client කෙනෙක් connect වුණාම, accept() method එකෙන් අලුත් Socket එකක් return කරනවා
            clientSocket = serverSocket.accept();
            System.out.println("Client connected: " + clientSocket.getInetAddress().getHostAddress());

            // client ගෙන් data කියවන්න InputStream එකක් හදනවා
            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

            // client ට data ලියන්න OutputStream එකක් හදනවා
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); // 'true' for auto-flush

            String clientMessage;
            while ((clientMessage = in.readLine()) != null) {
                System.out.println("Client says: " + clientMessage);
                if (clientMessage.equals("bye")) {
                    System.out.println("Client requested to close connection. Closing server connection.");
                    break;
                }
                out.println("Server received your message: \"" + clientMessage + "\"");
            }

        } catch (IOException e) {
            System.err.println("Error: " + e.getMessage());
        } finally {
            // connections වහන්න අමතක කරන්න එපා!
            try {
                if (clientSocket != null) clientSocket.close();
                if (serverSocket != null) serverSocket.close();
                System.out.println("Server connection closed.");
            } catch (IOException e) {
                System.err.println("Error closing connections: " + e.getMessage());
            }
        }
    }
}

මේ කෝඩ් එකේදී වෙන්නේ මොකක්ද කියලා පොඩ්ඩක් පැහැදිලි කරමු:

  • මුලින්ම, අපි Server එකට Port Number එකක් (6000) specify කරනවා. මේ Port එක හරහා තමයි Client ලා Server එකට connect වෙන්නේ.
  • new ServerSocket(port) කියන එකෙන් අපි ServerSocket object එකක් හදනවා. මේක network එකේ ඒ Port එකේ listen කරන්න පටන් ගන්නවා.
  • serverSocket.accept() කියන method එක තමයි මැජික් එක කරන්නේ. මේක blocking method එකක්. ඒ කියන්නේ, client කෙනෙක් connect වෙනකම් මේ method එක එතනම "හිරවෙලා" ඉන්නවා. Client කෙනෙක් connect වුණාම, මේ method එක අලුත් Socket object එකක් return කරනවා. මේ Socket object එක තමයි අර connect වුණ Client එක්ක කතා බහ කරන්න පාවිච්චි කරන්නේ.
  • ඊට පස්සේ, අපි InputStreamReader සහ BufferedReader පාවිච්චි කරලා client ගෙන් එන messages කියවන්න ready වෙනවා. OutputStream සහ PrintWriter පාවිච්චි කරලා client ට messages යවන්න ready වෙනවා.
  • while loop එකක් ඇතුලේ client ගෙන් එන messages කියවනවා. "bye" කියලා message එකක් ආවොත්, connection එක close කරනවා.
  • අන්තිමට finally block එක ඇතුලේ අපි Sockets වහනවා. මේක අනිවාර්යයෙන් කරන්න ඕන දෙයක්, නැත්නම් resources leak වෙන්න පුළුවන්.

Client Side එකත් එක්ක යමු!

දැන් අපි Server එකට connect වෙලා message එකක් යවන Client application එකක් හදමු.


import java.io.*;
import java.net.*;
import java.util.Scanner;

public class SimpleClient {
    public static void main(String[] args) {
        String serverAddress = "localhost"; // Server එක තියෙන තැන (මේකම නම් localhost)
        int port = 6000; // Server එකේ Port එක

        Socket socket = null;
        try {
            // Server එකට connect වෙන්න Socket එක හදනවා
            socket = new Socket(serverAddress, port);
            System.out.println("Connected to server: " + serverAddress + " on port " + port);

            // server එකට data ලියන්න OutputStream එකක් හදනවා
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

            // server එකෙන් data කියවන්න InputStream එකක් හදනවා
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            Scanner scanner = new Scanner(System.in); // Console එකෙන් input ගන්න

            String userInput;
            String serverResponse;

            while (true) {
                System.out.print("Enter message to server (type 'bye' to exit): ");
                userInput = scanner.nextLine(); // Console එකෙන් user input ගන්නවා

                out.println(userInput); // User input එක server එකට යවනවා

                if (userInput.equals("bye")) {
                    break;
                }

                // server එකෙන් එන reply එක කියවනවා
                serverResponse = in.readLine();
                System.out.println("Server says: " + serverResponse);
            }

            scanner.close(); // Scanner එක close කරනවා

        } catch (IOException e) {
            System.err.println("Error: " + e.getMessage());
        } finally {
            // connections වහන්න අමතක කරන්න එපා!
            try {
                if (socket != null) socket.close();
                System.out.println("Client connection closed.");
            } catch (IOException e) {
                System.err.println("Error closing client connection: " + e.getMessage());
            }
        }
    }
}

Client කෝඩ් එකේ වෙන්නේ මේ ටිකයි:

  • අපි Server එක තියෙන IP address එකයි (localhost කියන්නේ මේ computer එකම කියන එක) Port Number එකයි (6000) specify කරනවා.
  • new Socket(serverAddress, port) කියන එකෙන් Server එකට connect වෙන්න උත්සාහ කරනවා. Connection එක successful වුණොත්, Socket object එකක් හැදෙනවා.
  • Server Side එකේ වගේම, අපි PrintWriter එකක් data server එකට යවන්නත්, BufferedReader එකක් server එකෙන් එන data කියවන්නත් හදනවා.
  • Scanner එකක් පාවිච්චි කරලා user ගෙන් input අරගෙන, ඒ input එක server එකට යවනවා. Server එකෙන් reply එකක් ආවොත් ඒක print කරනවා.
  • "bye" කියලා type කළොත් client application එක close වෙනවා.
  • අන්තිමට finally block එකේදී Socket එක close කරනවා.

මේ දෙක run කරන්නේ කොහොමද?

  1. මුලින්ම SimpleServer.java compile කරලා run කරන්න. Console එකේ "Server started on port 6000. Waiting for clients..." කියලා පෙන්වයි.
  2. ඊට පස්සේ, වෙනම terminal/command prompt එකක් ඇරලා SimpleClient.java compile කරලා run කරන්න.
  3. දැන් Client එකෙන් message type කරලා enter කරන්න. Server console එකේ ඒ message එක පෙන්වලා, Client console එකට Server එකෙන් reply එකක් ආවා කියලා පෙන්වයි.
  4. "bye" කියලා type කරලා දෙපැත්තෙම connection close කරන්න පුළුවන්.

සුපිරි නේද වැඩේ! මේක තමයි Client-Server communication වල මූලිකම පදනම. මේකෙන් ඔයාලට ඔයාලගේම Chat application එකක් වුණත් හදාගන්න පුළුවන්.

මතක තියාගන්න ඕන වැදගත් දේවල්

  • Error Handling (try-catch-finally): Network operations වලදී errors (e.g., server not found, connection lost) එන්න පුළුවන්. ඒ නිසා try-catch-finally blocks පාවිච්චි කරලා errors handle කරන එක අනිවාර්යයි.
  • Closing Resources: Sockets, InputStreams, OutputStreams වගේ දේවල් පාවිච්චි කරලා ඉවර වුණාම close() කරන්න අමතක කරන්න එපා. නැත්නම් memory leaks සහ resource exhaustion වෙන්න පුළුවන්. finally block එක මේකට හොඳම තැන.
  • Blocking Operations: accept(), readLine() වගේ methods blocking methods. ඒ කියන්නේ, ඒ method එක complete වෙනකම් program එකේ execution එක එතනම නැවතිලා ඉන්නවා. Production applications වලදී මේ වගේ blocking operations threads වල run කරන එක තමයි හොඳ, එතකොට main thread එක block වෙන්නේ නැහැ.
  • Multiple Clients: අපි හදපු Server එක එක පාරට client කෙනෙක්ට විතරයි handle කරන්න පුළුවන්. ගොඩක් client ලා handle කරන්න නම්, ServerSocket එක accept() කළාට පස්සේ හැම client connection එකක්ම අලුත් Thread එකකට භාර දෙන්න ඕන. ඒ ගැන අපි ඊළඟ ලිපියකදී කතා කරමු.
  • Port Numbers: Port numbers 0-65535 අතර වෙනවා. 0-1023 අතර ports "well-known ports" විදිහට හඳුන්වනවා (e.g., HTTP වලට 80, HTTPS වලට 443). මේවා පාවිච්චි කරන්න administrator privileges ඕන වෙන්න පුළුවන්. ඒ නිසා අපේ වගේ applications වලට 1024ට වැඩි ports (e.g., 6000, 8080) පාවිච්චි කරන එක හොඳයි.

හරි යාළුවනේ, මේක තමයි Java වල Socket Programming වල මූලිකම ටික. ඔයාලා දැක්කා නේද කොච්චර ලේසියෙන් Client එකයි Server එකයි අතරේ data එහා මෙහා කරගන්න පුළුවන්ද කියලා? මේ දැනුමෙන් ඔයාලට තව ගොඩක් දේවල් කරන්න පුළුවන්. Distributed systems, network monitoring tools, හෝ ඔයාලගේම multiplayer game එකක් වගේ දේවල් හදන්න මේක මුල් පියවරක් වෙන්න පුළුවන්.

දැන් ඉතින් අතපය බැඳගෙන ඉන්න එපා. කෝඩ් එක copy කරලා, paste කරලා, run කරලා බලන්න. පොඩි පොඩි වෙනස්කම් කරලා මොනවා වෙනවද කියලා experiment කරන්න. මතක තියාගන්න, coding කියන්නේ කරලාම ඉගෙන ගන්න පුළුවන් දෙයක්.

මේ ලිපිය ගැන ඔයාලගේ අදහස්, ප්‍රශ්න, යෝජනා පහළින් comment කරන්න අමතක කරන්න එපා. තව මොන වගේ topics ගැනද ඔයාලට දැනගන්න ඕන කියලා කියන්නත් පුළුවන්. ඊළඟ ලිපියකින් නැවත හමුවෙමු! Stay tuned for more awesome tech stuff!