答案:define CMD_MSG_UP 1 //客户端到服务器端,图形数据
#define CMD_MSG_DOWN 2 //服务器端到客户端,聊天语句
#define CMD_CHECK_LINK 3 //服务器端到客户端,链路检测
#define CMD_SEND_IMAGE 4 //服务器端到客户端,发送图像
#define MAX_IMAGE_SIZE 300*1024
#define MAX_TEXT_SIZE 512
struct MSG_HEAD
{
unsigned short dwCmdID;
int dwLength;
};struct MSG_UP
{
int dwLength;
char szContent[MAX_IMAGE_SIZE];
};struct MSG_DOWN
{
int dwLength;
char szContent[MAX_TEXT_SIZE];
};struct MSG_STRUCT
{
MSG_HEAD MsgHead;
union
{
MSG_UP MsgUp;
MSG_DOWN MsgDown;}one;
};
typedef MSG_STRUCT MESG;对照上面的协议,看一下我自己写的用于非阻塞模式的读取完整数据包的函数GetPacket():
bool MySocket::GetPacket(void* packet)
{
if( ! packet)
return false;
try{
int read = Receive(mBuffer+mRcvd,1024);
mRcvd += read;
//如果缓冲区中不够一个数据包头的长度,则返回。
if( read < sizeof(MSG_HEAD))
return false;
//取出数据包头中整个数据包的长度。
struct MSG_HEAD msgHead;
memcpy(&msgHead,mBuffer,sizeof(MSG_HEAD));
int packetLength = msgHead.dwLength;
//如果数据包大于缓冲区长度或者小于0,则肯定不合法,返回并将mRcvd置0。
if( packetLength > sizeof(MSG_STRUCT) || packetLength <= 0)
{
mRcvd = 0;
return false;
}//如果缓冲区中存在足够数据,则解析数据包并返回。
if(mRcvd >= packetLength)
{
memcpy(packet,mBuffer,packetLength);
mRcvd -= packetLength;
memmove(mBuffer,mBuffer+packetLength,mRcvd);
return true;
}//没有完整数据包,返回假。
return false;
}
catch(Exception &e){
if(e.ErrorCode() == EOperationWouldBlock){
return false;
}
//若是其他错误则向上层抛出。
throw;
}}
经测试,上面的GetPacket应该不会有问题,而且也不用担心粘包问题,注释比较详细,我就不详解了。
客户端的逻辑模块比较简单,首先不停的连接服务端,20秒一次,直到连接成功。然后不停检查套接字是否可读并根据读取的数据包进行相关操作。代码如下:
DWORD WINAPI WorkThread(LPVOID lparam)
{
//不断反向连接服务器,直到连接成功,注意这里sock默认是阻塞模式while(true)
{
try{
gsocket.Connect(serverIP,5520);
break;
}
catch(Exception &e){
if(e.ErrorCode() == EAlreadyConnected){
break;
}
else{
Sleep(20*1000);
continue;
}
}
}//设置为非阻塞模式
gsocket.SetBlocking(false);
sset.AddSocket(gsocket);
while(true)
{
try{
sset.Poll(2*1000);
if(sset.HasActivity(gsocket))
{
MESG msg;
if(gsocket.GetPacket(&msg))
{
if(msg.MsgHead.dwCmdID == CMD_MSG_DOWN)
{
std::string tmpstr = msg.one.MsgDown.szContent;
AfxMessageBox(tmpstr.data());
}
else if(msg.MsgHead.dwCmdID == CMD_SEND_IMAGE)
{
DWORD ThreadId;
CreateThread(NULL,0,SendImage,0,0,&ThreadId);
}