代理服务器是我参加工作以来开发的第一个项目。代理服务器的功能是提供一个协议重构、转发的服务器端程序。这个程序功能比较简单,但是要求工作稳定,可以长期工作,减少人工干预。这个项目开始于07年1月份,3月份已经完成了调试工作。现在开始了一个新的工作。下面介绍一下代理服务器开发过程中的一些想法。
一、 通信部分。
代理服务器负责将局域网中的协议转发到公网中,局域部分的通信组件现在已经成熟,公网中的通信组件现在还没有,因此,这个项目就从公网通信组件开发。
公网通信组件是使用的是UDP的通信。UDP与TCP相比,它的连接方便,传输速度快,但是它是一种不可靠的传输方式,所以要想使用UDP方式传输数据,就要首先确定一个可靠的数据传输机制。UDP的可靠数据传输,就是“发送端”要保证“接收端”确实接收到了,否则就要重新发送一次。因此在这个通信组件中设计了双列表,即发送列表和接收列表。组件将要发送的数据分解成一个个小的数据帧保存在发送列表中,然后发送这些数据帧,当接收端接收到了“接收端”的接收确认信息后,将发送确认的数据帧从列表中删除。组件将接收到的数据帧组合成一个完的数据包,当数据包组合完,将这些数据包提交给上层应用程序处理,并将该数据包从列表中删除。当发送列表中的数据包超时,将重新发送数据数据包。当接收数据包中的数据超时,将将发送重发请求帧。这种发送机制可以保证数据的可靠传输。UDP方式在公网中的传输的推荐长度为512个字节,这就是要净一个发的数据包分解成一个个的数据帧的原因,这里将一个数据包分解成512个字节的数据帧在网络中传输,也可以提高数据传输的稳定性。
由于自己在网络传输方式的经验不足,这里没有考虑到网络速度的因素。虽然现在网络带宽足够使用,但是在设计时,还是要限制瞬间发送的数据数量。瞬间发送大量的数据时,将造成数据溢出缓冲区,只是发送了少量的数据,丢失了大量的数据,而且UDP方式下,sendto函数返回的不是实际发送出去的数据个数,该函数的返回值对UDP方式的意义不大。现在考虑这个网络速度方面的限制因素,在设计中,应当增加第三个数据列表,这个数据列表用于保存实际正在操作的数据列表,这个列表进行长度限制,这个表的起到的是一个窗口的作用。使用三个数据列表,使通信部分的设计更趋于完善。
二、 协议分析部分。
协议分析是代理服务器的一个重要功能。代理服务器的协议分析部分需要处理两部分的协议,局域网协议和公网协议,这两部分的协议解析结构是一致的。
协议分析部分的结构如下:
协议分析基类 |
协议1 |
协议N |
…… |
图1 协议分析类结构 |
在协议分析基类中定了一些协议分析所需要的一些基本的成员和方法。
表1 协议分析基类声明 |
class CBussBase { protected: CYxNaviProxyFar* m_pNaviProxy; public: CBussBase(void); virtual ~CBussBase(void);
int Set_NaviProxy(CYxNaviProxyFar* const v_pNaviProxy) { assert( NULL != v_pNaviProxy ); m_pNaviProxy = v_pNaviProxy; return 0; } virtual int Deal(RECEIVEDATA* const v_pReceiveData) = 0; protected: int ShowWndLog(const int v_iTypeLog, const char v_szWndLog[]); int ShowWndLog(const int v_iTypeLog, const UINT v_nIDLog); protected: int PrintFileLog(const char v_szDescription[],const char v_szBuf[]); }; |
基类中的方法Deal(RECEIVEDATA* const v_pReceiveData)是一个纯虚函数,子类通过重载这个函数来实现协议的分析工作。
三、 实现方面。
在程序设计上,每一个部分的功能都比较独立,因此在实现时,每个独立的功能都封装成为一个独立的COM组件,组件通过指针进行相互调用。
局域网端协议分析组件 |
公网端协议分析组件 |
公网通信组件 |
图2 组件间调用关系 |
这种设计大大降低了程序之间的代码耦合度,分个组件的功能单一,这也降低了组件中代码的复杂度,减小了开发的难度,而且组件的更换也更加容易。
总节:代理服务器的功能比较单一,所以设计的结构也比较简便,但是由于每个部分都使用了异步调用的方式进行调用,因此程序不容易调试,这其中通信组件的调试难度要比其它的组件大,这主要是因为通信组件中异步调用比较多的原故。