答题接口和答题器之间的通讯协议

数据结构定义:

// 答题类型 对应于FaqRequestPacket中的request_type
#define REQUEST_TYPE_POS   0           //
坐标
#define REQUEST_TYPE_ABCD  1           //
选项
#define REQUEST_TYPE_OTHER 2           //
文本
#define REQUEST_TYPE_DOUBLE_POS 3      //
双坐标

 

#pragma pack(push) //保存对齐状态
#pragma pack(4)//
设定为4字节对齐

// 发送包结构体
typedef struct
{
    //
服务器转发用(分流的时候可能会用到)
    SOCKET socket;               //
分流上级发送的socket id.
    char  ip[32];                //
分流上级机器IP

// 客户端内容
    DWORD request_type; //
答题类型
    DWORD data_len;     //
真实数据的长度
    char  data[1];      //
数据指针.
}FaqRequestPacket;

// 接收包结构体
typedef struct
{
    //
服务器转发用
    SOCKET socket;         //
FaqRequestPacket
    char  ip[32];          //
FaqRequestPacket

// 客户端内容
    char result[256];      //
接收到的答案. 文本形式描述.
}FaqReceivePacket;

#pragma pack(pop)//恢复对齐状态

组织结构图通讯过程流程描述

 

 

 

 

 

 

 

 

 

 

 

 

 

发送数据包详细格式解析:

发送的数据包由包头+数据体 两部分组成.

其中包头的长度是sizeof(FaqRequestPacket).

数据体的长度是FaqRequestPacket结构体中data_len指定的长度. 用图形描述如下:

                              整个数据包(发送)

   

        FaqRequestPacket(12字节)  |    数据(数据头+数据体)

 

      长度sizeof(FaqRequestPacket)        长度(data_len)

 

其中数据部分的结构定义如下:

数据部分包含2部分,数据头和数据体.
数据头12个字节. 4个字节是一个头标识. 内容是4个字符 'D' 'M' 'F' 'Q'.
接下来4个字节的内容表示当前图像有多少帧. 如果是静态图像此值为1或者65535. (65535表示是BMP图像数据,65534表示是TXT文字数据(文字编码是GBK))
再接下来4个字节表示每帧之间的延时是多少毫秒. 如果是静态图像此值为0

数据体部分是连续顺序存放图片数据. 按照帧的顺序依次存放. 每个帧前的4个字节表示当前帧有多少个字节.比如
(
长度)帧数1(长度)帧数2……(长度)帧数N.   每个帧的图像格式是jpeg或者bmp格式,或者是TXT文字数据

综上,发送包总的长度描述如下:
12
字节(sizeof(FaqRequestPacket)) + 12字节(数据头) + 4字节(第一帧长度) + (第一帧数据) + 4字节(第二帧长度) + (第二帧数据) + .


接收包的格式很简单。就不多说了.

 

注意的是,以上只是接收和发送的纯数据格式. 真正发送时,还会在包头加上序列号,长度等校验信息. 以下是我用到的发送和接收数据的函数源码. 其他语言也可以参考.

 

 

// Common Return Code.

#define RET_SUCCESS                                  1

#define RET_FAIL                                 -1

 

#define RET_NET_NOT_INIT                         -2

#define RET_NO_ERROR                             1

#define RET_ACCEPT_SOCKET_ERROR                      -1

#define RET_CLIENT_SOCKET_ERROR                      -2

#define RET_NO_SOCKET                                -3

#define RET_NO_CONNECTION                            -4

#define RET_SOCKET_CLOSED                            0

#define RET_TIMEOUT                                  -5

#define RET_SOCKET_SHUT_DOWN                         -6

#define RET_SERVER_NOT_PREPARED                      -7

#define RET_SOCKET_NO_ORDER                          -8

#define RET_CONNECT                                  -9

#define RET_DISCONNECT                               -10

#define RET_NO_AVAILABLE_PEER                        -11

#define RET_NO_PEER                                  -12

#define RET_NO_PACKAGE                               -13

 

#define READ_TIMEOUT                             5000

#define WRITE_TIMEOUT                                5000

 

#define READ_UNIT_SIZE                        512 // less than general MTU size.

#define WRITE_UNIT_SIZE                       512 // less than general MTU size.

#define MSG_LEN_INFO_SIZE                     4

 

#define MAX_NET_TRIAL                         30

 

#define CHECK_ALIVE_VALUE                     -987654321

 

#define MAX_RECV_AGAIN                        3

 

int TCPRead(SOCKET Socket, char *Buffer, DWORD BufferSize, DWORD TimeoutMilli, DWORD *ErrorCode)

{

    BOOL bError;

    int ReturnCode;

    char LenInfo[MSG_LEN_INFO_SIZE];

    int RetLen;

    char ReadBuff[READ_UNIT_SIZE];

    DWORD CopyingSize;

    int ResRecv;

    int Timeout;

    char *pReadingPoint;

    int ToReadSize;

    int UnitReadSize;

    int TimeoutOld;

    int OutLen;

    int LoopCount;

    int RecvAgainCount;

 

    bError = 0;

    ReturnCode = RET_FAIL;

    TimeoutOld = -1;

 

    if (0 > Socket || 0 == ErrorCode)

    {

       bError = 1;

       ReturnCode = RET_FAIL;

       goto ErrHand;

    }

 

    *ErrorCode = 0;

 

    OutLen = sizeof (TimeoutOld);

    if (SOCKET_ERROR == getsockopt(Socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&TimeoutOld, &OutLen))

    {

       *ErrorCode = WSAGetLastError();

       bError = 1;

       ReturnCode = RET_FAIL;

       goto ErrHand;

    }

 

    Timeout = TimeoutMilli; // Assigning is due to input the address of the Timeout. 0 = infinite.

    if (SOCKET_ERROR == setsockopt(Socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&Timeout, sizeof (Timeout)))

    {

       *ErrorCode = WSAGetLastError();

       bError = 1;

       ReturnCode = RET_FAIL;

       goto ErrHand;

    }

 

    if (0 == Buffer || 0 == BufferSize)

    {

RecvAgainNew:    

       RecvAgainCount = 0;

RecvAgain:

       ZeroMemory(LenInfo, sizeof (LenInfo));

       ResRecv = recv(Socket, LenInfo, sizeof (LenInfo), MSG_PEEK);

       if (SOCKET_ERROR == ResRecv)

       {

           *ErrorCode = WSAGetLastError();

          

           if (WSAECONNRESET == *ErrorCode || WSAECONNABORTED == *ErrorCode || WSAESHUTDOWN == *ErrorCode || WSAENETRESET == *ErrorCode)

           {

              bError = 1;

              ReturnCode = RET_SOCKET_SHUT_DOWN;

              goto ErrHand;

           }

           else if (WSAETIMEDOUT == *ErrorCode)

           {

              bError = 1;

              ReturnCode = RET_TIMEOUT;

              goto ErrHand;

           }

          

           bError = 1;

           ReturnCode = RET_FAIL;

           goto ErrHand;

       }

       else if (0 == ResRecv) // socket closed.

       {

           bError = 1;

           ReturnCode = RET_SOCKET_CLOSED;

           goto ErrHand;

       }

       else if (sizeof (LenInfo) > ResRecv)

       {

           if (MAX_RECV_AGAIN <= RecvAgainCount)

           {

              bError = 1;

              ReturnCode = RET_FAIL;

              goto ErrHand;

           }

           RecvAgainCount++;

           goto RecvAgain;

       }

       else if (sizeof (LenInfo) != ResRecv)

       {

           bError = 1;

           ReturnCode = RET_FAIL;

           goto ErrHand;

       }

       else

       {

           memcpy(&RetLen, LenInfo, sizeof (LenInfo));

           if (CHECK_ALIVE_VALUE == RetLen)

           {

              ResRecv = recv(Socket, LenInfo, sizeof (LenInfo), 0); // recv() may return arbitrary value. <- but it can handle the size of data as much as MTU.

              if (SOCKET_ERROR == ResRecv)

              {

                  *ErrorCode = WSAGetLastError();

                 

                  if (WSAECONNRESET == *ErrorCode || WSAECONNABORTED == *ErrorCode || WSAESHUTDOWN == *ErrorCode || WSAENETRESET == *ErrorCode)

                  {

                     bError = 1;

                     ReturnCode = RET_SOCKET_SHUT_DOWN;

                     goto ErrHand;

                  }

                  else if (WSAETIMEDOUT == *ErrorCode)

                  {

                     bError = 1;

                     ReturnCode = RET_TIMEOUT;

                     goto ErrHand;

                  }

                 

                  bError = 1;

                  ReturnCode = RET_FAIL;

                  goto ErrHand;

              }

              else if (0 == ResRecv) // socket closed.

              {

                  bError = 1;

                  ReturnCode = RET_SOCKET_CLOSED;

                  goto ErrHand;

              }

 

              goto RecvAgainNew;

           }

           else if (0 >= RetLen)

           {

              bError = 1;

              ReturnCode = RET_SOCKET_NO_ORDER;

              goto ErrHand;

           }

           else

           {

              bError = 0;

              ReturnCode = RetLen;

              goto ErrHand;

           }

       }

    }

    else // read the data.

    {

       CopyingSize = 0;

       LoopCount = 0;

       ToReadSize = BufferSize + sizeof (LenInfo);

       while (1)

       {

           if (sizeof (ReadBuff) <= ToReadSize)

           {

              UnitReadSize = sizeof (ReadBuff);

           }

           else

           {

              UnitReadSize = ToReadSize;

           }

           ZeroMemory(ReadBuff, sizeof (ReadBuff));

           pReadingPoint = ReadBuff;

           ResRecv = recv(Socket, pReadingPoint, UnitReadSize, 0);

           if (SOCKET_ERROR == ResRecv)

           {

              *ErrorCode = WSAGetLastError();

 

              if (WSAECONNRESET == *ErrorCode || WSAECONNABORTED == *ErrorCode || WSAESHUTDOWN == *ErrorCode || WSAENETRESET == *ErrorCode)

              {

                  bError = 1;

                  ReturnCode = RET_SOCKET_SHUT_DOWN;

                  goto ErrHand;

              }

              else if (WSAETIMEDOUT == *ErrorCode)

              {

                  bError = 1;

                  ReturnCode = RET_TIMEOUT;

                  goto ErrHand;

              }

 

              bError = 1;

              ReturnCode = RET_FAIL;

              goto ErrHand;

           }

           else if (0 == ResRecv) // socket closed.

           {

              bError = 1;

              ReturnCode = RET_SOCKET_CLOSED;

              goto ErrHand;

           }

           else

           {

              if (0 == LoopCount)

              {

                  memcpy(Buffer + CopyingSize, ReadBuff + sizeof (LenInfo), ResRecv - sizeof (LenInfo));

                  CopyingSize += ResRecv - sizeof (LenInfo);

              }

              else

              {

                  memcpy(Buffer + CopyingSize, ReadBuff, ResRecv);

                  CopyingSize += ResRecv;

              }

              LoopCount++;

              ToReadSize -= ResRecv;

 

              if (0 >= ToReadSize)

              {

                  break;

              }

           }

       }

 

       bError = 0;

       ReturnCode = CopyingSize;

       goto ErrHand;

    }

 

ErrHand:

    if (-1 != TimeoutOld)

    {

       Timeout = TimeoutOld;

       if (SOCKET_ERROR == setsockopt(Socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&Timeout, sizeof (Timeout)))

       {

           *ErrorCode = WSAGetLastError();

           bError = 1;

           ReturnCode = RET_FAIL;

           goto ErrHand;

       }

    }

 

    return ReturnCode;

} // TCPRead()

 

int TCPWrite(SOCKET Socket, char *Buffer, DWORD BufferSize, DWORD TimeoutMilli, DWORD *ErrorCode)

{

    BOOL bError;

    int ReturnCode;

    DWORD WritingSize;

    int ResSend;

    int Timeout;

    int TimeoutOld;

    int OutLen;

    int ToSendSize;

    int ToSendSizeUnit;

    int RemainedUnit;

    int Loop;

    int SendingNumber;

    char *pIndex;

    char WriteBuf[WRITE_UNIT_SIZE];

    char *pIndexUnit;

 

    bError = 0;

    ReturnCode = RET_FAIL;

    TimeoutOld = -1;

 

    if (0 > Socket || 0 == Buffer || 0 == BufferSize || 0 == ErrorCode)

    {

       bError = 1;

       ReturnCode = RET_FAIL;

       goto ErrHand;

    }

 

    *ErrorCode = 0;

 

    OutLen = sizeof (TimeoutOld);

    if (SOCKET_ERROR == getsockopt(Socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&TimeoutOld, &OutLen))

    {

       *ErrorCode = WSAGetLastError();

       bError = 1;

       ReturnCode = RET_FAIL;

       goto ErrHand;

    }

 

    Timeout = TimeoutMilli; // Assigning is due to input the address of the Timeout. 0 = infinite.

    if (SOCKET_ERROR == setsockopt(Socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&Timeout, sizeof (Timeout)))

    {

       *ErrorCode = WSAGetLastError();

       bError = 1;

       ReturnCode = RET_FAIL;

       goto ErrHand;

    }

 

    SendingNumber = ((BufferSize + sizeof (BufferSize)) / WRITE_UNIT_SIZE) + ((0 != ((BufferSize + sizeof (BufferSize)) % WRITE_UNIT_SIZE)) ? 1 : 0);

    WritingSize = 0;

    pIndex = Buffer;

    ToSendSize = BufferSize;

    for (Loop = 0; Loop < SendingNumber; Loop++)

    {

       if (0 == Loop)

       {

           ZeroMemory(WriteBuf, sizeof (WriteBuf));

           ToSendSizeUnit = ((WRITE_UNIT_SIZE - sizeof (BufferSize)) > ToSendSize) ? ToSendSize : WRITE_UNIT_SIZE - sizeof (BufferSize);

           memcpy(WriteBuf, &BufferSize, sizeof (BufferSize));

           memcpy(WriteBuf + sizeof (BufferSize), pIndex, ToSendSizeUnit);

           pIndex += ToSendSizeUnit;

           ToSendSize -= ToSendSizeUnit;

           ToSendSizeUnit += sizeof (BufferSize); // compensation.

       }

       else

        {

           ZeroMemory(WriteBuf, sizeof (WriteBuf));

           ToSendSizeUnit = (WRITE_UNIT_SIZE > ToSendSize) ? ToSendSize : WRITE_UNIT_SIZE;

           memcpy(WriteBuf, pIndex, ToSendSizeUnit);

           pIndex += ToSendSizeUnit;

           ToSendSize -= ToSendSizeUnit;

       }

 

       RemainedUnit = ToSendSizeUnit;

       pIndexUnit = WriteBuf;

       while (0 < RemainedUnit)

       {

           ResSend = send(Socket, pIndexUnit, RemainedUnit, 0); // send() operates as all or nothing.

           if (SOCKET_ERROR == ResSend)

           {

              *ErrorCode = WSAGetLastError();

             

              if (WSAECONNRESET == *ErrorCode || WSAECONNABORTED == *ErrorCode || WSAESHUTDOWN == *ErrorCode || WSAENETRESET == *ErrorCode)

              {

                  bError = 1;

                  ReturnCode = RET_SOCKET_SHUT_DOWN;

                  goto ErrHand;

              }

              else if (WSAETIMEDOUT == *ErrorCode)

              {

                  bError = 1;

                  ReturnCode = RET_TIMEOUT;

                  goto ErrHand;

              }

             

              bError = 1;

              ReturnCode = RET_FAIL;

              goto ErrHand;

           }

           else if (0 == ResSend) // socket closed.

           {

              bError = 1;

              ReturnCode = RET_SOCKET_CLOSED;

              goto ErrHand;

           }

           else if (ResSend == RemainedUnit) // the normal case.

           {

              WritingSize += ResSend;

              RemainedUnit -= ResSend;

           }

           else if (ResSend > RemainedUnit)

           {

              bError = 1;

              ReturnCode = RET_FAIL;

              goto ErrHand;

           }

           else if (ResSend < RemainedUnit)

           {

              WritingSize += ResSend;

              RemainedUnit -= ResSend;

              pIndexUnit += ResSend;

           }

           else // what? error.

           {

              bError = 1;

              ReturnCode = RET_FAIL;

              goto ErrHand;

           }

       }

    }

 

ErrHand:

    if (-1 != TimeoutOld)

    {

       Timeout = TimeoutOld;

       if (SOCKET_ERROR == setsockopt(Socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&Timeout, sizeof (Timeout)))

       {

           *ErrorCode = WSAGetLastError();

           bError = 1;

           ReturnCode = RET_FAIL;

       }

    }

 

    if (1 == bError)

    {

       return ReturnCode;

    }

 

    ReturnCode = WritingSize - sizeof (BufferSize);

    return ReturnCode;

} // TCPWrite()