أرشيف

Posts Tagged ‘Delphi’

كيف تكتب Component أول عنصر بالـ Delphi

المقالة تشرح كتابة أول عنصر مرئي في الدلفي

Unit جديدة

unit Unit1;
interface
implementation
end.

قراءة المزيد…

Advertisements
التصنيفات :مقالات, برمجة, دروس الوسوم:, , , ,

كيف تصنع ويب سيرفر متخلف

سأحاول أن نتعرف على طرق الاتصال Sockets من خلال هذا المثال
و لكن سنقوم بالعمل على مراحل
سنصنع ويب سرفر متخلف جدا
قم بعمل مشروع جديد
New/Application
أضف إلى الشاشة عنصر TIdTCPServer
إذا بحثت عنه و لم تجدة من كثرة العناصر قم بالضغط على ALT+V ثم C
ثم في نافذة Component اكتب اسم العنصر TIdTCPServer

لا نحتاج إلى معايرة العنصر في التصميم لأنني سوف اعايره برمجيا
عند حدث انشاء الشاشة اكتب

procedure TForm1.FormCreate(Sender: TObject);
begin
  IdTCPServer1.DefaultPort:=80;
  IdTCPServer1.CommandHandlersEnabled:=False;
  IdTCPServer1.Active:=True;
end;

البوابة التي اخترناها هي 80 و لكن إذا كان لديك ويب سيرفر شغال اختر البوابة 81
السطر الثاني اوقفنا فيه المعالجة التلقائية للأوامر في Indy لأننا نحن سوف نعالج الأمر يدويا
السطر الثالث تشغير العنصر

الأن يجب ان نعلم أن المستعرض Internet Explorer يقوم بإرسال امر الى الويب سيرفر و يستقبل منه المعلومات
لكن ماهو شكل هذا الأمر

GET / HTTP/1.1

or

GET /filename.html HTTP/1.1

الأمر هو GET و طبعا هناك أوامر اخرى يستعملها المستعرض
لاحظ أن اسم الملف تم ادراجه بعد الأمر و بعد اسم الملف رقم الاصدار لتبادل البيانات (ليس مهم هنا)

المهم علينا استقبال الامر الآتي و الرد عليه

في حدث OnExecute للعنصر IdTCPServer1 نعالج هذه المسائل الاستقبال و الرد

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
  c:string;
begin
  c:=AThread.Connection.Readln;
end;

و معناه عند تنفيذ الاتصال مع المستعرض هناك Thread يشتغل لكي يستطيع العنصر تنفيذ عدة أوامر بآن معا
و هذا الـ Thread يحوي Connection و هو الاتصال الحالي الذي سنعالجه

المستعرض Internet Exeplorer يقوم بارسال الأمر بشكل نصي و على عدة اسطر و آخر سطر يكون سطر فارغ، حتى يعلم الويب سيرفر بانتهاء الأمر ، و بعده تبدأ البيانات الخاصة بالارسال مثل كلمة المرور و اسم المستخدم إلخ، يهمنا هنا فقط السطر الأول و سنتجاهل حاليا بقية الاسطر و البيانات المرسلة

إذا ReadLn تقرأ سطرا واحدا الذي حتما يحوي الأمر GET مع اسم الملف المطلوب، Ln يعني Line في نهاية السطر يوجد حرفين #13#10

ما رأيكم بأن نرسل الجواب بغض النظر عن الملف المطلوب، طبعا بعد ارسال الجواب نقطع الاتصال.

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
	c:string;
begin
  c:=AThread.Connection.Readln;
  AThread.Connection.Writeln('<html><body><h5>Hello World</body></html>');
  AThread.Connection.Disconnect;
end;

لاحظ اننا ارسلنا الرد فورا و هذا خطأ لاننا لم نصف الرد للمستعرض هل هو html أم صورة gif أم ملف مضغوط إذا علينا ارسال التوصيف و ذلك مثل ما استقبلنا بإرسال اسطر متتالية تنتهي بسطر فراغ ثم نرسل البيانات كالاستقبال تماما

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
	c:string;
begin
  c:=AThread.Connection.Readln;
  AThread.Connection.WriteLn('HTTP/1.0 200 OK');
  AThread.Connection.WriteLn('Content-Type: text/html');
  AThread.Connection.WriteLn('');
  AThread.Connection.Writeln('<html><body><h5>Hello World</body></html>');
  AThread.Connection.Disconnect;
end;

لاحظ أن توصيف البيانات تم على أنه text/html حتى يعرف المستعرض ان البيانات المرسلة بعد السطر الفارغ هي html و هذا الوصف يدعى mime

كيف نجرب البرنامج: شغله ثم افتح المستعرض ثم اكتب http://localhost و إذا كنت حددت رقم البوابة 81 يكون العنوان http://localhost:81

جربه و أخبرنا على المنتدى في حال حدوث مشاكل

و لنتابع من حيث معالجة اسم الملف لنرسله للمستعرض بدلا من ارسال كلمة الترحيب فقط

إن الامر اصبح في المتحول c سنعالجه لنستخرج اسم الملف

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
	c:string;
	p:integer;
	f:string;
begin
  c:=AThread.Connection.Readln;
  p:=AnsiPos(' ',c);
  f:=Copy(c,p+1,MaxInt);
  p:=AnsiPos(' ',f);
  f:=Copy(f,1,p-1);
end;

هنا حصلنا على اسم الملف في المتحول f
عندما تطلب اسم الموقع بدون اسم الملف يأتي اسم الملف فقط /
سنقوم بتصحيح اسم الملف إلى الملف الافتراضي و عادة يكون index.html
ثم نفتح الملف بواسطة Stream أي TFileStream و نرسله للمستعرض عبر نفس الاتصال

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
  c:string;
  p:integer;
  f:string;
  fs:TFileStream;
begin
  c:=AThread.Connection.Readln;
  p:=AnsiPos(' ',c);
  f:=Copy(c,p+1,MaxInt);
  p:=AnsiPos(' ',f);
  f:=Copy(f,1,p-1);

  if f='/' then
  f:='/index.html';

  f:='c:\httpd'+f;
  fs:=TFileStream.Create(f,fmOpenRead);

  AThread.Connection.WriteLn('HTTP/1.0 200 OK');
  AThread.Connection.WriteLn('Content-Type: text/html');
  AThread.Connection.WriteLn('Content-Size: '+IntToStr(fs.Size));
  AThread.Connection.WriteLn('');
  AThread.Connection.WriteStream(fs);
  AThread.Connection.Disconnect;
  fs.Free;
end;

اسم الملف محتوى في المتحول f ارسلناه للمستعرض بعد توصيفه على أنه text/html و ارسلنا وصفا جديدا وهو طول الملف، و لكن ماذا لو كان الملف عبارة عن صورة، إن المستعرض تصله البيانات و لايقوم بتحليلها من خلال امتداد اسم الملف لانه قد يكون اسم الملف تنفيدي exe و يرسل البيانات بعد تنفيذ الملف و هذه البيانات قد تكون صورة، المهم ان الامتداد ليس له علاقة بنوع الصورة بل الـ MIME هو التوصيف المرسل ضمن Content-Type
لذلك سنعالج ببساطة نوع الـ MIME و يكون الاجراء النهائي كالتالي

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
  c:string;
  p:integer;
  f,e,m:string;
  fs:TFileStream;
begin
  c:=AThread.Connection.ReadLn;
  p:=AnsiPos(' ',c);
  f:=Copy(c,p+1,MaxInt);
  p:=AnsiPos(' ',f);
  f:=Copy(f,1,p-1);

  if f='/' then
  f:='/index.html';

  f:='c:\httpd'+f;

التصنيفات :مقالات, برمجة الوسوم:, , ,