ونڈوز کلائنٹ سرور یوٹیلیٹیز کی فعالیت کے ساتھ سافٹ ویئر لکھنا، حصہ 02

ونڈوز کنسول یوٹیلیٹیز کے حسب ضرورت نفاذ کے لیے وقف مضامین کی جاری سیریز کو جاری رکھتے ہوئے، ہم TFTP (Trivial File Transfer Protocol) - ایک سادہ فائل ٹرانسفر پروٹوکول کو چھوئے بغیر مدد نہیں کر سکتے۔

پچھلی بار کی طرح، آئیے مختصراً نظریہ پر جائیں، کوڈ دیکھیں جو مطلوبہ کی طرح فعالیت کو نافذ کرتا ہے، اور اس کا تجزیہ کریں۔ مزید تفصیلات - کٹ کے نیچے

میں حوالہ کی معلومات کو کاپی پیسٹ نہیں کروں گا، جن کے لنکس روایتی طور پر مضمون کے آخر میں مل سکتے ہیں، میں صرف اتنا کہوں گا کہ اس کے بنیادی طور پر، TFTP FTP پروٹوکول کی ایک آسان تبدیلی ہے، جس میں رسائی کنٹرول کی ترتیب ہٹا دیا گیا ہے، اور درحقیقت یہاں فائل وصول کرنے اور منتقل کرنے کے کمانڈز کے علاوہ کچھ نہیں ہے۔ تاہم، ہمارے نفاذ کو کچھ زیادہ خوبصورت اور تحریری کوڈ کے موجودہ اصولوں کے مطابق ڈھالنے کے لیے، نحو کو قدرے تبدیل کر دیا گیا ہے - اس سے آپریشن کے اصولوں میں کوئی تبدیلی نہیں آئی، لیکن انٹرفیس، IMHO، قدرے زیادہ منطقی ہو جاتا ہے اور FTP اور TFTP کے مثبت پہلوؤں کو یکجا کرتا ہے۔

خاص طور پر، جب لانچ کیا جاتا ہے، کلائنٹ سرور کے آئی پی ایڈریس اور اس پورٹ کی درخواست کرتا ہے جس پر کسٹم TFTP کھلا ہے (معیاری پروٹوکول کے ساتھ عدم مطابقت کی وجہ سے، میں نے صارف کو پورٹ منتخب کرنے کی صلاحیت چھوڑنا مناسب سمجھا)، جس کے بعد کنکشن ہوتا ہے، جس کے نتیجے میں کلائنٹ سرور کو فائل وصول کرنے یا بھیجنے کے لیے ایک کمانڈ - get or put، بھیج سکتا ہے۔ منطق کو آسان بنانے کے لیے تمام فائلیں بائنری موڈ میں بھیجی جاتی ہیں۔

پروٹوکول کو لاگو کرنے کے لئے، میں نے روایتی طور پر 4 کلاسز کا استعمال کیا:

  • TFTPC کلائنٹ
  • TFTPSسرور
  • TFTPC کلائنٹ ٹیسٹر
  • TFTPServerTester

اس حقیقت کی وجہ سے کہ ٹیسٹنگ کلاسز صرف اہم کو ڈیبگ کرنے کے لیے موجود ہیں، میں ان کا تجزیہ نہیں کروں گا، لیکن کوڈ ریپوزٹری میں ہوگا؛ مضمون کے آخر میں اس کا لنک مل سکتا ہے۔ اب میں مین کلاسز کو دیکھوں گا۔

TFTPC کلائنٹ

اس کلاس کا کام اپنے آئی پی اور پورٹ نمبر کا استعمال کرتے ہوئے ریموٹ سرور سے جڑنا، ان پٹ اسٹریم (اس معاملے میں، کی بورڈ) سے کمانڈ پڑھنا، اسے پارس کرنا، اسے سرور پر منتقل کرنا، اور اس بات پر منحصر ہے کہ آیا آپ فائل بھیجنے یا وصول کرنے، اسے منتقل کرنے یا حاصل کرنے کی ضرورت ہے۔

کلائنٹ کو سرور سے منسلک کرنے اور ان پٹ اسٹریم سے کمانڈ کا انتظار کرنے کے لیے کوڈ اس طرح نظر آتا ہے۔ یہاں استعمال ہونے والے متعدد عالمی متغیرات کو مضمون کے باہر پروگرام کے مکمل متن میں بیان کیا گیا ہے۔ ان کی معمولی باتوں کی وجہ سے، میں ان کا حوالہ نہیں دیتا تاکہ مضمون پر زیادہ بوجھ نہ پڑے۔

 public void run(String ip, int port)
    {
        this.ip = ip;
        this.port = port;
        try {
            inicialization();
            Scanner keyboard = new Scanner(System.in);
            while (isRunning) {
                getAndParseInput(keyboard);
                sendCommand();
                selector();
                }
            }
        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

آئیے کوڈ کے اس بلاک میں کہے گئے طریقوں پر غور کریں:

یہاں فائل بھیجی جاتی ہے - اسکینر کا استعمال کرتے ہوئے، ہم فائل کے مواد کو بائٹس کی ایک صف کے طور پر پیش کرتے ہیں، جسے ہم ایک ایک کرکے ساکٹ پر لکھتے ہیں، جس کے بعد ہم اسے بند کرکے دوبارہ کھولتے ہیں (سب سے واضح حل نہیں، لیکن یہ وسائل کی رہائی کی ضمانت دیتا ہے) جس کے بعد ہم کامیاب منتقلی کے بارے میں ایک پیغام ڈسپلے کرتے ہیں۔

private  void put(String sourcePath, String destPath)
    {

        File src = new File(sourcePath);
        try {

            InputStream scanner = new FileInputStream(src);
            byte[] bytes = scanner.readAllBytes();
            for (byte b : bytes)
                sout.write(b);
            sout.close();
            inicialization();
            System.out.println("nDonen");
            }

        catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

یہ کوڈ کا ٹکڑا سرور سے ڈیٹا کی بازیافت کی وضاحت کرتا ہے۔ سب کچھ پھر معمولی ہے، کوڈ کا صرف پہلا بلاک دلچسپی کا ہے۔ یہ سمجھنے کے لیے کہ ساکٹ سے کتنے بائٹس پڑھنے کی ضرورت ہے، آپ کو یہ جاننا ہوگا کہ منتقل شدہ فائل کا وزن کتنا ہے۔ سرور پر فائل کے سائز کو لمبے عدد کے طور پر دکھایا جاتا ہے، اس لیے یہاں 4 بائٹس قبول کیے جاتے ہیں، جو بعد میں ایک نمبر میں تبدیل ہو جاتے ہیں۔ یہ بہت زیادہ جاوا اپروچ نہیں ہے، بلکہ یہ SI کے لیے بھی ایسا ہی ہے، لیکن یہ اس کا مسئلہ حل کرتا ہے۔

پھر سب کچھ معمولی ہے - ہم ساکٹ سے معلوم تعداد میں بائٹس وصول کرتے ہیں اور انہیں فائل میں لکھتے ہیں، جس کے بعد ہم کامیابی کا پیغام ظاہر کرتے ہیں۔

   private void get(String sourcePath, String destPath){
        long sizeOfFile = 0;
        try {


            byte[] sizeBytes = new byte[Long.SIZE];
           for (int i =0; i< Long.SIZE/Byte.SIZE; i++)
           {
               sizeBytes[i] = (byte)sin.read();
               sizeOfFile*=256;
               sizeOfFile+=sizeBytes[i];
           }

           FileOutputStream writer = new FileOutputStream(new File(destPath));
           for (int i =0; i < sizeOfFile; i++)
           {
               writer.write(sin.read());
           }
           writer.close();
           System.out.println("nDONEn");
       }
       catch (Exception e){
            System.out.println(e.getMessage());
       }
    }

اگر کلائنٹ ونڈو میں get یا put کے علاوہ کوئی اور کمانڈ داخل کی گئی ہے تو showErrorMessage فنکشن کو کال کیا جائے گا، جو اس بات کی نشاندہی کرتا ہے کہ ان پٹ غلط تھا۔ معمولی بات کی وجہ سے، میں اس کا حوالہ نہیں دوں گا۔ کچھ زیادہ دلچسپ ان پٹ سٹرنگ کو وصول کرنے اور تقسیم کرنے کا کام ہے۔ ہم اسکینر کو اس میں منتقل کرتے ہیں، جس سے ہمیں ایک لائن موصول ہونے کی امید ہوتی ہے جو دو جگہوں سے الگ ہوتی ہے اور اس میں کمانڈ، سورس ایڈریس اور منزل کا پتہ ہوتا ہے۔

    private void getAndParseInput(Scanner scanner)
    {
        try {

            input = scanner.nextLine().split(" ");
            typeOfCommand = input[0];
            sourcePath = input[1];
            destPath = input[2];
        }
        catch (Exception e) {
            System.out.println("Bad input");
        }
    }

کمانڈ بھیجنا - سکینر سے داخل کردہ کمانڈ کو ساکٹ میں منتقل کرتا ہے اور اسے بھیجنے پر مجبور کرتا ہے۔

    private void sendCommand()
    {
        try {

            for (String str : input) {
                for (char ch : str.toCharArray()) {
                    sout.write(ch);
                }
                sout.write(' ');
            }
            sout.write('n');
        }
        catch (Exception e) {
            System.out.print(e.getMessage());
        }
    }

سلیکٹر ایک ایسا فنکشن ہے جو درج کردہ سٹرنگ کے لحاظ سے پروگرام کے اعمال کا تعین کرتا ہے۔ یہاں کی ہر چیز بہت خوبصورت نہیں ہے اور کوڈ بلاک سے باہر جبری طور پر باہر نکلنے کے ساتھ استعمال کی گئی چال بہترین نہیں ہے، لیکن اس کی بنیادی وجہ جاوا میں کچھ چیزوں کی عدم موجودگی ہے، جیسے C# میں مندوبین، C++ سے فنکشن پوائنٹرز، یا at کم از کم خوفناک اور خوفناک گوٹو، جو آپ کو اس کو خوبصورتی سے نافذ کرنے کی اجازت دیتا ہے۔ اگر آپ جانتے ہیں کہ کوڈ کو کس طرح مزید خوبصورت بنانا ہے، تو میں تبصروں میں تنقید کا خیرمقدم کرتا ہوں۔ مجھے ایسا لگتا ہے کہ یہاں String-delegate ڈکشنری کی ضرورت ہے، لیکن کوئی مندوب نہیں ہے...

    private void selector()
    {
        do{
            if (typeOfCommand.equals("get")){
                get(sourcePath, destPath);
                break;
            }
            if (typeOfCommand.equals("put")){
                put(sourcePath, destPath);
                break;
            }
            showErrorMessage();
        }
        while (false);
    }
}

TFTPSسرور

سرور کی فعالیت کلائنٹ کی فعالیت سے مختلف ہوتی ہے، بڑے پیمانے پر، صرف اس میں کمانڈز کی بورڈ سے نہیں بلکہ ساکٹ سے آتے ہیں۔ کچھ طریقے عام طور پر ایک جیسے ہوتے ہیں، اس لیے میں ان کا حوالہ نہیں دوں گا، میں صرف اختلافات کو چھوؤں گا۔

شروع کرنے کے لیے، رن کا طریقہ استعمال کیا جاتا ہے، جو ان پٹ کے طور پر ایک پورٹ وصول کرتا ہے اور ساکٹ سے ان پٹ ڈیٹا کو ابدی لوپ میں پروسیس کرتا ہے۔

    public void run(int port) {
            this.port = port;
            incialization();
            while (true) {
                getAndParseInput();
                selector();
            }
    }

پوٹ میتھڈ، جو writeToFileFromSocket طریقہ کو لپیٹتا ہے جو فائل میں رائٹ اسٹریم کو کھولتا ہے اور ساکٹ سے تمام ان پٹ بائٹس لکھتا ہے، تحریر مکمل ہونے پر ٹرانسفر کی کامیاب تکمیل کی نشاندہی کرتا ہے۔

    private  void put(String source, String dest){
            writeToFileFromSocket();
            System.out.print("nDonen");
    };
    private void writeToFileFromSocket()
    {
        try {
            FileOutputStream writer = new FileOutputStream(new File(destPath));
            byte[] bytes = sin.readAllBytes();
            for (byte b : bytes) {
                writer.write(b);
            }
            writer.close();
        }
        catch (Exception e){
            System.out.println(e.getMessage());
        }
    }

حاصل کرنے کا طریقہ سرور فائل کو بازیافت کرتا ہے۔ جیسا کہ پہلے ہی پروگرام کے کلائنٹ سائڈ کے سیکشن میں بتایا گیا ہے، کسی فائل کو کامیابی کے ساتھ منتقل کرنے کے لیے آپ کو اس کا سائز جاننے کی ضرورت ہے، جو ایک لمبے عدد میں محفوظ ہے، اس لیے میں نے اسے 4 بائٹس کی ایک صف میں تقسیم کیا، انہیں بائٹ بائٹ منتقل کر دیا۔ ساکٹ میں، اور پھر، کلائنٹ پر ایک نمبر میں موصول اور جمع کرنے کے بعد، میں فائل کے ان پٹ اسٹریم سے پڑھنے والے تمام بائٹس کو منتقل کرتا ہوں۔


 private  void get(String source, String dest){
        File sending = new File(source);
        try {
            FileInputStream readFromFile = new FileInputStream(sending);
            byte[] arr = readFromFile.readAllBytes();
            byte[] bytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(sending.length()).array();
            for (int i = 0; i<Long.SIZE / Byte.SIZE; i++)
                sout.write(bytes[i]);
            sout.flush();
            for (byte b : arr)
                sout.write(b);
        }
        catch (Exception e){
            System.out.println(e.getMessage());
        }
    };

getAndParseInput طریقہ کلائنٹ کی طرح ہی ہے، فرق صرف اتنا ہے کہ یہ کی بورڈ کے بجائے ساکٹ سے ڈیٹا پڑھتا ہے۔ کوڈ ریپوزٹری میں ہے، بالکل سلیکٹر کی طرح۔
اس صورت میں، ابتدا کو کوڈ کے ایک الگ بلاک میں رکھا جاتا ہے، کیونکہ اس نفاذ کے اندر، منتقلی مکمل ہونے کے بعد، وسائل کو جاری کیا جاتا ہے اور دوبارہ قبضہ کر لیا جاتا ہے - دوبارہ سے میموری لیک ہونے سے تحفظ فراہم کرنے کے لیے۔

    private void  incialization()
    {
        try {
            serverSocket = new ServerSocket(port);
            socket = serverSocket.accept();
            sin = socket.getInputStream();
            sout = socket.getOutputStream();
        }
        catch (Exception e) {
            System.out.print(e.getMessage());
        }
    }

مختصر کرنے کے لئے:

ہم نے صرف ایک سادہ ڈیٹا ٹرانسفر پروٹوکول پر اپنی تبدیلی لکھی ہے اور اندازہ لگایا ہے کہ اسے کیسے کام کرنا چاہیے۔ اصولی طور پر، میں نے یہاں امریکہ کو دریافت نہیں کیا اور نہ ہی زیادہ نئی چیزیں لکھیں، لیکن Habré پر اس سے ملتے جلتے مضامین نہیں تھے، اور cmd افادیت کے بارے میں مضامین کی ایک سیریز لکھنے کے حصے کے طور پر اس پر ہاتھ نہ ڈالنا ناممکن تھا۔

حوالہ جات:

ماخذ کوڈ ذخیرہ
TFTP کے بارے میں مختصراً
ایک ہی چیز، لیکن روسی میں

ماخذ: www.habr.com

نیا تبصرہ شامل کریں