介绍
上一篇所实现的TCP程序没有包括通信协议的内容,因此服务器和客户端只能进行简单的信息交流。当交互的要求比较多的时候(比如说客户端不只是发送文本信息,同时还要求能够对服务器数据进行增、删、改等操作),通常会定义一些通信协议。
通信协议由命令号和附加数据所组成。命令号也叫协议号,就是用一个数字或者字符串通知对方执行一个对应的命令;附加数据包含与某个命令相关的数据。
通信协议的使用使客户端和服务器的交流更有条理性。
实例
创建一个C/S结构的网络版信息管理软件
1. 首先,定义需要的协议编号和数据信息:
enum
{ //协议编号
INF_ADD=0x1234,
INF_BROW,
INF_DEL,
INF_MOD,
INF_FIND
};
struct SData
{ //传输的数据
int nNumb;
char sName[20];
float fSala;
}
将以上代码同时添加到服务器和客户端的App类的头文件中;
2. 分别开发服务器程序和客户端程序:
A.服务器程序
(1)在App头文件中添加一个变量,用于管理数据:
CList<SData,SData> m_list;
(2)添加两个CSocket类,用于侦听和应答:
在CListenSocket类中:
添加虚函数OnAccept用于获得客户端的连接;
在CCLientSocket类中:
添加虚函数OnClose,用于检查客户端的断开;
添加虚函数OnReceive,用于接收客户端命令并做出相应的反应:
void CClientSocket::OnReceive(int nErrorCode)
{
int nCmd;
Receive(&nCmd,sizeof(nCmd));//接受命令号
switch (nCmd)
{
//根据不同的命令号做出相应的操作
case INF_ADD:
AddData();
break;
case INF_BROW:
Browse();
break;
}
CSocket::OnReceive(nErrorCode);
}
//以下为自定义的函数
void CClientSocket::AddData(void)
{ //添加数据
SData data;
Receive(&data,sizeof(data));//接收客户端的数据
theApp.m_list.AddTail(data);//存入列表
}
void CClientSocket::Browse(void)
{ //浏览数据
int nCount=theApp.m_list.GetCount();
Send(&nCount,sizeof(nCount));//将列表中的信息条数先发给客户端
POSITION pos=theApp.m_list.GetHeadPosition();
while(pos)
{
SData data=theApp.m_list.GetNext(pos);
Send(&data,sizeof(data));//再逐条发送信息
}
}
(3)在主对话框中定义CListenSocket的成员变量,并在初始化函数中创建端口并调用侦听函数进行侦听。
B.客户端程序
(1)编辑客户端界面;
(2)在主对话框类中添加CSocket类的成员变量,并在初始化函数中创建端口并调用Connect函数连接服务器;
(3)为“添加”按钮建立消息响应函数,同时在初始化函数中添加OnRefresh函数,用于每次登录后从服务器处获得信息更新显示列表:
void C客户端Dlg::OnBnClickedAdd()
{
int nCmd=INF_ADD;
m_sock.Send(&nCmd,sizeof(nCmd));//先发送命令:添加数据
//获取要添加的数据并存入SData中,用于发送
SData data={GetDlgItemInt(IDC_NUMB)};
GetDlgItemText(IDC_NAME,data.sName,sizeof(data.sName));
CString str;
GetDlgItemText(IDC_SALA,str);
data.fSala=(float)atof(str);//将string型转化成float型数据
//发送信息
m_sock.Send(&data,sizeof(data));
//将添加的一条信息发给服务器后,更新列表
OnRefresh();
}
void C客户端Dlg::OnRefresh()
{
int nCmd=INF_BROW;
m_sock.Send(&nCmd,sizeof(nCmd));//发送浏览命令
int nCount=0,i=0;
m_sock.Receive(&nCount,sizeof(nCount));//得到新列表的信息条数
list.DeleteAllItems();//列表控件清空
SData data;
CString str;
while(i<nCount)
{ //逐条接受数据,并显示在列表中
m_sock.Receive(&data,sizeof(data));
str.Format("%d",data.nNumb);
list.InsertItem(i,str);
list.SetItemText(i,1,data.sName);
str.Format("%0.2f",data.fSala);
list.SetItemText(i,2,str);
++i;
}
}
运行效果:
在另外一台电脑上同时运行一个客户端程序,会看到程序一打开就会载入与此客户端相同的数据;而且当在任意客户端上添加数据时,会看到其他对话框中也添加了此条数据。
原因是:当某个对话框添加数据后,同时会向服务器发送此条消息,服务器作为中转站又向所有在线的客户端发送消息,从而使得各个客户端的数据同步。