即时通讯系统的设计与实现毕业设计论文 下载本文

char m_nUserName [20]; //用户名(20bytes) char m_nPassword[32]; //密码md5值(32bytes)

} DATA_LOGIN; (2)应答包定义

应答包的包头中,除了PacketLength包体长度和ReturnCode返回码以外,其他的都与请求包相同。在应答包中,m_nPacketLength = 4,ReturnCode则用来标识认证是否通过,具体返回码可以参见2.1中的内容。

包体中则是认证后返回给客户端的对应用户的ID码,如果认证失败,返回的ID码为0,程序将关闭其SOCKET其结构定义如下:

m_nPacketLength = 4; typedef struct data_back_login {

uint32 m_nStaffID; } DATA_BACK_LOGIN; 2)获取用户详细信息

客户端通过发送请求包,告知服务器要进行获取信息的操作以及需要获取何用户的信息,以ID号标识用户。此操作的MajorType为0x01,ServiceCode为0x02,OperationCode为0x28,包体长度为4,仅包含要请求的用户信息对应的ID。以下为该协议请求包和应答包结构定义的关键实现代码:

(1)请求包定义

其中,包头中关键属性设置为: m_nMajorType = 0x01; m_nServiceCode = 0x02; m_nOperation = 0x28; m_nPacketLength = 4; 包体的结构定义如下: typedef struct data_getInfo {

int32 m_nStaffID; } DATA_GETINFO; (2)应答包的定义

同其他应答请求包一样,包头中的m_nMajorType,m_nServiceCode,m_nOperation这三个标识操作类型的属性在请求包和应答包中是对应相等的。而

作为应答包,另一个重要的关键属性就是ReturnCode的值,以此来告知请求方服务端对其请求的响应结果。

此应答包包体包含内容为对应请求包包体中ID号的对应用户的全部详细信息。结构定义代码如下: Typedef struct staff_info {

Int32 m_nStaffID; Int8 m_nStaffNameLen;

Char m_nStaffName[MAX]; //员工姓名 Int8 m_nEmailLen;

Char m_nEmail[MAX]; //联系邮箱 Int8 m_nPhoneLen;

Char m_nPhone[MAX]; //手机号 Int8 m_nAddressLen;

Char m_nAddress[MAX]; //联系地址 Byte m_nSex; //性别(0男 1女) Byte m_nDateTime[8]; //加入时间(YYYYMMDD) Byte m_nBirthday[8]; //出生年月(YYYYMMDD)

Byte m_nUserState; //用户在线状态,0表示在线 1表示不在线 } STAFF_INFO

//员工ID

3.3 本章小结

本章主要介绍了本系统在TCP模式下具体的数据传输办法,详细设计出了系统C/S结构下Client端与Server端之间进行网络传输的通信协议。并在通信协议数据包中,以三个常见的典型功能作为示例论述了协议包的设计和实现方案。

4 网络数据传输控制模块的设计与实现

4.1 异步套接字与多线程同步介绍

4.1.1 SOCKET的阻塞方式

在SOCKET网络编程中,WinSocket基本API的socket函数通常是以阻塞方式进行的,即在执行数据收发过程的时候,程序线程会在收发过程完成之前阻

塞,只有在完成了当前数据收发工作后,才会执行其他指令,这样一来,接收数据、发送数据、处理数据、逻辑过程等都无法同步进行,在频繁的网络数据传输过程中,各种传输工作以及处理工作都有可能同时发生,无法同步显然会造成用户使用过程中过多的等待,给用户操作带来极大的不良感觉。

在这样的情况下,必须通过自行创建额外线程来辅助程序主线程的工作,以多线程同步的方法来解决阻塞方式数据传输带来的不良影响。为此,除了主线程外,每一次数据接收、数据发送、数据处理等工作都将分别单独占用一个线程,以此来完成数据接收发送以及处理的同步工作。图4-1所示即为多线程控制下的数据传输同步过程的示意图。

发送线程 线程启动 由线程函数参数获得数据信息 调用SEND发送数据 接收线程 线程启动 等待接收到来的数据 接收数据 将收到的数据做为参数创建处理线程 解读数据包分析数据 按照数据完成相应操作 线程关闭 处理线程 线程启动 由线程函数参数获得数据包信息 主线程 线程启动 功能初始化 等待用户操作指令 根据用户指令创建请求发送线程 线程关闭程序结束 反馈发送结果(成功与否) 关闭线程 线程随主线程关闭

图4-1 多线程实现数据传输同步过程

4.1.2 异步套接字的使用

在WinSock库中,本身也提供了一种基于消息的非阻塞式数据传输方法,称为异步套接字。在异步套接字中,无论数据的接收或发送工作是否完成,函数都会立刻返回。这也是为了支持Windows的消息驱动机制,使开发者能够更方便的处理网络通信,同时也有利于提高应用程序的性能。在异步套接字中,操作系统会监听程序中登记的网络事件消息,当有网络I/O事件发生时,Windows系统会给应用程序窗口发送相应的消息加入主线程消息循环列表中,应用程序则根据消息内容进行相关操作。

CAsyncSocket是MFC对异步套接字的一个简单封装,其中包含了创建

socket、建立连接、发送接收数据、对应消息响应等成员函数,本系统就是通过由CAsyncSocket自行派生异步套接字类来创建自己的网络传输管理对象。以此方式来避免多线程同步中带来的一些较为棘手的问题。

4.2 网络传输管理模块的设计与实现

4.2.1 数据包缓冲区的定义

在系统中,创建一个数据缓冲区来存放即将要发送的数据或者刚刚接收到的数据,在缓冲区中,所有类型的数据包都以字节流的形式存放在缓冲区中。在此,对缓冲区还要进行封装,构造一个提供按字节读取数据的READ接口和将数据按字节写入的WRITE接口的缓冲区对象,同时,为了该对象与网络传输模块进行良好的洽接,还为该对象加入将其中数据写出发至网络或者将网络中发来的数据写入缓冲区的功能。下面是程序中对缓冲区对象类的定义源代码:

class CBuffer {

public: CBuffer(); virtual ~CBuffer(); //初始化,申请空间,由长度参数确定缓冲区实际大小 bool Init(uint32 len); //关闭,所有成员置空置 void Shutdown(); //将数据写入缓冲区 uint32 Write(int8 * buf,uint32 len); int32 SocketSend(CIMSocket * socket); //将m_buf中的数据发送到指定的SOCKET上 int32 SocketRecv(CIMSocket * socket); //在指定的SOCKET上接收数据,放入m_buf中,返回缓冲区的读标记位

int8 * GetBuffer(); //获得缓冲区数据空间的首地址指针 uint32 GetSize(); //获得缓冲区的长度大小 int8 ReadChar();//读取一个字节并做为char类型数据返回 uint8 ReadByte(); //读取一个字节以无符号整形返回 int16 ReadShort(); //读取两个字节以带符号数返回 uint16 ReadWord(); //读取两个字节以无符号数返回 int32 ReadInt(); //读取四个字节以带符号形式返回 uint32 ReadDword(); //读取四个字节以无符号形式返回 int64 ReadLongLong(); //读取八个字节以带符号形式返回 uint64 ReadULongLong(); //读取八个字节以无符号形式返回 //读取一定长度的数据以String类型返回结果 void ReadString(std::string & str,uint32 len); void ReadString(int8 * buf,uint32 len); protected: bool ReadData(int8 * buf,uint32 bytes);