用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{修正會話數組,釋放尾部未用項}