المصدر الأصلي: "تقنية برمجة شبكة Windows" الفصل الثامن نموذج المنفذ
نظرًا لأن الكتاب الأصلي مصحوب برمز C ، فقد ترجمته إلى رمز Delphi.
من بينها ، لا يتم تضمين Winsock2.pas في Delphi.
إكمال البرنامج ؛
{$ apptype console}
يستخدم
sysutils ،
Winsock2 في "winsock2.pas" ،
أنابيب في "mains.pas" ؛
يبدأ
رئيسي()؛
نهاية.
// اسم الوحدة: iocmmplt.cpp
//
// وصف:
//
// توضح هذه العينة كيفية تطوير خادم صدى بسيط
// التطبيق باستخدام نموذج I/O Completeion
// يتم تنفيذ العينة كتطبيق على غرار وحدة التحكم وطباعة ببساطة
// الرسائل عند إنشاء الاتصالات وإزالتها من الخادم.
// يستمع التطبيق لاتصالات TCP على المنفذ 5150 ويقبلها
// عند وصولهم.
// صدى ببساطة (لهذا السبب نسميه خادم صدى) مرة أخرى
// إنه نموذج أصلي حتى يغلق العميل الاتصال.
//
// 2005-2-5
// CPP تحويل إلى Delphi PAS بواسطة Johnson
//
وحدة التيار الكهربائي.
واجهة
يستخدم Windows ، Winsock2 ، Winsock ، sysutils ؛
كونست
المنفذ = 5150 ؛
data_bufsize = 8192 ؛
يكتب
lpvoid = مؤشر ؛
lpper_io_operation_data = ^ per_io_operation_data ؛
per_io_operation_data = سجل معبأ
تداخل: تداخل ؛
Databuf: Twsabuf ؛
المخزن المؤقت: صفيف [0..data_bufsize] من char ؛
bytessend: dword ؛
bytesrecv: dword ؛
نهاية؛
lpper_handle_data = ^ per_handle_data ؛
per_handle_data = سجل معبأ
المقبس: tsocket ؛
نهاية؛
الإجراء الرئيسي ؛
تطبيق
Function ServerWorkThread (CompleteionPortId: LPVoid): DWORD ؛
الإجراء printf (fmt: string ؛ num: integer) ؛
يبدأ
Writeln (Format (FMT ، [num])) ؛
نهاية؛
الإجراء الرئيسي ؛
var
InternetAddr: sockaddr_in ؛
الاستماع: tsocket.
قبول: tsocket ؛
إكمال: Thandle ؛
SystemInfo: system_info ؛
perhandledata: lpper_handle_data ؛
reparata: lpper_io_operation_data ؛
أنا: عدد صحيح.
recvbytes: dword ؛
الأعلام: DWORD ؛
ThreadId: Dword ؛
Wsadata: Twsadata ؛
RET: DWORD ؛
ThreadHandle: Thandle ؛
يبدأ
RET: = wsastartup (0202 دولار ، wsadata) ؛
إذا (ret <> 0) ثم
يبدأ
printf ('wsastartup فشل مع خطأ ٪ d' ، ret) ؛
مخرج؛
نهاية؛
// قم بإعداد منفذ إكمال I/O.
completionport: = createiOcplessionPort (invalid_handle_value ، 0 ، 0 ، 0) ؛
إذا (expectionport = 0) ثم
يبدأ
PRINTF ('CreateIOCOPTIONPORT فشل مع الخطأ: ٪ d' ، getLasterror ()) ؛
مخرج؛
نهاية؛
// تحديد عدد المعالجات الموجودة على النظام.
getSystemInfo (SystemInfo) ؛
// إنشاء مؤشرات ترابط العمال بناءً على عدد المعالجات المتوفرة على
// النظام.
لـ i: = 0 إلى SystemInfo.dwNumberOfProcsors * 2 - 1
يبدأ
// قم بإنشاء مؤشر ترابط عامل الخادم وقم بتمرير منفذ الإكمال إلى مؤشر الترابط.
ThreadHandle: = CreateTheRead (NIL ، 0 ، ServerWorkerThread ، Pointer (CompletionPort) ،
0 ، threadid) ؛
إذا (ThreadHandle = 0) ثم
يبدأ
فشل printf ('createThread () مع خطأ ٪ d' ، getLasterror ()) ؛
مخرج؛
نهاية؛
// أغلق مقبض الخيط
CloseHandle (ThreadHandle) ؛
نهاية؛
// إنشاء مقبس الاستماع
الاستماع: = wsasocket (af_inet ، sock_stream ، 0 ، nil ، 0 ، wsa_flag_overlapped) ؛
إذا (الاستماع = invalid_socket) ثم
يبدأ
فشل printf ('wsasocket () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية؛
InternetAddr.sin_family: = af_inet ؛
InternetAddr.sin_addr.s_addr: = htonl (inaddr_any) ؛
InternetAddr.sin_port: = htons (port) ؛
إذا (bind (استمع ، InternetAddr ، sizeof (InternetADDR)) = socket_error) ثم
يبدأ
فشل printf ('bind () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية؛
// إعداد المقبس للاستماع
if (winsock.listen (الاستماع ، 5) = socket_error) ثم
يبدأ
فشل printf ('الاستماع () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية
آخر
يبدأ
printf ('الخادم الاستماع على المنفذ = ٪ d ...' ، المنفذ) ؛
نهاية؛
// قبول الاتصالات وتعيين إلى منفذ الانتهاء.
بينما (صحيح) تفعل
يبدأ
قبول: = wsaaccept (الاستماع ، nil ، nil ، nil ، 0) ؛
إذا (قبول = socket_error) ثم
يبدأ
فشل printf ('wsaaccept () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية؛
// قم بإنشاء بنية معلومات المقبس لربطها بالمقبس
perhandledata: = lpper_handle_data (globalalloc (gptr ، sizeof (per_handle_data))) ؛
إذا (perhandledata = nil) ثم
يبدأ
فشل printf ('globalalloc () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية؛
// ربط المقبس المقبول بمنفذ الانتهاء الأصلي.
printf ('رقم المقبس ٪ d متصل "، قبول) ؛
perhandledata.socket: = قبول ؛
إذا (createiOcpletionPort (قبول ، إكمال ، dword (perhandledata) ، 0) = 0) ثم
يبدأ
فشل printf ('createiOcpletionPort () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية؛
// إنشاء بنية معلومات مقبس I/O للربط مع
// WSARECV Call أدناه.
erianatata: = lpper_io_operation_data (globalalloc (gptr ، sizeof (per_io_operation_data))) ؛
if (eriatata = nil) ثم
يبدأ
فشل printf ('globalalloc () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية؛
zeromemory ( @erimentata.overled ، sizeof (تداخل)) ؛
erianatata.bytessend: = 0 ؛
eriantata.bytesrecv: = 0 ؛
erianatata.databuf.len: = data_bufsize ؛
erianatata.databuf.buf: = @erimentata.buffer ؛
أعلام: = 0 ؛
if (wsarecv (قبول ، @(erianata.databuf) ، 1 ، Recvbytes ، @flags ،
@(eriesata.overled) ، nil) = socket_error) ثم
يبدأ
if (wsagetlasterror () <> error_io_pending) ثم
يبدأ
فشل printf ('wsarecv () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية
نهاية؛
نهاية؛
نهاية؛
دالة ServerWorkThread (CompletionPortId: LPVoid): DWORD ؛
var
إكمال: Thandle ؛
bytestransferred: dword ؛
// متداخلة: poverlopped ؛
perhandledata: lpper_handle_data ؛
reparata: lpper_io_operation_data ؛
sendbytes ، recvbytes: dword ؛
الأعلام: DWORD ؛
يبدأ
completionport: = thandle (completionPortId) ؛
النتائج: = 0 ؛
بينما (صحيح) تفعل
يبدأ
إذا (getQueuedCompletionStatus (CompletionPort ، bytestransferred ،
dword (perhandledata) ، poverliped (periodata) ، infinite) = false) ثم
يبدأ
printf ('getQueuedCompletionStatus فشل مع الخطأ ٪ d' ، getLasterror ()) ؛
مخرج؛
نهاية؛
// تحقق أولاً لمعرفة ما إذا كان حدث خطأ على المقبس وإذا كان الأمر كذلك
// ثم أغلق المقبس وقم بتنظيف بنية socket_information
// المرتبطة بالمقبس.
إذا (bytestransferred = 0) ثم
يبدأ
printf ("إغلاق المقبس ٪ d/'، perhandledata.socket) ؛
if (clossocket (perhandledata.socket) = socket_error) ثم
يبدأ
فشل printf ('clossocket () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية؛
GlobalFree (Dword (Perhandledata)) ؛
GlobalFree (dword (periodata)) ؛
يكمل؛
نهاية؛
// تحقق لمعرفة ما إذا كان حقل bytesrecv يساوي الصفر.
// هذا يعني مكالمة WSARECV التي تم الانتهاء منها للتو ، لذا قم بتحديث حقل BYTESRECV
// مع القيمة bytestransferred من مكالمة WSARECV () المكتملة.
if (eriatata.bytesrecv = 0) ثم
يبدأ
eriatata.bytesrecv: = bytestransferred ؛
erianatata.bytessend: = 0 ؛
نهاية
آخر
يبدأ
erianatata.bytessend: = quateata.bytessend + bytestransferred ؛
نهاية؛
if (eriatata.bytesrecv> eriesata.bytessend) ثم
يبدأ
// نشر طلب آخر wsasend ().
// بما أن WSASEND () لم يتم غمره لإرسال جميع البايتات المطلوبة ،
// استمر في نشر مكالمات WSASEND () حتى يتم إرسال جميع البايتات المستلمة.
zeromemory (@(erianatata.overled) ، sizeof (تداخل)) ؛
erianatata.databuf.buf: = erimentata.buffer + quateata.bytessend ؛
erianatata.databuf.len: = fareata.bytesrecv - pareatata.bytessend ؛
if (wsasend (perhandledata.socket ، @(eriesata.databuf) ، 1 ، sendbytes ، 0 ،
@(eriesata.overled) ، nil) = socket_error) ثم
يبدأ
if (wsagetlasterror () <> error_io_pending) ثم
يبدأ
فشل printf ('wsasend () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية؛
نهاية؛
نهاية
آخر
يبدأ
eriantata.bytesrecv: = 0 ؛
// الآن بعد أن لم يكن هناك مزيد من البايتات لإرسال طلب آخر wsarecv ().
أعلام: = 0 ؛
zeromemory (@(erianatata.overled) ، sizeof (تداخل)) ؛
erianatata.databuf.len: = data_bufsize ؛
erianatata.databuf.buf: = @erimentata.buffer ؛
if (wsarecv (perhandledata.socket ، @(eriesata.databuf) ، 1 ، recvbytes ، @flags ،
@(eriesata.overled) ، nil) = socket_error) ثم
يبدأ
if (wsagetlasterror () <> error_io_pending) ثم
يبدأ
فشل printf ('wsarecv () مع خطأ ٪ d' ، wsagetlasterror ()) ؛
مخرج؛
نهاية؛
نهاية؛
نهاية؛
نهاية؛
نهاية؛
نهاية.