用DELPHI設計代理伺服器程式 |
|
jackkcg
站務副站長 發表:891 回覆:1050 積分:848 註冊:2002-03-23 發送簡訊給我 |
用DELPHI設計代理伺服器程式
摘自《天極網學習中心》 (文/萬雪勇)
用Delphi開發串口通信軟體一般有兩種方法:一是利用Windows的通信API函數,另一種是採用Microsoft的MSComm控制項。利用API編寫串口通信程式較爲複雜,需要掌握大量通信知識,其優點是可實現的功能更強大,應用面更廣泛,更適合於編寫較爲複雜的低層次通信程式。而利用MSComm控制項則相對較簡單,該控制項具有豐富的與串口通信密切相關的屬性及事件,提供了對串口的各種操作。
一、MSComm控制項的主要屬性及事件
(1)CommPort:設置或返回序列埠號,缺省爲1。
(2)Setting:設置或返回串口通信參數,格式爲“串列傳輸速率,奇偶校驗位元,資料位元,停止位元”。例如:MSComm1.Setting:='9600,n,8,1'
(3)PortOpen:打開或關閉序列埠,格式爲:MSComm1.PortOpen:={True|False}
(4)InBufferSize:設置或返回接收緩衝區的大小,缺省值爲1024位元組。
(5)InBufferCount:返回接收緩衝區內等待讀取的位元組數,可通過設置該屬性爲0來清空接收緩衝區。
(6)RThreshold:該屬性爲一閥值,它確定當接收緩衝區內的位元組個數達到或超過該值後就産生代碼爲ComEvReceive的OnComm事件。
(7)SThreshold:該屬性爲一閥值,它確定當發送緩衝區內的位元組個數少於該值後就産生代碼爲ComEvSend的OnComm事件。
(8)InputLen:設置或返回接收緩衝區內用Input讀入的位元組數,設置該屬性爲0表示Input讀取整個緩衝區的內容。
(9)Input:從接收緩衝區讀取一串字元。
(10)OutBufferSize:設置或返回發送緩衝區的大小,缺省值爲512位元組。
(11)OutBufferCount:返回發送緩衝區內等待發送的位元組數,可通過設置該屬性爲0來清空緩衝區。
(12)OutPut:向發送緩衝區傳送一串字元。
如果在通信過程中發生錯誤或事件,就會引發OnComm事件,並由CommEvent屬性代碼反映錯誤類型,在通信程式的設計中可根據該屬性值來執行不同的操作。CommEvent屬性值及其含義如下:
(1)ComEvSend:值爲1,發送緩衝區的內容少於SThreshold指定的值。
(2)ComEvReceive:值爲2,接收緩衝區內字元數達到RThreshold指定的值。
(3)ComEvFrame:值爲1004,硬體檢測到幀錯誤。
(4)ComEvRxOver:值爲1008,接收緩衝區溢出。
(5)ComEvTxFull:值爲1010,發送緩衝區溢出。
(6)ComEvRxParity:值爲1009,奇偶校驗錯誤。
(7)ComEvEOF:值爲7,接收資料中出現文件尾(ASCII碼爲26)字元。
二、程式樣例
在Delphi3.0中無法使用MSComm控制項,筆者使用的是Delphi5.0。MSComm控制項是VB中的OCX控制項,首先需要將其添加到Delphi中,選擇功能表“Component”→“Import ActiveX Control”,在“Import ActiveX”頁內選擇“Microsoft Comm Control”,點擊“Install”安裝,安裝後在“ActiveX”元件板中出現MSComm圖示,即可被使用。有一點要注意,在Object Inspector中MSComm控制項的Input和Output屬性是不可見的,但它們仍然存在,這兩個屬性的類型是OleVariant(Ole萬能變數)。
下面是一接收程式的樣例(主要部分),大家可根據實際需要進行完善。
在Form中放置一Memo控制項用於顯示接收的資料,Combobox1選擇通信參數(Setting屬性值),Combobox2選擇串口(CommPort屬性值),按Button1開始接收資料,按Button2停止接收。
procedure TForm1.FormCreate(Sender: TObject);
begin
Mscomm1.InBufferCount :=0; // 清空接收緩衝區
Mscomm1.InputLen :=0; // Input讀取整個緩衝區內容
Mscomm1.RThreshold :=1; // 每次接收到字元即産生OnComm事件
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Mscomm1.Settings :=ComboBox1.Text;
if ComboBox2.Text ='com1' then // 假設只考慮com1和com2兩種情況
Mscomm1.CommPort :=1
else
Mscomm1.CommPort :=2;
Mscomm1.PortOpen :=true; // 打開串口
Mscomm1.DTREnable :=true; // 資料終端準備好
Mscomm1.RTSEnable :=true; // 請求發送
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Mscomm1.PortOpen :=false; // 關閉串口
Mscomm1.DTREnable :=false;
Mscomm1.RTSEnable :=false;
end;
procedure TForm1.MSComm1Comm(Sender: TObject);
var recstr:Olevariant;
begin
if Mscomm1.CommEvent = 2 then
begin
recstr := Mscomm1.Input ;
Memo1.text := Memo1.Text + recstr;
end;
end;
//主窗口建立
procedure TForm1.FormCreate(Sender: TObject);
begin
Service_Enabled:=false;
timer2.Enabled:=true;{窗口建立時,打開計時器}
end;
//窗口關閉時
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
timer1.Enabled:=false;{關閉計時器}
if Service_Enabled then
serversocket1.Active:=false;{退出程式時關閉服務}
end;
//退出程式按鈕
procedure TForm1.N01Click(Sender: TObject);
begin
form1.Close;{退出程式}
end;
//開啓代理服務後
procedure TForm1.ServerSocket1Listen(Sender: TObject;Socket: TCustomWinSocket);
begin
Service_Enabled:=true;{置正在服務標誌}
N11.Enabled:=false;
N21.Enabled:=true;
end;
//被代理端連接到代理伺服器後,建立一個會話,並與套接字綁定...
procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;Socket: TCustomWinSocket);
var i,j: integer;
begin
j:=-1;
for i:=1 to sessions do{查找是否有空白項}
if not session[i-1].Used and not session[i-1].CSocket.active then
begin
j:=i-1;{有,分配它}
session[j].Used:=true;{置爲在用}
break;
end
else
if not session[i-1].Used and session[i-1].CSocket.active then
session[i-1].CSocket.active:=false;
if j=-1 then
begin{無,新增一個}
j:=sessions;
inc(sessions);
setlength(session,sessions);
session[j].Used:=true;{置爲在用}
session[j].CSocket:=TClientSocket.Create(nil);
session[j].CSocket.OnConnect:=ClientSocket1Connect;
session[j].CSocket.OnDisconnect:=ClientSocket1Disconnect;
session[j].CSocket.OnError:=ClientSocket1Error;
session[j].CSocket.OnRead:=ClientSocket1Read;
session[j].CSocket.OnWrite:=ClientSocket1Write;
session[j].Lookingup:=false;
end;
session[j].SS_Handle:=socket.socketHandle; {保存控制碼,實現綁定}
session[j].Request:=false;{無請求}
session[j].client_connected:=true;{客戶機已連接}
session[j].remote_connected:=false;{遠端未連接}
edit1.text:=inttostr(sessions);
end;
//被代理端斷開時
procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject;Socket: TCustomWinSocket);
var i,j,k: integer;
begin
for i:=1 to sessions do
if (session[i-1].SS_Handle=socket.SocketHandle) and session[i-1].Used then
begin
session[i-1].client_connected:=false; {客戶機未連接}
if session[i-1].remote_connected then
session[i-1].CSocket.active:=false {假如遠端尚連接,斷開它}
else
session[i-1].Used:=false;{假如兩者都斷開,則置釋放資源標誌}
break;
end;
j:=sessions;
k:=0;
for i:=1 to j do{統計會話陣列尾部有幾個未用項}
begin
if session[j-i].Used then break;
inc(k);
end;
if k>0 then{修正會話陣列,釋放尾部未用項}
begin
sessions:=sessions-k;
setlength(session,sessions);
end;
edit1.text:=inttostr(sessions);
end;
//通信錯誤出現時
procedure TForm1.ServerSocket1ClientError(Sender: TObject;Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;var ErrorCode: Integer);
var i,j,k: integer;
begin
for i:=1 to sessions do
if (session[i-1].SS_Handle=socket.SocketHandle) and session[i-1].Used then
begin
session[i-1].client_connected:=false;{客戶機未連接}
if session[i-1].remote_connected then
session[i-1].CSocket.active:=false{假如遠端尚連接,斷開它}
else
session[i-1].Used:=false;{假如兩者都斷開,則置釋放資源標誌}
break;
end;
j:=sessions;
k:=0;
for i:=1 to j do
begin
if session[j-i].Used then break;
inc(k);
end;
if k>0 then
begin
sessions:=sessions-k;
setlength(session,sessions);
end;
edit1.text:=inttostr(sessions);
errorcode:=0;
end;
//被代理端發送來頁面請求時
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;Socket: TCustomWinSocket);
var tmp,line,host: string;
i,j,port: integer;
begin
for i:=1 to sessions do{判斷是哪一個會話}
if session[i-1].Used and (session[i-1].SS_Handle=socket.sockethandle) then
begin
session[i-1].request_str:=socket.ReceiveText; {保存請求資料}
tmp:=session[i-1].request_str; {存放到臨時變數}
memo1.lines.add(tmp);
j:=pos(char(13)+char(10),tmp);{一行標誌}
while j>0 do{逐行掃描請求文本,查找主機位址}
begin
line:=copy(tmp,1,j-1);{取一行}
delete(tmp,1,j+1);{刪除一行}
j:=pos('Host',line);{主機位址標誌}
if j>0 then
begin
delete(line,1,j+5);{刪除前面的無效字元}
j:=pos(':',line);
if j>0 then
begin
host:=copy(line,1,j-1);
delete(line,1,j);
try
port:=strtoint(line);
except
port:=80;
end;
end
else
begin
host:=trim(line);{獲取主機位址}
port:=80;
end;
if not session[i-1].remote_connected then{假如遠征尚未連接}
begin
session[i-1].Request:=true;{置請求資料就緒標誌}
session[i-1].CSocket.host:=host;{設置遠端主機位址}
session[i-1].CSocket.port:=port;{設置埠}
session[i-1].CSocket.active:=true;{連接遠端主機}
session[i-1].Lookingup:=true;{置標誌}
session[i-1].LookupTime:=0;{從0開始計時}
end
else
{假如遠端已連接,直接發送請求}
session[i-1].CSocket.socket.sendtext(session[i-1].request_str);
break;{停止掃描請求文本}
end;
j:=pos(char(13)+char(10),tmp);{指向下一行}
end;
break;{停止迴圈}
end;
//當連接遠端主機成功時
procedure TForm1.ClientSocket1Connect(Sender: TObject;Socket: TCustomWinSocket);
var i: integer;
begin
for i:=1 to sessions do
if (session[i-1].CSocket.socket.sockethandle= socket.SocketHandle)and session[i-1].Used < BR> then
begin
session[i-1].CSocket.tag:=socket.SocketHandle;
session[i-1].remote_connected:=true;{置遠端主機已連通標誌}
session[i-1].Lookingup:=false;{清標誌}
break;
end;
end;
//當遠端主機斷開時
procedure TForm1.ClientSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket);
var i,j,k: integer;
begin
for i:=1 to sessions do
if (session[i-1].CSocket.tag=socket.SocketHandle) and session[i-1].Used then
begin
session[i-1].remote_connected:=false;{置爲未連接}
if not session[i-1].client_connected then
session[i-1].Used:=false{假如客戶機已斷開,則置釋放資源標誌}
else for k:=1 to serversocket1.Socket.ActiveConnections do
if (serversocket1.Socket.Connections[k-1].SocketHandle=session[i-1].SS_Handle) and
session[i-1].used then
begin
serversocket1.Socket.Connections[k-1].Close;
break;
end;
break;
end;
j:=sessions;
k:=0;
for i:= 1 to j do
begin
if session[j-i].Used then break;
inc(k);
end;
if k>0 then{修正會話陣列}
begin
sessions:=sessions-k;
setlength(session,sessions);
end;
edit1.text:=inttostr(sessions);
end;
file://當與遠端主機通信發生錯誤時/
procedure TForm1.ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
var i,j,k: integer;
begin
for i:=1 to sessions do
if (session[i-1].CSocket.tag=socket.SocketHandle) and session[i-1].Used then
begin
socket.close;
session[i-1].remote_connected:=false;{置爲未連接}
if not session[i-1].client_connected then
session[i-1].Used:= false{假如客戶機已斷開,則置釋放資源標誌}
else
for k:=1 to serversocket1.Socket.ActiveConnections do
if (serversocket1.Socket.Connections[k-1].SocketHandle=session[i-1].SS_Handle)
and session[i-1].used then
begin
serversocket1.Socket.Connections[k-1].Close;
break;
end;
break;
end;
j:= sessions;
k:= 0;
for i:= 1 to j do
begin
if session[j-i].Used then break;
inc(k);
end;
errorcode:= 0;
if k>0 then{修正會話陣列}
begin
sessions:=sessions-k;
setlength(session,sessions);
end;
edit1.text:= inttostr(sessions);
end;
//向遠端主機發送頁面請求
procedure TForm1.ClientSocket1Write(Sender: TObject; Socket:TCustomWinSocket);
var i: integer;
begin
for i:= 1 to sessions do
if (session[i-1].CSocket.tag= socket.SocketHandle) and session[i-1].Used then
begin
if session[i-1].Request then
begin
socket.SendText(session[i-1].request_str);{假如有請求,發送}
session[i-1].Request:= false;{清標誌}
end;
break;
end;
end;
//遠端主機發來頁面資料時
procedure TForm1.ClientSocket1Read(Sender: TObject;Socket: TCustomWinSocket);
var i,j: integer;
rec_bytes: integer;{傳回的資料塊長度}
rec_Buffer: array[0..2047] of char; {傳回的資料塊緩衝區}
begin
for i:= 1 to sessions do
if (session[i-1].CSocket.tag= socket.SocketHandle) and session[i-1].Used then
begin
rec_bytes:= socket.ReceiveBuf(rec_buffer,2048); {接收資料}
for j:= 1 to serversocket1.Socket.ActiveConnections do
if serversocket1.Socket.Connections[j-1].SocketHandle= session[i-1].SS_Handle then
begin
serversocket1.Socket.Connections[j-1].SendBuf(rec_buffer,rec_bytes); {發送資料}
break;
end;
break;
end;
end;
//“頁面找不到”等錯誤資訊出現時
procedure TForm1.AppException(Sender:TObject; E: Exception);
begin
inc(invalidrequests);
end;
file://查找遠端主機定時/
procedure TForm1.Timer1Timer(Sender: TObject);
var i,j: integer;
begin
for i:= 1 to sessions do
if session[i-1].Used and session[i-1].Lookingup then{假如正在連接}
begin
inc(session[i-1].LookupTime);
if session[i-1].LookupTime > lookuptimeout then{假如超時}
begin
session[i-1].Lookingup:=false;
session[i-1].CSocket.active:=false;{停止查找}
for j:=1 to serversocket1.Socket.ActiveConnections do
if serversocket1.Socket.Connections[j-1].SocketHandle=session[i-1].SS_Handle then
begin
serversocket1.Socket.Connections[j-1].Close;{斷開客戶機}
break;
end;
end;
end;
end;
end.
三、總結
由於這種設計思路僅僅在被代理端和遠端主機之間增加了一個重定向功能,被代理端原有的緩存技術等特點均保留,因此效率較高。經過測試,利用1個33.6K的Modem上網時,三到十個被代理工作站同時上網,仍有較好的回應速度。由於被代理工作站和代理伺服器之間的連接一般通過高速鏈路,因此瓶頸主要出現在代理伺服器的上網方式上。 通過上述方法,作者成功開發了一套完善的代理伺服器軟體並與機房計費系統完全集成,實現了利用一台工作站完成上網代理、上網計費、用機計費等功能。 有編程經驗的朋友完全可以另行增加代理伺服器功能,如設定禁止訪問站點、統計客戶流量、Web訪問列表等等。 *********************************************************
哈哈&兵燹
最會的2大絕招 這個不會與那個也不會 哈哈哈 粉好 Delphi K.Top的K.Top分兩個字解釋Top代表尖端的意思,希望本討論區能提供Delphi的尖端新知
K.表Knowlege 知識,就是本站的標語:Open our mind to make knowledge together!
希望能大家敞開心胸,將知識寶庫結合一起
------
********************************************************** 哈哈&兵燹 最會的2大絕招 這個不會與那個也不會 哈哈哈 粉好 Delphi K.Top的K.Top分兩個字解釋Top代表尖端的意思,希望本討論區能提供Delphi的尖端新知 K.表Knowlege 知識,就是本站的標語:Open our mind |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |