แหล่งที่มาดั้งเดิม: "เทคโนโลยีการเขียนโปรแกรมเครือข่าย Windows" บทที่ 8 Port Model เสร็จสมบูรณ์
เนื่องจากหนังสือต้นฉบับมาพร้อมกับรหัส C ฉันจึงแปลเป็นรหัส Delphi
ในหมู่พวกเขา winsock2.pas ไม่รวมอยู่ใน Delphi
เสร็จสิ้นโปรแกรม;
{$ AppType Console}
ใช้
Sysutils
winsock2 ใน 'winsock2.pas'
ไฟหลักใน 'mains.pas';
เริ่ม
หลัก();
จบ.
// ชื่อโมดูล: iocmmplt.cpp
-
// คำอธิบาย:
-
// ตัวอย่างนี้แสดงวิธีการพัฒนา WinSock เซิร์ฟเวอร์ Echo Select
// แอปพลิเคชั่นโดยใช้โมเดล I/O PORCITYION นี้
// ตัวอย่างถูกนำไปใช้เป็นแอปพลิเคชันสไตล์คอนโซลและพิมพ์เพียงอย่างเดียว
// ข้อความเมื่อมีการสร้างการเชื่อมต่อและลบออกจากเซิร์ฟเวอร์
// แอปพลิเคชันรับฟังการเชื่อมต่อ TCP บนพอร์ต 5150 และยอมรับพวกเขา
// เมื่อพวกเขามาถึง
// เพียงแค่ echos (นี่คือเหตุผลที่เราเรียกมันว่าเซิร์ฟเวอร์ echo) ข้อมูลกลับเข้ามา
// เป็นแบบฟอร์มดั้งเดิมจนกว่าไคลเอ็นต์จะปิดการเชื่อมต่อ
-
// 2005-2-5
// CPP แปลงเป็น Delphi Pas โดย Johnson
-
หน่วยไฟ;
ส่วนต่อประสาน
ใช้ windows, winsock2, winsock, sysutils;
const
พอร์ต = 5150;
data_bufsize = 8192;
พิมพ์
lpvoid = ตัวชี้;
LPPER_IO_OPERATION_DATA = ^ PER_IO_OPERATION_DATA;
per_io_operation_data = บันทึกที่บรรจุ
ซ้อนทับ: ซ้อนทับ;
Databuf: twsabuf;
บัฟเฟอร์: อาร์เรย์ [0..DATA_BUFSIZE] ของถ่าน;
Bytessend: DWORD;
BytesRecv: DWORD;
จบ;
lpper_handle_data = ^ per_handle_data;
per_handle_data = บันทึกที่บรรจุ
ซ็อกเก็ต: Tsocket;
จบ;
ขั้นตอนหลัก;
การดำเนินการ
ฟังก์ชั่น ServerworkerThread (COMPLEYPORTID: LPVOID): DWORD;
ขั้นตอน printf (fmt: string; num: จำนวนเต็ม);
เริ่ม
writeln (รูปแบบ (fmt, [num]));
จบ;
ขั้นตอนหลัก;
วาจา
InternetAddr: sockaddr_in;
ฟัง: Tsocket;
ยอมรับ: Tsocket;
เสร็จสิ้นพอร์ต: แทนเดิล;
SystemInfo: system_info;
perhandledata: lpper_handle_data;
Periodata: 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 เสร็จสมบูรณ์
เสร็จสิ้นพอร์ต: = createIocompletionport (invalid_handle_value, 0, 0, 0);
ถ้า (เสร็จสมบูรณ์ = 0) จากนั้น
เริ่ม
printf ('createiocomportport ล้มเหลวด้วยข้อผิดพลาด: %d', getLasterror ());
ออก;
จบ;
// กำหนดจำนวนโปรเซสเซอร์ในระบบ
getSystemInfo (SystemInfo);
// สร้างเธรดคนงานตามจำนวนโปรเซสเซอร์ที่มีอยู่ในไฟล์
// ระบบ
สำหรับ i: = 0 ถึง systeminfo.dwnumberofprocessors * 2 - 1 ทำ
เริ่ม
// สร้างเธรด Worker เซิร์ฟเวอร์และส่งผ่านพอร์ตเสร็จสิ้นไปยังเธรด
threadhandle: = createThread (nil, 0, @ServerWorkerThread, pointer (contecityPort),
0, ThreadId);
if (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 (พอร์ต);
if (bind (ฟัง, internetaddr, sizeof (internetaddr)) = socket_error) แล้ว
เริ่ม
printf ('bind () ล้มเหลวด้วยข้อผิดพลาด %d', wsagetlasterror ());
ออก;
จบ;
// เตรียมซ็อกเก็ตสำหรับการฟัง
if (winsock.listen (ฟัง, 5) = socket_error) แล้ว
เริ่ม
printf ('ฟัง () ล้มเหลวด้วยข้อผิดพลาด %d', wsagetlasterror ());
ออก;
จบ
อื่น
เริ่ม
printf ('เซิร์ฟเวอร์ฟังบนพอร์ต = %d ... ', พอร์ต);
จบ;
// ยอมรับการเชื่อมต่อและกำหนดให้กับพอร์ตที่สมบูรณ์
ในขณะที่ (จริง) ทำ
เริ่ม
ยอมรับ: = wsaaccept (ฟัง, ไม่มี, ไม่มี, ไม่มี, 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: = ยอมรับ;
if (createIocompletionport (ยอมรับ, เสร็จสิ้น, dword (perhandledata), 0) = 0) จากนั้น
เริ่ม
printf ('createIocomportOrtion () ล้มเหลวด้วยข้อผิดพลาด %D', WSAGETLASTROR ());
ออก;
จบ;
// สร้างโครงสร้างข้อมูลซ็อกเก็ต I/O เพื่อเชื่อมโยงกับไฟล์
// การโทร WSARECV ด้านล่าง
periodata: = lpper_io_operation_data (globalalloc (gptr, sizeof (per_io_operation_data)));
ถ้า (periodata = nil) แล้ว
เริ่ม
printf ('globalalloc () ล้มเหลวด้วยข้อผิดพลาด %d', wsagetlasterror ());
ออก;
จบ;
Zeromemory ( @periodata.overlapped, sizeof (ซ้อนทับ));
periodata.bytessend: = 0;
periodata.bytesrecv: = 0;
periodata.databuf.len: = data_bufsize;
periodata.databuf.buf: = @periodata.buffer;
ธง: = 0;
if (WSARECV (ยอมรับ, @(periodata.databuf), 1, @recvbytes, @flags,
@(periodata.overlapped), nil) = socket_error) จากนั้น
เริ่ม
if (wsagetlasterror () <> error_io_pending) จากนั้น
เริ่ม
printf ('WSARECV () ล้มเหลวด้วยข้อผิดพลาด %D', WSAGETLASTERROR ());
ออก;
จบ
จบ;
จบ;
จบ;
ฟังก์ชั่น ServerworkerThread (COMPLIENTID: LPVOID): DWORD;
วาจา
เสร็จสิ้นพอร์ต: แทนเดิล;
Bytestransferred: DWORD;
// ซ้อนทับ: poverlapped;
perhandledata: lpper_handle_data;
Periodata: LPPER_IO_OPERATION_DATA;
Sendbytes, Recvbytes: DWORD;
ธง: DWORD;
เริ่ม
เสร็จสิ้นพอร์ต: = thandle (เสร็จสมบูรณ์);
ผลลัพธ์: = 0;
ในขณะที่ (จริง) ทำ
เริ่ม
ถ้า (getqueuedcompletionstatus (เสร็จสิ้นพอร์ต, bytransferred,
dword (perhandledata), poverlapped (periodata), ไม่มีที่สิ้นสุด) = 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
// ด้วยค่าที่ผ่านมาจากการโทร WSARECV () ที่เสร็จสมบูรณ์
if (periodata.bytesrecv = 0) จากนั้น
เริ่ม
periodata.bytesrecv: = bytestransferred;
periodata.bytessend: = 0;
จบ
อื่น
เริ่ม
periodata.bytessend: = periodata.bytessend + bytransferred;
จบ;
if (periodata.bytesrecv> periodata.bytessend) แล้ว
เริ่ม
// โพสต์คำขอ WSASEND () อื่น
// เนื่องจาก WSASEND () ไม่ได้ถูกส่งไปส่งไบต์ทั้งหมดที่ร้องขอ
// ดำเนินการโพสต์การโทร WSASEND () ต่อไปจนกว่าจะมีการส่งไบต์ทั้งหมด
Zeromemory (@(periodata.overlapped), sizeof (ซ้อนทับ));
periodata.databuf.buf: = periodata.buffer + periodata.bytessend;
periodata.databuf.len: = periodata.bytesrecv - periodata.bytessend;
if (wsasend (perhandledata.socket, @(periodata.databuf), 1, @sendbytes, 0, 0,
@(periodata.overlapped), nil) = socket_error) จากนั้น
เริ่ม
if (wsagetlasterror () <> error_io_pending) จากนั้น
เริ่ม
printf ('WSASEND () ล้มเหลวด้วยข้อผิดพลาด %D', WSAGETLASTERROR ());
ออก;
จบ;
จบ;
จบ
อื่น
เริ่ม
periodata.bytesrecv: = 0;
// ตอนนี้ไม่มีไบต์อีกต่อไปในการส่งคำขอ WSARECV () อีกครั้ง
ธง: = 0;
Zeromemory (@(periodata.overlapped), sizeof (ซ้อนทับ));
periodata.databuf.len: = data_bufsize;
periodata.databuf.buf: = @periodata.buffer;
if (wsarecv (perhandledata.socket, @(periodata.databuf), 1, @recvbytes, @flags,
@(periodata.overlapped), nil) = socket_error) จากนั้น
เริ่ม
if (wsagetlasterror () <> error_io_pending) จากนั้น
เริ่ม
printf ('WSARECV () ล้มเหลวด้วยข้อผิดพลาด %D', WSAGETLASTERROR ());
ออก;
จบ;
จบ;
จบ;
จบ;
จบ;
จบ.