上一次我们讲过了GPRSServer那个模块,这结我们讲ClientIn这个模块。上一节我留下一个东西没有讲,就是CutReceiveData这个函数,这个函数其实很大,涉及到的内容也很多,当然也涉及到一些商业机密。你们只要知道它是用来解包的就可以了,当然也可以把CutReceiveData做成一个类,但我没有这样做,速度和编程规范两者要我选择的话,我一般会选择速度,虽然我这段时间一直在写模式设计这个体系的东西,但我编程一般的原则是,能用结构绝不用类,能用函数决不用结构,能用指针决不用高级方法,关键方法的编写用汇编而不用函数,尽量避免用接口,能用GOTO的决不用Break,尽量将所有的程序做到一个模块中,当然还有能用别人的代码的就用别人代码。这样做可以最大限度的保证高速度。因为我觉得评价一个程序的好坏的指标就是:运行速度,稳定性,可扩展性,外延深度。代码好不好看是次要的。别人用的是你的程序而不是看你的语法。
下面我们看看ClientIn是如何工作的。
我们先来看看这个模块总的结构:
2
3interface
4
5uses
6 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
7 Dialogs,unitmain,unit1message,UnitProtocal,UnitMatch, ScktComp,IniFiles,
8 StdCtrls, ComCtrls, ToolWin, Menus,DateUtils,ComObj, ExtCtrls;
9
10type
11 TFormClientIn = class(TForm)
12 ToolBar1: TToolBar;
13 ToolButton1: TToolButton;
14 ToolButton2: TToolButton;
15 ToolButton3: TToolButton;
16 ToolButton4: TToolButton;
17 Memo1: TMemo;
18 ToolButton5: TToolButton;
19 SaveDialog1: TSaveDialog;
20 PopupMenu1: TPopupMenu;
21 SaveInfo1: TMenuItem;
22 ServerJava: TServerSocket;
23 TimerSend: TTimer;
24 TimerSendJ: TTimer;
25 Timer54: TTimer;
26 procedure FormCreate(Sender: TObject);
27 procedure ToolButton1Click(Sender: TObject);
28 procedure ToolButton2Click(Sender: TObject);
29 procedure ToolButton3Click(Sender: TObject);
30 procedure ToolButton4Click(Sender: TObject);
31 procedure ToolButton5Click(Sender: TObject);
32 procedure SaveInfo1Click(Sender: TObject);
33 procedure FormDestroy(Sender: TObject);
34 procedure ServerJavaClientError(Sender: TObject;
35 Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
36 var ErrorCode: Integer);
37 procedure ServerJavaClientRead(Sender: TObject;
38 Socket: TCustomWinSocket);
39 procedure ServerJavaClientConnect(Sender: TObject;
40 Socket: TCustomWinSocket);
41 procedure ServerJavaClientDisconnect(Sender: TObject;
42 Socket: TCustomWinSocket);
43 procedure TimerSendTimer(Sender: TObject);
44 procedure TimerSendJTimer(Sender: TObject);
45 procedure Timer54Timer(Sender: TObject);
46
47 private
48 procedure WMCopyData(var M:TMessage);Message WM_COPYDATA;
49 Procedure ReMessage(Var Msg:TMessage);Message ServerSendToClientIn;
50 procedure ClientTimerSend(var Msg:TMessage);message ClientTimer;
51 Procedure JEndFlgMessage(Var Msg:TMessage);Message JEndFlgM;
52 Procedure JPacketIDFlgMessage(Var Msg:TMessage);Message JPacketIDM;
53 procedure AddErrorToLine(Var Msg:TMessage); Message SendToClientInError;
54 public
55 procedure Set_SocketInfo(S:String);
56 procedure SendMessageTo(hwndSend: HWND; s: String;
57 Mg:DWord;Wparam: Word; Flg: boolean);
58 procedure AppException(Sender: TObject; E: Exception);
59 function Change(B:byte):byte;
60 end;
总体上来说这个模块里的函数并不多,为什么很少呢?因为这个模块的功能主要是将我们上章讲的GPRSServer模块处理好的数据包转发给Java后台,同时将Java后台发送过来的数据包分析后转发给GPRSServer模块,让GPRSServer模块发给下位机。所以数据解析的大部分工作都让GPRSServer里的CutReceiveData给做掉了。我们来看看他的第一个任务是如何完成的。
第一个任务:转发GPRSServer送过来的数据,因为GPRSServer已经将下位机传送上来的数据解析成了电量电压电流等等数据了,所以我们的ClientIN只要做:收到——〉确认——〉发给Java后台就OK了。
看看转发的代码:(我真的受不了了,才写好的文章,被博客网一停止响应给毁了,还要重写)
2var
3Count,i,l:integer;
4PP:Pchar;
5S,SS,sss,GPRSIDSTR,SSS_Old:string;//测试用
6hwndReceiver:HWND;
7label H1;
8begin
9if (PcopyDataStruct(M.LParam)^.dwData=ServerSendToClientInInfo) then
10 begin
11 S:=Pchar(PCopyDataStruct(M.LParam)^.lpData);
12 Count:=ServerJava.Socket.ActiveConnections;
13 for i:=0 to Count-1 do
14 begin
15 ServerJava.Socket.Connections[i].SendText(S);
16 end;
17 if G_ShowInfo then
18 Memo1.Lines.Add(S);
19 end;
20 if (PcopyDataStruct(M.LParam)^.dwData=ServerInSendToClientInInfo) then
21 begin
22 S:=Pchar(PCopyDataStruct(M.LParam)^.lpData);
23 memo1.Lines.Add('ClientIn<--ServerIn:收到数据!'+S+#13+#10);
24 sss:=S;
25 SSS_Old:=S;
26 SS:='';
27 GPRSIDSTR:='';
28 ////////////////////////////////
29 ///
30 /// 假如为GPRS数据包则先进行拆包,对内部数据包进行打包后再进行合包,发送到TFormServer后再进行具体发送
31 /// 包格式为:%%%%|13851823257|####|后面格式不变
32 /////////////////////////////////
33 if copy(sss,1,5)='%%%%|' then
34 begin
35 GPRSIDSTR:=copy(sss,1,17);
36 sss:=copy(sss,18,1024);
37 end;
38 ///////////////////////////////////////////////////////////////////////////////
39 //
40 //对9520H,J进行打包
41 ///////////////////////////////////////////////////////////////////////////////
42 if copy(sss,1,5)='####|' then
43 begin
44 Goble_S:='';
45 SS:=IPro.BulidSendData(sss);
46 if SS='' then
47 goto H1;
48
49 if copy(SSS_Old,1,5)='%%%%|' then
50 begin
51 SS:=GPRSIDSTR+SS;
52 hwndReceiver:=findwindow('TFormGPRSServer',nil); //向GPRS服务器发送数据
53 if hwndReceiver<>0 then
54 SendMessageTo(hwndReceiver,SS,ClientInSendToGPRSServer,0,true);
55 end else
56 begin
57 hwndReceiver:=findwindow('TFormServer',nil);//向无线电服务器发送数据
58 if hwndReceiver<>0 then
59 SendMessageTo(hwndReceiver,SS,ClientInSendToServerInfo,0,true);
60
61 end;
62 H1:
63 end;
64 end;
65end;
其实这个消息函数中涉及到ClientIn转发功能的也只有9~19行,上面的15行就是将GPRSServer发送过来的数据包通过Socket转发给JSP后台。我们从上面的代码中可以看出ClientIn模块并没有对这些数据包做容错处理,因为我们在GPRSServer模块中已经做过容错处理了,就是在我上面讲的CutReceiveData中。当然我们还会在Java后台中再作一次容错处理,但是在Java后台中作的容错操作要比在CutReceiveData中做的容错要简单得多了。
当然我们在GPRSServer模块和Java后台的通讯上也作了很多的约定,比如说数据包如何转换,数据包的格式之类的东西,有很多,我写了大概有一本书之多。为什么要有这些约定呢?你们可能要问,直接将Java后台发送的数据包传送给底层模块不就行了吗。还需要这么麻烦用GPRSServer和ClientIN来转发吗。这个里面涉及到一些语言之间的差别,我们下一章再说。
第二个任务:ClientIN 将Java后台发送过来的数据包解析之后发送给GPRSServer模块。
我们来看看转发代码:
2 Socket: TCustomWinSocket);
3var
4Count,i,j:integer; //测试用
5S,SS,sss,GPRSIDSTR,SSS_Old:string;//测试用
6hwndReceiver:HWND;
7label H1;
8 begin
9 try
10while socket.ReceiveLength>0 do
11 begin
12 Goble_S:='';
13 Goble_S:=socket.ReceiveText;
14 memo1.Lines.Add('ClientIn<--JavaServer:收到数据!'+Goble_S+#13+#10);
15 sss:=Goble_S;
16 SSS_Old:=Goble_S;
17 SS:='';
18 GPRSIDSTR:='';
19 ////////////////////////////////
20 ///
21 /// 假如为GPRS数据包则先进行拆包,对内部数据包进行打包后再进行合包,发送到TFormServer后再进行具体发送
22 /// 包格式为:%%%%|13851823257|####|后面格式不变
23 /////////////////////////////////
24 if copy(sss,1,5)='%%%%|' then
25 begin
26 GPRSIDSTR:=copy(sss,1,17);
27 sss:=copy(sss,18,1024);
28 end;
29 ///////////////////////////////////////////////////////////////////////////////
30 //
31 //对9520H,J进行打包
32 ///////////////////////////////////////////////////////////////////////////////
33 if copy(sss,1,5)='####|' then
34 begin
35 Goble_S:='';
36 SS:=IPro.BulidSendData(sss);
37 if SS='' then
38 goto H1;
39
40 if copy(SSS_Old,1,5)='%%%%|' then
41 begin
42 SS:=GPRSIDSTR+SS;
43 hwndReceiver:=findwindow('TFormGPRSServer',nil); //向GPRS服务器发送数据
44 if hwndReceiver<>0 then
45 SendMessageTo(hwndReceiver,SS,ClientInSendToGPRSServer,0,true);
46 end else
47 begin
48 hwndReceiver:=findwindow('TFormServer',nil);//向无线电服务器发送数据
49 if hwndReceiver<>0 then
50 SendMessageTo(hwndReceiver,SS,ClientInSendToServerInfo,0,true);
51
52 end;
53 H1:
54 if G_ShowInfo then
55 begin
56 S:='';
57 for j:=1 to length(SS) do
58 begin
59 S:=S+inttohex(ord(SS[j]),2)+' ';
60 end;
61 memo1.Lines.Add('通讯服务器<--后台:收到数据!'+S+#13+#10);
62 end;
63 end;
64 end;
65 except
66 on EConvertError do begin Goble_S:='';abort; end;
67 end;
68end;
这个里面的36行就是将Java后台发送的数据包解析成GPRSServer模块能够识别的数据包的函数,其实36行是一个接口:IProtocal,我将这个接口设计成了Singleton模式。我将这类解析过程全部封装到了这个接口中。你们只要知道它的作用就可以了,因为里面涉及到商业机密。
上面的45行和50行就是将已经解析好的数据包按照约定发送给GPRSServer和Server(无线电)模块。
总体来说整个框架还是比较简单的。
ClientIn里面一些很“变态”的程序我就不讲了,以免你们对主体程序产生混淆。我在毕业设计文章的时候总是在想,要是再让我写一次这么变态的程序,我还会不会去写。下一章我将会告诉你们为什么我们需要用到转换。