Rancang server proxy Anda sendiri menggunakan Delphi
Ketika penulis sedang menulis perangkat lunak penagihan Internet, hal ini melibatkan masalah bagaimana menagih akses Internet untuk setiap stasiun kerja di jaringan area lokal. Secara umum, stasiun kerja ini mengakses Internet melalui server proxy. Saat menggunakan perangkat lunak server proxy yang sudah jadi, karena perangkat lunak server proxy adalah sistem tertutup, sulit untuk menulis program untuk mendapatkan informasi waktu akses Internet secara real-time. Oleh karena itu, pertimbangkan apakah Anda dapat menulis server proxy Anda sendiri untuk menyelesaikan masalah akses Internet grup di satu sisi dan masalah penagihan di sisi lain?
Setelah pemrograman eksperimental, masalah tersebut akhirnya terpecahkan dengan memuaskan. Tuliskan sekarang dan bagikan dengan kolega Anda.
1. Ide
Terdapat parameter dalam opsi sistem browser populer saat ini, yaitu "Hubungkan melalui server proxy".
Coba, ketika workstation di jaringan lokal menentukan atribut ini dan kemudian mengeluarkan permintaan Internet, data permintaan akan dikirim ke server proxy yang ditentukan.
DAPATKAN http://home.microsoft.com/intl/cn/HTTP/1.0
Menerima: */*
Bahasa Terima: zh-cn
Terima-Encoding: gzip, mengempis
Agen Pengguna: Mozilla/4.0 (kompatibel; MSIE 5.0; Windows NT)
Tuan rumah: home.microsoft.com
Koneksi PROxy: Tetap Hidup
Baris pertama adalah URL target dan metode serta protokol terkait, dan baris "Host" menentukan alamat host target.
Dari sini kita mengetahui proses layanan proxy: menerima permintaan dari proxy, menghubungkan ke host sebenarnya, menerima data yang dikembalikan oleh host, dan mengirimkan data yang diterima ke proxy.
Untuk tujuan ini, sebuah program sederhana dapat ditulis untuk menyelesaikan masalah pengalihan komunikasi jaringan di atas.
Saat mendesain dengan Delphi, pilih ServerSocket sebagai kontrol soket untuk berkomunikasi dengan stasiun kerja proxy, dan pilih array dinamis ClientSocket sebagai kontrol soket untuk berkomunikasi dengan host jarak jauh.
Masalah penting yang harus diselesaikan selama pemrograman adalah masalah pemrosesan beberapa koneksi. Untuk mempercepat layanan proxy dan kecepatan respons agen, properti kontrol soket harus disetel ke non-pemblokiran setiap sesi komunikasi terikat secara dinamis ke soket, gunakan nilai atribut SocketHandle dari soket untuk menentukan sesi mana yang dimilikinya.
Proses koneksi komunikasi ditunjukkan pada gambar di bawah ini:
server proksi
soket server
(1) menerima
Dikirim oleh agen ke host jarak jauh
(6) (2) (5)
Soket Klien Peramban (4) Server Web
mengambil alih
Kirim(3)
(1) Browser proxy mengirimkan permintaan Web, dan Serversocket server proxy menerima permintaan tersebut.
(2) Program server proxy secara otomatis membuat ClientSocket, menetapkan alamat host, port, dan atribut lainnya, lalu menyambung ke host jarak jauh.
(3) Setelah koneksi jarak jauh, acara kirim dipicu dan paket permintaan Web yang diterima oleh Serversocket dikirim ke host jarak jauh.
(4) Ketika host jarak jauh mengembalikan data halaman, peristiwa baca ClientSocket dipicu untuk membaca data halaman.
(5) Program server proxy menentukan Socket mana dalam kontrol ServerSocket yang harus mengirimkan informasi halaman yang diterima dari host ke ujung proksi berdasarkan informasi yang mengikat.
(6) Socket yang sesuai di ServerSocket mengirimkan data halaman ke agen.
2. Pemrograman
Sangat sederhana untuk merancang proses komunikasi di atas menggunakan Delphi, terutama terkait dengan ServerSocket dan ClientSocket.
Pemrograman driver perangkat lunak. Berikut ini adalah daftar antarmuka server proxy eksperimental dan program sumber yang ditulis oleh penulis, termasuk deskripsi singkat fungsinya:
satuan utama;
antarmuka
kegunaan
Windows, Pesan, SysUtils, Kelas, Grafik, Kontrol, Formulir, Dialog,
ExtCtrls, ScktComp, TrayIcon, Menu, StdCtrls;
jenis
session_record=catatan
Digunakan: boolean; {apakah rekaman sesi tersedia}
SS_Handle: integer; {pegangan soket server proxy}
CSocket: TClientSocket; {soket yang digunakan untuk terhubung ke remote}
Pencarian: boolean; {apakah mencari server}
Waktu Pencarian: bilangan bulat; {Waktu pencarian server}
Permintaan: boolean; {apakah ada permintaan}
request_str: string; {permintaan blok data}
client_connected: boolean; {tanda online klien}
remote_connected: boolean; {tanda koneksi server jarak jauh}
akhir;
jenis
TForm1 = kelas(TForm)
ServerSocket1: TServerSocket;
ClientSocket1: TClientSocket;
Pengatur Waktu2: Pengatur Waktu;
Ikon Baki1: Ikon Baki;
Menu Popup1: Menu Popup;
N11: TMenuItem;
N21: TMenuItem;
N1: TMenuItem;
N01: TMenuItem;
Memo1: TMemo;
Sunting1: TEdit;
Label1: Label;
Pengatur Waktu1: Pengatur Waktu;
procedure Timer2Timer(Pengirim: TObject);
prosedur N11Klik(Pengirim: TObject);
prosedur FormCreate(Pengirim: TObject);
procedure FormClose(Pengirim: TObject; var Action: TCloseAction);
prosedur N21Klik(Pengirim: TObject);
prosedur N01Klik(Pengirim: TObject);
prosedur ServerSocket1ClientConnect(Pengirim: TObject;
Soket: TCustomWinSocket);
prosedur ServerSocket1ClientDisconnect(Pengirim: TObject;
Soket: TCustomWinSocket);
prosedur ServerSocket1ClientError(Pengirim: TObject;
Soket: TCustomWinSocket; ErrorEvent: TErrorEvent;
varErrorCode: Bilangan Bulat);
prosedur ServerSocket1ClientRead(Pengirim: TObject;
Soket: TCustomWinSocket);
prosedur ClientSocket1Connect(Pengirim: TObject;
Soket: TCustomWinSocket);
prosedur ClientSocket1Disconnect(Pengirim: TObject;
Soket: TCustomWinSocket);
prosedur ClientSocket1Error(Pengirim: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var Kode Kesalahan: Integer);
prosedur ClientSocket1Write(Pengirim: TObject;
Soket: TCustomWinSocket);
prosedur ClientSocket1Read(Pengirim: TObject; Socket: TCustomWinSocket);
procedure ServerSocket1Listen(Pengirim: TObject;
Soket: TCustomWinSocket);
prosedur AppException(Pengirim: TObject; E: Pengecualian);
procedure Timer1Timer(Pengirim: TObject);
pribadi
{Deklarasi pribadi}
publik
Service_Enabled: boolean; {apakah layanan proxy diaktifkan}
sesi: array dari session_record; {array sesi}
sesi: bilangan bulat; {jumlah sesi}
LookUpTimeOut: integer; {nilai batas waktu koneksi}
Permintaan Tidak Valid: integer; {jumlah permintaan tidak valid}
akhir;
var
Formulir1: TForm1;
pelaksanaan
{$R *.DFM}
file://System startup timer, setelah jendela startup ditampilkan, menyusut ke System Tray...
prosedur TForm1.Timer2Timer(Pengirim: TObject);
mulai
timer2.Enabled:=false; {mematikan timer}
sesi:=0; {jumlah sesi=0}
application.OnException := AppException; {Untuk melindungi pengecualian yang terjadi di server proxy}
Permintaan tidak valid:=0; {0 kesalahan}
LookUpTimeOut:=60000; {nilai batas waktu=1 menit}
timer1.Enabled:=true; {nyalakan timer}
n11.Enabled:=false; {Aktifkan item menu layanan tidak valid}
n21.Enabled:=true; {Tutup item menu layanan valid}
serverocket1.Port:=988; {port server proksi=988}
serverocket1.Active:=true; {Mulai layanan}
form1.hide; {sembunyikan antarmuka, kecilkan ke System Tray}
akhir;
file://Buka item menu layanan…
prosedur TForm1.N11Klik(Pengirim: TObject);
mulai
serverocket1.Active:=true; {Mulai layanan}
akhir;
file://Hentikan item menu layanan…
prosedur TForm1.N21Klik(Pengirim: TObject);
mulai
serverocket1.Active:=false; {menghentikan layanan}
N11.Diaktifkan:=Benar;
N21.Diaktifkan:=Salah;
Service_Enabled:=false; {bendera dihapus}
akhir;
file://Pembuatan jendela utama…
prosedur TForm1.FormCreate(Pengirim: TObject);
mulai
Layanan_Diaktifkan:=salah;
timer2.Enabled:=true; {Saat jendela dibuat, buka pengatur waktu}
akhir;
file://Saat jendela ditutup...
procedure TForm1.FormClose(Pengirim: TObject; var Action: TCloseAction);
mulai
timer1.Enabled:=false; {mematikan timer}
jika Service_Enabled maka
serverocket1.Active:=false; {Tutup layanan saat keluar dari program}
akhir;
file://tombol keluar program…
prosedur TForm1.N01Klik(Pengirim: TObject);
mulai
form1.Tutup; {Keluar dari program}
akhir;
file://Setelah mengaktifkan layanan proxy...
prosedur TForm1.ServerSocket1Listen(Pengirim: TObject;
Soket: TCustomWinSocket);
mulai
Service_Enabled:=true; {setel tanda layanan}
N11.Diaktifkan:=salah;
N21.Diaktifkan:=benar;
akhir;
Setelah file:// terhubung ke server proxy melalui proxy, sebuah sesi dibuat dan diikat ke soket...
prosedur TForm1.ServerSocket1ClientConnect(Pengirim: TObject;
Soket: TCustomWinSocket);
var
i,j: bilangan bulat;
mulai
j:=-1;
untuk i:=1 ke sesi lakukan {temukan jika ada item kosong}
jika bukan session[i-1].Digunakan dan bukan session[i-1].CSocket.active maka
mulai
j:=i-1; {Ya, tetapkan}
sesi[j].Digunakan:=true;
merusak;
akhir
kalau tidak
jika bukan session[i-1].Used dan session[i-1].CSocket.active maka
sesi[i-1].CSocket.aktif:=false;
jika j=-1 maka
mulai {tidak ada, tambahkan satu}
j:=sesi;
inc(sesi);
setlength(sesi,sesi);
sesi[j].Digunakan:=true;
sesi[j].CSocket:=TClientSocket.Create(nil);
sesi[j].CSocket.OnConnect:=ClientSocket1Connect;
sesi[j].CSocket.OnDisconnect:=ClientSocket1Disconnect;
sesi[j].CSocket.OnError:=ClientSocket1Error;
sesi[j].CSocket.OnRead:=ClientSocket1Read;
sesi[j].CSocket.OnWrite:=ClientSocket1Write;
sesi[j].Pencarian:=false;
akhir;
session[j].SS_Handle:=socket.socketHandle; {Simpan pegangan dan implementasikan pengikatan}
sesi[j].Permintaan:=false; {Tidak ada permintaan}
sesi[j].client_connected:=true; {klien terhubung}
sesi[j].remote_connected:=false; {jarak jauh tidak terhubung}
edit1.text:=inttostr(sesi);
akhir;
Ketika file:// terputus oleh agen...
prosedur TForm1.ServerSocket1ClientDisconnect(Pengirim: TObject;
Soket: TCustomWinSocket);
var
i,j,k: bilangan bulat;
mulai
untuk i:=1 hingga sesi dilakukan
if (sesi[i-1].SS_Handle=socket.SocketHandle) dan sesi[i-1].Digunakan kemudian
mulai
sesi[i-1].client_connected:=false; {klien tidak terhubung}
jika sesi[i-1].remote_connected maka
session[i-1].CSocket.active:=false {Jika koneksi jarak jauh masih terhubung, putuskan sambungannya}
kalau tidak
session[i-1].Used:=false; {Jika keduanya terputus, setel tanda sumber daya rilis}
merusak;
akhir;
j:=sesi;
k:=0;
for i:=1 to j do {Ada beberapa item yang tidak terpakai di akhir susunan sesi statistik}
mulai
jika sesi[ji].Digunakan kemudian
merusak;
inc(k);
akhir;
jika k>0 maka {Ubah susunan sesi dan lepaskan item yang tidak digunakan di akhir}
mulai
sesi:=sesi-k;
setlength(sesi,sesi);
akhir;
edit1.text:=inttostr(sesi);
akhir;
Ketika kesalahan file://komunikasi terjadi...
prosedur TForm1.ServerSocket1ClientError(Pengirim: TObject;
Soket: TCustomWinSocket; ErrorEvent: TErrorEvent;
varErrorCode: Bilangan Bulat);
var
i,j,k: bilangan bulat;
mulai
untuk i:=1 hingga sesi dilakukan
if (sesi[i-1].SS_Handle=socket.SocketHandle) dan sesi[i-1].Digunakan kemudian
mulai
sesi[i-1].client_connected:=false; {klien tidak terhubung}
jika sesi[i-1].remote_connected maka
session[i-1].CSocket.active:=false {Jika koneksi jarak jauh masih terhubung, putuskan sambungannya}
kalau tidak
session[i-1].Used:=false; {Jika keduanya terputus, setel tanda sumber daya rilis}
merusak;
akhir;
j:=sesi;
k:=0;
untuk i:=1 untuk j lakukan
mulai
jika sesi[ji].Digunakan kemudian
merusak;
inc(k);
akhir;
jika k>0 maka
mulai
sesi:=sesi-k;
setlength(sesi,sesi);
akhir;
edit1.text:=inttostr(sesi);
kode kesalahan:=0;
akhir;
Ketika file:// dikirim oleh proxy untuk meminta halaman...
prosedur TForm1.ServerSocket1ClientRead(Pengirim: TObject;
Soket: TCustomWinSocket);
var
tmp,baris,host: string;
saya,j,pelabuhan: bilangan bulat;
mulai
untuk i:=1 ke sesi lakukan {tentukan sesi yang mana}
jika sesi[i-1].Digunakan dan (sesi[i-1].SS_Handle=socket.sockethandle) maka
mulai
sesi[i-1].request_str:=socket.ReceiveText; {simpan data permintaan}
tmp:=sesi[i-1].request_str; {disimpan dalam variabel sementara}
memo1.lines.add(tmp);
j:=pos(char(13)+char(10),tmp); {tanda satu baris}
sementara j>0 lakukan {scan teks permintaan baris demi baris, cari alamat host}
mulai
baris:=copy(tmp,1,j-1); {ambil satu baris}
delete(tmp,1,j+1); {hapus satu baris}
j:=pos('Host',line); {tanda alamat host}
jika j>0 maka
mulai
delete(line,1,j+5); {hapus karakter sebelumnya yang tidak valid}
j:=pos(':',baris);
jika j>0 maka
mulai
tuan rumah:=salin(baris,1,j-1);
hapus(baris,1,j);
mencoba
port:=strtoint(baris);
kecuali
pelabuhan:=80;
akhir;
akhir
kalau tidak
mulai
host:=trim(baris); {dapatkan alamat host}
pelabuhan:=80;
akhir;
if not session[i-1].remote_connected maka {Jika ekspedisi belum terhubung}
mulai
sesi[i-1].Permintaan:=true; {setel tanda siap data permintaan}
session[i-1].CSocket.host:=host; {Tetapkan alamat host jarak jauh}
sesi[i-1].CSocket.port:=port; {setel port}
sesi[i-1].CSocket.active:=true; {Hubungkan ke host jarak jauh}
sesi[i-1].Pencarian:=true;
session[i-1].LookupTime:=0; {mulai menghitung dari 0}
akhir
kalau tidak
{Jika remote terhubung, kirimkan permintaan secara langsung}
sesi[i-1].CSocket.socket.sendtext(sesi[i-1].request_str);
break; {berhenti memindai teks permintaan}
akhir;
j:=pos(char(13)+char(10),tmp); {menunjuk ke baris berikutnya}
akhir;
istirahat; {berhenti putaran}
akhir;
akhir;
file://Ketika koneksi ke host jarak jauh berhasil...
prosedur TForm1.ClientSocket1Connect(Pengirim: TObject;
Soket: TCustomWinSocket);
var
saya: bilangan bulat;
mulai
untuk i:=1 hingga sesi dilakukan
if (sesi[i-1].CSocket.socket.sockethandle=socket.SocketHandle) dan sesi[i-1].Digunakan kemudian
mulai
sesi[i-1].CSocket.tag:=socket.SocketHandle;
session[i-1].remote_connected:=true; {Setel tanda host jarak jauh yang terhubung}
sesi[i-1].Pencarian:=false; {hapus tanda}
merusak;
akhir;
akhir;
file://Ketika host jarak jauh terputus...
prosedur TForm1.ClientSocket1Disconnect(Pengirim: TObject;
Soket: TCustomWinSocket);
var
i,j,k: bilangan bulat;
mulai
untuk i:=1 hingga sesi dilakukan
if (sesi[i-1].CSocket.tag=socket.SocketHandle) dan sesi[i-1].Digunakan kemudian
mulai
sesi[i-1].remote_connected:=false; {setel ke tidak terhubung}
jika bukan sesi[i-1].client_connected maka
session[i-1].Used:=false {Jika klien terputus, setel tanda sumber daya rilis}
kalau tidak
untuk k:=1 ke serverocket1.Socket.ActiveConnections lakukan
if (serversocket1.Socket.Connections[k-1].SocketHandle=session[i-1].SS_Handle) dan session[i-1].digunakan kemudian
mulai
serverocket1.Socket.Koneksi[k-1].Tutup;
merusak;
akhir;
merusak;
akhir;
j:=sesi;
k:=0;
untuk i:=1 untuk j lakukan
mulai
jika sesi[ji].Digunakan kemudian
merusak;
inc(k);
akhir;
jika k>0 maka {perbaiki array sesi}
mulai
sesi:=sesi-k;
setlength(sesi,sesi);
akhir;
edit1.text:=inttostr(sesi);
akhir;
file://Ketika terjadi kesalahan saat berkomunikasi dengan host jarak jauh...
prosedur TForm1.ClientSocket1Error(Pengirim: TObject;
Soket: TCustomWinSocket; ErrorEvent: TErrorEvent;
varErrorCode: Bilangan Bulat);
var
i,j,k: bilangan bulat;
mulai
untuk i:=1 hingga sesi dilakukan
if (sesi[i-1].CSocket.tag=socket.SocketHandle) dan sesi[i-1].Digunakan kemudian
mulai
soket.tutup;
sesi[i-1].remote_connected:=false; {setel ke tidak terhubung}
jika bukan sesi[i-1].client_connected maka
session[i-1].Used:=false {Jika klien terputus, setel tanda sumber daya rilis}
kalau tidak
untuk k:=1 ke serverocket1.Socket.ActiveConnections lakukan
if (serversocket1.Socket.Connections[k-1].SocketHandle=session[i-1].SS_Handle) dan session[i-1].digunakan kemudian
mulai
serverocket1.Socket.Koneksi[k-1].Tutup;
merusak;
akhir;
merusak;
akhir;
j:=sesi;
k:=0;
untuk i:=1 untuk j lakukan
mulai
jika sesi[ji].Digunakan kemudian
merusak;
inc(k);
akhir;
kode kesalahan:=0;
jika k>0 maka {perbaiki array sesi}
mulai
sesi:=sesi-k;
setlength(sesi,sesi);
akhir;
edit1.text:=inttostr(sesi);
akhir;
file://Mengirim permintaan halaman ke host jarak jauh…
prosedur TForm1.ClientSocket1Write(Pengirim: TObject;
Soket: TCustomWinSocket);
var
saya: bilangan bulat;
mulai
untuk i:=1 hingga sesi dilakukan
if (sesi[i-1].CSocket.tag=socket.SocketHandle) dan sesi[i-1].Digunakan kemudian
mulai
jika sesi[i-1].Minta kemudian
mulai
socket.SendText(session[i-1].request_str); {Jika ada permintaan, kirim}
sesi[i-1].Permintaan:=false; {hapus tanda}
akhir;
merusak;
akhir;
akhir;
file://Ketika host jarak jauh mengirimkan data halaman...
procedure TForm1.ClientSocket1Read(Pengirim: TObject;
Soket: TCustomWinSocket);
var
i,j: bilangan bulat;
rec_bytes: integer; {panjang blok data yang dikembalikan}
rec_Buffer: array[0..2047] dari char; {mengembalikan buffer blok data}
mulai
untuk i:=1 hingga sesi dilakukan
if (sesi[i-1].CSocket.tag=socket.SocketHandle) dan sesi[i-1].Digunakan kemudian
mulai
rec_bytes:=socket.ReceiveBuf(rec_buffer,2048);
untuk j:=1 hingga serverocket1.Socket.ActiveConnections lakukan
jika serverocket1.Socket.Connections[j-1].SocketHandle=sesi[i-1].SS_Handle maka
mulai
serverocket1.Socket.Connections[j-1].SendBuf(rec_buffer,rec_bytes);
merusak;
akhir;
merusak;
akhir;
akhir;
File:// "Halaman tidak ditemukan" dan pesan kesalahan lainnya muncul...
prosedur TForm1.AppException(Pengirim: TObject; E: Pengecualian);
mulai
inc(permintaan tidak valid);
akhir;
file://Temukan waktu host jarak jauh...
prosedur TForm1.Timer1Timer(Pengirim: TObject);
var
i,j: bilangan bulat;
mulai
untuk i:=1 hingga sesi dilakukan
jika sesi[i-1].Digunakan dan sesi[i-1].Cari lalu {if menghubungkan}
mulai
inc(sesi[i-1].Waktu Pencarian);
jika sesi[i-1].LookupTime>lookuptimeout maka {if timeout}
mulai
sesi[i-1].Pencarian:=false;
sesi[i-1].CSocket.active:=false; {berhenti mencari}
untuk j:=1 hingga serverocket1.Socket.ActiveConnections lakukan
jika serverocket1.Socket.Connections[j-1].SocketHandle=sesi[i-1].SS_Handle maka
mulai
serverocket1.Socket.Connections[j-1].Tutup; {putuskan sambungan klien}
merusak;
akhir;
akhir;
akhir;
akhir;
akhir.
3. Catatan tambahan
Karena ide desain ini hanya menambahkan fungsi pengalihan antara ujung proxy dan host jarak jauh, ujung proxy asli
Beberapa fitur seperti teknologi caching tetap dipertahankan sehingga efisiensinya tinggi. Setelah dilakukan pengujian, saat menggunakan Modem 33.6K untuk mengakses Internet, tiga hingga sepuluh workstation proxy dapat mengakses Internet secara bersamaan, dan masih terdapat kecepatan respon yang baik. Karena koneksi antara stasiun kerja proksi dan stasiun kerja server proksi umumnya melewati tautan berkecepatan tinggi, kemacetan terutama terjadi pada metode akses Internet server proksi.
Melalui metode di atas, penulis berhasil mengembangkan satu set perangkat lunak server proxy yang lengkap dan mengintegrasikannya sepenuhnya dengan sistem penagihan ruang komputer.
Berhasil, satu stasiun kerja dapat digunakan untuk menyelesaikan fungsi seperti proksi Internet, penagihan Internet, dan penagihan penggunaan mesin. Teman dengan pengalaman pemrograman dapat menambahkan fungsi server proxy tambahan, seperti mengatur situs akses terlarang, menghitung lalu lintas pelanggan, daftar akses Web, dll.