ออกแบบพร็อกซีเซิร์ฟเวอร์ของคุณเองโดยใช้ Delphi
ตอนที่ผู้เขียนเขียนซอฟต์แวร์เรียกเก็บเงินทางอินเทอร์เน็ต เกี่ยวข้องกับปัญหาวิธีเรียกเก็บเงินการเข้าถึงอินเทอร์เน็ตสำหรับแต่ละเวิร์กสเตชันในเครือข่ายท้องถิ่น โดยทั่วไปแล้ว เวิร์กสเตชันเหล่านี้จะเข้าถึงอินเทอร์เน็ตผ่านพร็อกซีเซิร์ฟเวอร์ เมื่อใช้ซอฟต์แวร์พร็อกซีเซิร์ฟเวอร์สำเร็จรูป เนื่องจากซอฟต์แวร์พร็อกซีเซิร์ฟเวอร์เป็นระบบปิด จึงเป็นเรื่องยากที่จะเขียนโปรแกรมเพื่อรับข้อมูลเวลาการเข้าถึงอินเทอร์เน็ตแบบเรียลไทม์ ดังนั้นให้พิจารณาว่าคุณสามารถเขียนพร็อกซีเซิร์ฟเวอร์ของคุณเองเพื่อแก้ไขปัญหาการเข้าถึงอินเทอร์เน็ตแบบกลุ่มในด้านหนึ่งและปัญหาการเรียกเก็บเงินในทางกลับกันได้หรือไม่
หลังจากทดลองโปรแกรมแล้ว ปัญหาก็ได้รับการแก้ไขอย่างน่าพอใจในที่สุด จดบันทึกตอนนี้และแบ่งปันกับเพื่อนร่วมงานของคุณ
1. ความคิด
มีพารามิเตอร์ในตัวเลือกระบบของเบราว์เซอร์ยอดนิยมในปัจจุบัน ได้แก่ "เชื่อมต่อผ่านพร็อกซีเซิร์ฟเวอร์" หลังจากการทดสอบการเขียนโปรแกรม
ลอง เมื่อเวิร์กสเตชันในเครือข่ายท้องถิ่นระบุคุณลักษณะนี้ แล้วออกคำขออินเทอร์เน็ต ข้อมูลคำขอจะถูกส่งไปยังพร็อกซีเซิร์ฟเวอร์ที่ระบุ ต่อไปนี้เป็นตัวอย่างของแพ็กเก็ตคำขอ:
รับ http://home.microsoft.com/intl/cn/ HTTP/1.0
ยอมรับ: */*
ยอมรับ-ภาษา: zh-cn
ยอมรับการเข้ารหัส: gzip, deflate
ตัวแทนผู้ใช้: Mozilla/4.0 (เข้ากันได้; MSIE 5.0; Windows NT)
โฮสต์: home.microsoft.com
พร็อกซี-การเชื่อมต่อ: Keep-Alive
บรรทัดแรกคือ URL เป้าหมายและวิธีการและโปรโตคอลที่เกี่ยวข้อง และบรรทัด "โฮสต์" ระบุที่อยู่ของโฮสต์เป้าหมาย
จากนี้เราทราบกระบวนการของบริการพร็อกซี: รับคำขอจากพร็อกซี, เชื่อมต่อกับโฮสต์จริง, รับข้อมูลที่โฮสต์ส่งคืน และส่งข้อมูลที่ได้รับไปยังพร็อกซี
เพื่อจุดประสงค์นี้ สามารถเขียนโปรแกรมอย่างง่ายเพื่อแก้ไขปัญหาการเปลี่ยนเส้นทางการสื่อสารเครือข่ายข้างต้นได้
เมื่อออกแบบด้วย Delphi ให้เลือก ServerSocket เป็นตัวควบคุมซ็อกเก็ตเพื่อสื่อสารกับพร็อกซีเวิร์กสเตชัน และเลือก ClientSocket ไดนามิกอาร์เรย์เป็นตัวควบคุมซ็อกเก็ตเพื่อสื่อสารกับโฮสต์ระยะไกล
ปัญหาสำคัญที่ควรแก้ไขระหว่างการเขียนโปรแกรมคือปัญหาของการประมวลผลการเชื่อมต่อหลายรายการ เพื่อเร่งความเร็วบริการพร็อกซีและความเร็วการตอบสนองของเอเจนต์ ควรตั้งค่าคุณสมบัติของการควบคุมซ็อกเก็ตเป็นแบบไม่บล็อกแต่ละเซสชัน ถูกผูกไว้กับซ็อกเก็ตแบบไดนามิก ให้ใช้ค่าแอตทริบิวต์ SocketHandle ของซ็อกเก็ตเพื่อพิจารณาว่าเป็นของเซสชันใด
กระบวนการเชื่อมต่อการสื่อสารแสดงในรูปด้านล่าง:
พร็อกซีเซิร์ฟเวอร์
ซ็อกเก็ตเซิร์ฟเวอร์
(1) รับ
ส่งโดยตัวแทนไปยังโฮสต์ระยะไกล
(6) (2) (5)
เบราว์เซอร์ ClientSocket (4) เว็บเซิร์ฟเวอร์
รับช่วงต่อ
ส่ง(3)
(1) พร็อกซีเบราว์เซอร์ส่งคำขอเว็บ และ Serversocket ของพร็อกซีเซิร์ฟเวอร์รับคำขอ
(2) โปรแกรมพร็อกซีเซิร์ฟเวอร์จะสร้าง ClientSocket โดยอัตโนมัติ ตั้งค่าที่อยู่โฮสต์ พอร์ต และคุณลักษณะอื่นๆ จากนั้นเชื่อมต่อกับโฮสต์ระยะไกล
(3) หลังจากการเชื่อมต่อระยะไกล เหตุการณ์การส่งจะถูกทริกเกอร์ และแพ็กเก็ตคำขอเว็บที่ได้รับจาก Serversocket จะถูกส่งไปยังโฮสต์ระยะไกล
(4) เมื่อโฮสต์ระยะไกลส่งคืนข้อมูลเพจ เหตุการณ์การอ่านของ ClientSocket จะถูกทริกเกอร์ให้อ่านข้อมูลเพจ
(5) โปรแกรมพร็อกซีเซิร์ฟเวอร์กำหนดว่าซ็อกเก็ตใดในการควบคุม ServerSocket ควรส่งข้อมูลหน้าที่ได้รับจากโฮสต์ไปยังจุดสิ้นสุดพร็อกซีตามข้อมูลการเชื่อมโยง
(6) ซ็อกเก็ตที่เกี่ยวข้องใน ServerSocket ส่งข้อมูลหน้าไปยังตัวแทน
2. การเขียนโปรแกรม
การออกแบบกระบวนการสื่อสารข้างต้นโดยใช้ Delphi นั้นง่ายมาก ซึ่งส่วนใหญ่เกี่ยวข้องกับ ServerSocket และ ClientSocket
การเขียนโปรแกรมไดรเวอร์ซอฟต์แวร์ ต่อไปนี้เป็นรายการอินเทอร์เฟซพร็อกซีเซิร์ฟเวอร์รุ่นทดลองและโปรแกรมต้นฉบับที่เขียนโดยผู้เขียน รวมถึงคำอธิบายฟังก์ชันโดยย่อ:
หน่วยหลัก;
อินเตอร์เฟซ
การใช้งาน
Windows, ข้อความ, SysUtils, คลาส, กราฟิก, การควบคุม, แบบฟอร์ม, กล่องโต้ตอบ,
ExtCtrls, ScktComp, TrayIcon, เมนู, StdCtrls;
พิมพ์
session_record=บันทึก
ใช้แล้ว: บูลีน; {ไม่ว่าจะมีการบันทึกเซสชันหรือไม่}
SS_Handle: จำนวนเต็ม {ตัวจัดการซ็อกเก็ตพร็อกซีเซิร์ฟเวอร์}
CSocket: TClientSocket {ซ็อกเก็ตที่ใช้เชื่อมต่อกับรีโมท}
การค้นหา: บูลีน; {ไม่ว่าจะมองหาเซิร์ฟเวอร์}
LookupTime: จำนวนเต็ม {เวลาเซิร์ฟเวอร์ค้นหา}
คำขอ: บูลีน; {ไม่ว่าจะมีการร้องขอ}
request_str: string; {บล็อกข้อมูลคำขอ}
client_connected: บูลีน; {สถานะลูกค้าออนไลน์}
remote_connected: บูลีน; {การตั้งค่าสถานะการเชื่อมต่อเซิร์ฟเวอร์ระยะไกล}
จบ;
พิมพ์
TForm1 = คลาส (TForm)
ServerSocket1: TServerSocket;
ClientSocket1: TClientSocket;
ตัวจับเวลา 2: TTimer;
TrayIcon1: TTrayIcon;
PopupMenu1: TPopupMenu;
N11: TMenuItem;
N21: TMenuItem;
N1: TMenuItem;
N01: TMenuItem;
Memo1: TMemo;
แก้ไข 1: TEdit;
Label1: TLabel;
ตัวจับเวลา 1: TTimer;
ขั้นตอน Timer2Timer (ผู้ส่ง: TObject);
ขั้นตอน N11Click (ผู้ส่ง: TObject);
ขั้นตอน FormCreate (ผู้ส่ง: TObject);
ขั้นตอน FormClose (ผู้ส่ง: TObject; var Action: TCloseAction);
ขั้นตอน N21Click (ผู้ส่ง: TObject);
ขั้นตอน N01Click (ผู้ส่ง: TObject);
ขั้นตอน ServerSocket1ClientConnect (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
ขั้นตอน ServerSocket1ClientDisconnect (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
ขั้นตอน ServerSocket1ClientError (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket; ErrorEvent: TERrorEvent;
varErrorCode: จำนวนเต็ม);
ขั้นตอน ServerSocket1ClientRead (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
ขั้นตอน ClientSocket1Connect (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
ขั้นตอน ClientSocket1Disconnect (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
ขั้นตอน ClientSocket1Error (ผู้ส่ง: TObject; ซ็อกเก็ต: TCustomWinSocket;
ErrorEvent: TERrorEvent; var ErrorCode: จำนวนเต็ม);
ขั้นตอน ClientSocket1Write (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
ขั้นตอน ClientSocket1Read (ผู้ส่ง: TObject; ซ็อกเก็ต: TCustomWinSocket);
ขั้นตอน ServerSocket1Listen (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
ขั้นตอน AppException (ผู้ส่ง: TObject; E: ข้อยกเว้น);
ขั้นตอน Timer1Timer (ผู้ส่ง: TObject);
ส่วนตัว
{ประกาศส่วนตัว}
สาธารณะ
Service_Enabled: บูลีน; {ไม่ว่าจะเปิดใช้งานบริการพร็อกซี}
เซสชัน: อาร์เรย์ของ session_record; {เซสชันอาร์เรย์}
เซสชัน: จำนวนเต็ม; {จำนวนเซสชัน}
LookUpTimeOut: จำนวนเต็ม {ค่าการหมดเวลาการเชื่อมต่อ}
InvalidRequests: จำนวนเต็ม {จำนวนคำขอที่ไม่ถูกต้อง}
จบ;
var
แบบฟอร์ม 1: TForm1;
การดำเนินการ
{$R *.DFM}
file://System startup timer หลังจากหน้าต่างเริ่มต้นปรากฏขึ้น ให้ย่อขนาดไปที่ System Tray...
ขั้นตอน TForm1.Timer2Timer (ผู้ส่ง: TObject);
เริ่ม
timer2.Enabled:=false; {ปิดตัวจับเวลา}
เซสชัน:=0; {จำนวนเซสชัน=0}
application.OnException := AppException; {เพื่อป้องกันข้อยกเว้นที่เกิดขึ้นบนพร็อกซีเซิร์ฟเวอร์}
คำขอไม่ถูกต้อง:=0; {0 ข้อผิดพลาด}
LookUpTimeOut:=60000; {ค่าการหมดเวลา=1 นาที}
timer1.Enabled:=true;
n11.Enabled:=false; {เปิดใช้งานรายการเมนูบริการไม่ถูกต้อง}
n21.Enabled:=true; {ปิดรายการเมนูบริการถูกต้อง}
serverocket1.Port:=988; {พอร์ตพร็อกซีเซิร์ฟเวอร์=988}
Serversocket1.Active:=true; {เริ่มบริการ}
form1.hide; {ซ่อนอินเทอร์เฟซ ย่อขนาดไปที่ System Tray}
จบ;
file://เปิดรายการเมนูบริการ…
ขั้นตอน TForm1.N11Click(Sender: TObject);
เริ่ม
Serversocket1.Active:=true; {เริ่มบริการ}
จบ;
file://รายการเมนูบริการหยุด…
ขั้นตอน TForm1.N21Click(Sender: TObject);
เริ่ม
Serversocket1.Active:=false; {หยุดบริการ}
N11.เปิดใช้งานแล้ว:=จริง;
N21.เปิดใช้งานแล้ว:=เท็จ;
Service_Enabled:=false; {ธงถูกล้าง}
จบ;
file://การสร้างหน้าต่างหลัก…
ขั้นตอน TForm1.FormCreate (ผู้ส่ง: TObject);
เริ่ม
Service_Enabled:=เท็จ;
timer2.Enabled:=true; {เมื่อหน้าต่างถูกสร้างขึ้น ให้เปิดตัวจับเวลา}
จบ;
file://เมื่อปิดหน้าต่าง...
ขั้นตอน TForm1.FormClose (ผู้ส่ง: TObject; var Action: TCloseAction);
เริ่ม
timer1.Enabled:=false; {ปิดตัวจับเวลา}
ถ้า Service_Enabled แล้ว
Serversocket1.Active:=false; {ปิดบริการเมื่อออกจากโปรแกรม}
จบ;
ไฟล์://ปุ่มออกจากโปรแกรม…
ขั้นตอน TForm1.N01Click(Sender: TObject);
เริ่ม
form1.ปิด {ออกจากโปรแกรม}
จบ;
file://หลังจากเปิดบริการพรอกซี...
ขั้นตอน TForm1.ServerSocket1Listen (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
เริ่ม
Service_Enabled:=true; {ตั้งค่าสถานะบริการ}
N11.เปิดใช้งาน:=false;
N21.เปิดใช้งานแล้ว:=จริง;
จบ;
หลังจาก file:// เชื่อมต่อกับพร็อกซีเซิร์ฟเวอร์โดยพร็อกซี เซสชันจะถูกสร้างขึ้นและเชื่อมโยงกับซ็อกเก็ต...
ขั้นตอน TForm1.ServerSocket1ClientConnect (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
var
ฉัน,j: จำนวนเต็ม;
เริ่ม
เจ:=-1;
สำหรับ i:=1 ถึงเซสชันทำ {ค้นหาว่ามีรายการว่างหรือไม่}
ถ้าไม่ใช่ session[i-1].ใช้แล้ว และไม่ใช่ session[i-1].CSocket.active แล้ว
เริ่ม
j:=i-1; {ใช่ มอบหมายมัน}
session[j].Used:=true; {ตั้งค่าตามการใช้งาน}
หยุดพัก;
จบ
อื่น
ถ้าไม่ใช่ session[i-1].Used และ session[i-1].CSocket.active แล้ว
เซสชัน [i-1].CSocket.active:=false;
ถ้า j=-1 แล้ว
เริ่มต้น {ไม่มี เพิ่มหนึ่ง}
เจ:=เซสชั่น;
inc (เซสชัน);
ความยาวที่กำหนด (เซสชัน, เซสชัน);
session[j].Used:=true; {ตั้งค่าตามการใช้งาน}
เซสชั่น [j] .CSocket: = TClientSocket.Create (ไม่มี);
เซสชั่น [j] .CSocket.OnConnect:=ClientSocket1Connect;
เซสชั่น [j] .CSocket.OnDisconnect: = ClientSocket1Disconnect;
เซสชั่น [j] .CSocket.OnError: = ClientSocket1Error;
เซสชั่น [j] .CSocket.OnRead: = ClientSocket1Read;
เซสชั่น [j] .CSocket.OnWrite: = ClientSocket1Write;
เซสชัน [j] การค้นหา: = false;
จบ;
session[j].SS_Handle:=socket.socketHandle; {บันทึกหมายเลขอ้างอิงและใช้การเชื่อมโยง}
session[j].Request:=false; {ไม่มีการร้องขอ}
session[j].client_connected:=true; {ไคลเอนต์เชื่อมต่อแล้ว}
เซสชัน [j] .remote_connected: = false; {ไม่ได้เชื่อมต่อระยะไกล}
edit1.text:=inttostr(เซสชัน);
จบ;
เมื่อ file:// ถูกตัดการเชื่อมต่อโดยตัวแทน...
ขั้นตอน TForm1.ServerSocket1ClientDisconnect (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
var
i,j,k: จำนวนเต็ม;
เริ่ม
สำหรับ i:=1 ถึงเซสชันทำ
if (session[i-1].SS_Handle=socket.SocketHandle) และ session[i-1].ใช้แล้ว
เริ่ม
session[i-1].client_connected:=false; {ไคลเอนต์ไม่ได้เชื่อมต่อ}
ถ้า session[i-1].remote_connected แล้ว
session[i-1].CSocket.active:=false {หากการเชื่อมต่อระยะไกลยังคงเชื่อมต่ออยู่ ให้ยกเลิกการเชื่อมต่อ}
อื่น
session[i-1].Used:=false; {หากทั้งสองถูกตัดการเชื่อมต่อ ให้ตั้งค่าสถานะทรัพยากรที่เผยแพร่}
หยุดพัก;
จบ;
เจ:=เซสชั่น;
เค:=0;
สำหรับ i:=1 ถึง j do {มีรายการที่ไม่ได้ใช้หลายรายการที่ส่วนท้ายของอาร์เรย์เซสชันสถิติ}
เริ่ม
ถ้า session[ji].ใช้แล้ว
หยุดพัก;
รวม(k);
จบ;
ถ้า k>0 แล้ว {แก้ไขอาร์เรย์เซสชันและปล่อยรายการที่ไม่ได้ใช้ในตอนท้าย}
เริ่ม
เซสชัน:=เซสชัน-k;
ความยาวที่กำหนด (เซสชัน, เซสชัน);
จบ;
edit1.text:=inttostr(เซสชัน);
จบ;
เมื่อเกิดข้อผิดพลาด file://communication...
ขั้นตอน TForm1.ServerSocket1ClientError (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket; ErrorEvent: TERrorEvent;
varErrorCode: จำนวนเต็ม);
var
i,j,k: จำนวนเต็ม;
เริ่ม
สำหรับ i:=1 ถึงเซสชันทำ
if (session[i-1].SS_Handle=socket.SocketHandle) และ session[i-1].ใช้แล้ว
เริ่ม
session[i-1].client_connected:=false; {ไคลเอนต์ไม่ได้เชื่อมต่อ}
ถ้า session[i-1].remote_connected แล้ว
session[i-1].CSocket.active:=false {หากการเชื่อมต่อระยะไกลยังคงเชื่อมต่ออยู่ ให้ยกเลิกการเชื่อมต่อ}
อื่น
session[i-1].Used:=false; {หากทั้งสองถูกตัดการเชื่อมต่อ ให้ตั้งค่าสถานะทรัพยากรที่เผยแพร่}
หยุดพัก;
จบ;
เจ:=เซสชั่น;
เค:=0;
สำหรับ i:=1 ถึง j ทำ
เริ่ม
ถ้า session[ji].ใช้แล้ว
หยุดพัก;
รวม(k);
จบ;
ถ้า k>0 แล้ว
เริ่ม
เซสชัน:=เซสชัน-k;
ความยาวที่กำหนด (เซสชัน, เซสชัน);
จบ;
edit1.text:=inttostr(เซสชัน);
รหัสข้อผิดพลาด:=0;
จบ;
เมื่อพร็อกซีส่ง file:// เพื่อขอเพจ...
ขั้นตอน TForm1.ServerSocket1ClientRead (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
var
tmp,line,โฮสต์: string;
i,j,พอร์ต: จำนวนเต็ม;
เริ่ม
สำหรับ i:=1 ถึงเซสชัน {กำหนดว่าเป็นเซสชันใด}
ถ้า session[i-1].ใช้แล้ว และ (session[i-1].SS_Handle=socket.sockethandle) แล้ว
เริ่ม
เซสชัน [i-1].request_str:=socket.ReceiveText; {บันทึกข้อมูลคำขอ}
tmp:=session[i-1].request_str; {เก็บไว้ในตัวแปรชั่วคราว}
memo1.lines.add(tmp);
j:=pos(ถ่าน(13)+ถ่าน(10),tmp); {เครื่องหมายหนึ่งบรรทัด}
ในขณะที่ j>0 ทำ {สแกนข้อความคำขอทีละบรรทัด มองหาที่อยู่โฮสต์}
เริ่ม
บรรทัด:=copy(tmp,1,j-1); {ใช้บรรทัด}
ลบ(tmp,1,j+1); {ลบแถว}
j:=pos('Host',line); {สถานะที่อยู่โฮสต์}
ถ้า j>0 แล้ว
เริ่ม
ลบ(บรรทัด,1,j+5); {ลบอักขระที่ไม่ถูกต้องก่อนหน้า}
j:=pos(':',line);
ถ้า j>0 แล้ว
เริ่ม
โฮสต์:=copy(line,1,j-1);
ลบ(บรรทัด,1,เจ);
พยายาม
พอร์ต:=strtoint(บรรทัด);
ยกเว้น
พอร์ต:=80;
จบ;
จบ
อื่น
เริ่ม
โฮสต์:=trim(line); {รับที่อยู่โฮสต์}
พอร์ต:=80;
จบ;
ถ้าไม่ใช่ session[i-1].remote_connected ดังนั้น {หากยังไม่ได้เชื่อมต่อการสำรวจ}
เริ่ม
session[i-1].Request:=true; {ตั้งค่าสถานะคำขอข้อมูลพร้อมแล้ว}
session[i-1].CSocket.host:=host; {ตั้งค่าที่อยู่โฮสต์ระยะไกล}
เซสชัน [i-1].CSocket.port:=port; {ตั้งค่าพอร์ต}
session[i-1].CSocket.active:=true; {เชื่อมต่อกับโฮสต์ระยะไกล}
เซสชัน [i-1] การค้นหา: = true; {set flag}
session[i-1].LookupTime:=0; {เริ่มนับจาก 0}
จบ
อื่น
{หากเชื่อมต่อรีโมตแล้ว ให้ส่งคำขอโดยตรง}
เซสชัน [i-1].CSocket.socket.sendtext (เซสชัน [i-1].request_str);
พัง; {หยุดการสแกนข้อความคำขอ}
จบ;
j:=pos(char(13)+char(10),tmp); {ชี้ไปที่บรรทัดถัดไป}
จบ;
พัง; {หยุดวง}
จบ;
จบ;
file://เมื่อเชื่อมต่อกับโฮสต์ระยะไกลสำเร็จ...
ขั้นตอน TForm1.ClientSocket1Connect (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
var
ฉัน: จำนวนเต็ม;
เริ่ม
สำหรับ i:=1 ถึงเซสชันทำ
if (session[i-1].CSocket.socket.sockethandle=socket.SocketHandle) และ session[i-1].ใช้แล้ว
เริ่ม
เซสชัน [i-1].CSocket.tag:=socket.SocketHandle;
session[i-1].remote_connected:=true; {ตั้งค่าสถานะการเชื่อมต่อโฮสต์ระยะไกล}
เซสชัน [i-1] การค้นหา: = false; {ล้างธง}
หยุดพัก;
จบ;
จบ;
file://เมื่อรีโมตโฮสต์ตัดการเชื่อมต่อ...
ขั้นตอน TForm1.ClientSocket1Disconnect (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
var
i,j,k: จำนวนเต็ม;
เริ่ม
สำหรับ i:=1 ถึงเซสชันทำ
if (session[i-1].CSocket.tag=socket.SocketHandle) และ session[i-1].ใช้แล้ว
เริ่ม
session[i-1].remote_connected:=false; {ตั้งค่าเป็นไม่ได้เชื่อมต่อ}
ถ้าไม่ใช่ session[i-1].client_connected แล้ว
session[i-1].Used:=false {หากไคลเอ็นต์ถูกตัดการเชื่อมต่อ ให้ตั้งค่าสถานะทรัพยากรที่เผยแพร่}
อื่น
สำหรับ k:=1 ถึง serverocket1.Socket.ActiveConnections ทำ
if (serversocket1.Socket.Connections[k-1].SocketHandle=session[i-1].SS_Handle) และ session[i-1].ใช้แล้ว
เริ่ม
Serversocket1.Socket.Connections [k-1] ปิด;
หยุดพัก;
จบ;
หยุดพัก;
จบ;
เจ:=เซสชั่น;
เค:=0;
สำหรับ i:=1 ถึง j ทำ
เริ่ม
ถ้า session[ji].ใช้แล้ว
หยุดพัก;
รวม(k);
จบ;
ถ้า k>0 แล้ว {แก้ไขอาร์เรย์เซสชัน}
เริ่ม
เซสชัน:=เซสชัน-k;
ความยาวที่กำหนด (เซสชัน, เซสชัน);
จบ;
edit1.text:=inttostr(เซสชัน);
จบ;
file://เมื่อเกิดข้อผิดพลาดในการสื่อสารกับโฮสต์ระยะไกล...
ขั้นตอน TForm1.ClientSocket1Error (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket; ErrorEvent: TERrorEvent;
varErrorCode: จำนวนเต็ม);
var
i,j,k: จำนวนเต็ม;
เริ่ม
สำหรับ i:=1 ถึงเซสชันทำ
if (session[i-1].CSocket.tag=socket.SocketHandle) และ session[i-1].ใช้แล้ว
เริ่ม
ซ็อกเก็ตปิด;
session[i-1].remote_connected:=false; {ตั้งค่าเป็นไม่ได้เชื่อมต่อ}
ถ้าไม่ใช่ session[i-1].client_connected แล้ว
session[i-1].Used:=false {หากไคลเอ็นต์ถูกตัดการเชื่อมต่อ ให้ตั้งค่าสถานะทรัพยากรที่เผยแพร่}
อื่น
สำหรับ k:=1 ถึง serverocket1.Socket.ActiveConnections ทำ
if (serversocket1.Socket.Connections[k-1].SocketHandle=session[i-1].SS_Handle) และ session[i-1].ใช้แล้ว
เริ่ม
Serversocket1.Socket.Connections [k-1] ปิด;
หยุดพัก;
จบ;
หยุดพัก;
จบ;
เจ:=เซสชั่น;
เค:=0;
สำหรับ i:=1 ถึง j ทำ
เริ่ม
ถ้า session[ji].ใช้แล้ว
หยุดพัก;
รวม(k);
จบ;
รหัสข้อผิดพลาด:=0;
ถ้า k>0 แล้ว {แก้ไขอาร์เรย์เซสชัน}
เริ่ม
เซสชัน:=เซสชัน-k;
ความยาวที่กำหนด (เซสชัน, เซสชัน);
จบ;
edit1.text:=inttostr(เซสชัน);
จบ;
file://ส่งคำขอหน้าไปยังโฮสต์ระยะไกล...
ขั้นตอน TForm1.ClientSocket1Write (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
var
ฉัน: จำนวนเต็ม;
เริ่ม
สำหรับ i:=1 ถึงเซสชันทำ
if (session[i-1].CSocket.tag=socket.SocketHandle) และ session[i-1].ใช้แล้ว
เริ่ม
ถ้า session[i-1].ร้องขอแล้ว
เริ่ม
socket.SendText(session[i-1].request_str); {หากมีคำขอ ให้ส่ง}
เซสชั่น [i-1]. คำขอ: = false;
จบ;
หยุดพัก;
จบ;
จบ;
file://เมื่อรีโมตโฮสต์ส่งข้อมูลเพจ...
ขั้นตอน TForm1.ClientSocket1Read (ผู้ส่ง: TObject;
ซ็อกเก็ต: TCustomWinSocket);
var
ฉัน,j: จำนวนเต็ม;
rec_bytes: จำนวนเต็ม {ความยาวบล็อกข้อมูลที่ส่งคืน}
rec_Buffer: อาร์เรย์ [0..2047] ของถ่าน {บัฟเฟอร์บล็อกข้อมูลที่ส่งคืน}
เริ่ม
สำหรับ i:=1 ถึงเซสชันทำ
if (session[i-1].CSocket.tag=socket.SocketHandle) และ session[i-1].ใช้แล้ว
เริ่ม
rec_bytes:=socket.ReceiveBuf(rec_buffer,2048); {รับข้อมูล}
สำหรับ j:=1 ถึง serverocket1.Socket.ActiveConnections ทำ
ถ้าserversocket1.Socket.Connections[j-1].SocketHandle=session[i-1].SS_Handle แล้ว
เริ่ม
serverocket1.Socket.Connections [j-1]. SendBuf (rec_buffer, rec_bytes); {ส่งข้อมูล}
หยุดพัก;
จบ;
หยุดพัก;
จบ;
จบ;
File:// "ไม่พบเพจ" และข้อความแสดงข้อผิดพลาดอื่นๆ ปรากฏขึ้น...
ขั้นตอน TForm1.AppException (ผู้ส่ง: TObject; E: ข้อยกเว้น);
เริ่ม
inc (คำขอที่ไม่ถูกต้อง);
จบ;
file:// ค้นหาเวลาโฮสต์ระยะไกล...
ขั้นตอน TForm1.Timer1Timer (ผู้ส่ง: TObject);
var
ฉัน,j: จำนวนเต็ม;
เริ่ม
สำหรับ i:=1 ถึงเซสชันทำ
ถ้า session[i-1].ใช้แล้ว และ session[i-1].ค้นหา จากนั้น {ถ้าเชื่อมต่อ}
เริ่ม
inc (เซสชัน [i-1]. LookupTime);
ถ้า session[i-1].LookupTime>lookuptimeout แล้ว {if timeout}
เริ่ม
เซสชัน [i-1] การค้นหา: = false;
เซสชั่น [i-1] .CSocket.active: = false;
สำหรับ j:=1 ถึง serverocket1.Socket.ActiveConnections ทำ
ถ้าserversocket1.Socket.Connections[j-1].SocketHandle=session[i-1].SS_Handle แล้ว
เริ่ม
Serversocket1.Socket.Connections [j-1] ปิด { ตัดการเชื่อมต่อไคลเอ็นต์ }
หยุดพัก;
จบ;
จบ;
จบ;
จบ;
จบ.
3. คำลงท้าย
เนื่องจากแนวคิดการออกแบบนี้เพิ่มฟังก์ชันการเปลี่ยนเส้นทางระหว่างปลายพร็อกซีและโฮสต์ระยะไกลเท่านั้น ซึ่งเป็นจุดสิ้นสุดพร็อกซีดั้งเดิม
คุณสมบัติบางอย่าง เช่น เทคโนโลยีแคช ยังคงอยู่ ดังนั้นจึงมีประสิทธิภาพสูง หลังจากการทดสอบ เมื่อใช้โมเด็ม 33.6K ในการเข้าถึงอินเทอร์เน็ต พร็อกซีเวิร์กสเตชันสามถึงสิบเครื่องจะสามารถเข้าถึงอินเทอร์เน็ตได้ในเวลาเดียวกัน และยังคงมีความเร็วในการตอบสนองที่ดี เนื่องจากการเชื่อมต่อระหว่างพร็อกซีเวิร์กสเตชันและเวิร์กสเตชันพร็อกซีเซิร์ฟเวอร์โดยทั่วไปจะผ่านลิงก์ความเร็วสูง ปัญหาคอขวดจึงมักเกิดขึ้นในวิธีการเข้าถึงอินเทอร์เน็ตของพร็อกซีเซิร์ฟเวอร์
ด้วยวิธีการข้างต้น ผู้เขียนประสบความสำเร็จในการพัฒนาชุดซอฟต์แวร์พร็อกซีเซิร์ฟเวอร์ครบชุด และบูรณาการเข้ากับระบบการเรียกเก็บเงินของห้องคอมพิวเตอร์ได้อย่างสมบูรณ์
ประสบความสำเร็จในการใช้เวิร์กสเตชันเครื่องเดียวเพื่อทำหน้าที่ต่างๆ เช่น พร็อกซีอินเทอร์เน็ต การเรียกเก็บเงินทางอินเทอร์เน็ต และการเรียกเก็บเงินการใช้เครื่อง เพื่อนที่มีประสบการณ์ด้านการเขียนโปรแกรมสามารถเพิ่มฟังก์ชันพร็อกซีเซิร์ฟเวอร์เพิ่มเติมได้ เช่น การตั้งค่าไซต์ที่ห้ามการเข้าถึง การนับปริมาณการเข้าชมของลูกค้า รายการการเข้าถึงเว็บ เป็นต้น