依照慣例,先上代碼:
- #pragma once
- #ifdef WIN32
- #include <windows.h>
- #include <WinSock.h>
- #else
- #include <sys/socket.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #define SOCKET int
- #define SOCKET_ERROR -1
- #define INVALID_SOCKET -1
- #endif
- #ifndef CHECKF
- #define CHECKF(x) \
- do \
- { \
- if (!(x)) { \
- log_msg("CHECKF", #x, __FILE__, __LINE__); \
- return 0; \
- } \
- } while (0)
- #endif
- #define _MAX_MSGSIZE 16 * 1024 // 暫定一個消息最大為16k
- #define BLOCKSECONDS 30 // INIT函數阻塞時間
- #define INBUFSIZE (64*1024) //? 具體尺寸根據剖面報告調整 接收數據的緩存
- #define OUTBUFSIZE (8*1024) //? 具體尺寸根據剖面報告調整。 發送數據的緩存,當不超過8K時,FLUSH只需要SEND一次
- class CGameSocket {
- public:
- CGameSocket(void);
- bool Create(constchar* pszServerIP, int nServerPort, int nBlockSec = BLOCKSECONDS, bool bKeepAlive = false);
- bool SendMsg(void* pBuf, int nSize);
- bool ReceiveMsg(void* pBuf, int& nSize);
- bool Flush(void);
- bool Check(void);
- void Destroy(void);
- SOCKET GetSocket(void) const { return m_sockClient; }
- private:
- bool recvFromSock(void); // 從網絡中讀取盡可能多的數據
- bool hasError(); // 是否發生錯誤,注意,異步模式未完成非錯誤
- void closeSocket();
- SOCKET m_sockClient;
- // 發送數據緩沖
- char m_bufOutput[OUTBUFSIZE]; //? 可優化為指針數組
- int m_nOutbufLen;
- // 環形緩沖區
- char m_bufInput[INBUFSIZE];
- int m_nInbufLen;
- int m_nInbufStart; // INBUF使用循環式隊列,該變量為隊列起點,0 - (SIZE-1)
- };
#pragma once
#ifdef WIN32
#include <windows.h>
#include <WinSock.h>
#else
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SOCKET int
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#endif
#ifndef CHECKF
#define CHECKF(x) \
do \
{ \
if (!(x)) { \
log_msg("CHECKF", #x, __FILE__, __LINE__); \
return 0; \
} \
} while (0)
#endif
#define _MAX_MSGSIZE 16 * 1024 // 暫定一個消息最大為16k
#define BLOCKSECONDS 30 // INIT函數阻塞時間
#define INBUFSIZE (64*1024) //? 具體尺寸根據剖面報告調整 接收數據的緩存
#define OUTBUFSIZE (8*1024) //? 具體尺寸根據剖面報告調整。 發送數據的緩存,當不超過8K時,FLUSH只需要SEND一次
class CGameSocket {
public:
CGameSocket(void);
bool Create(const char* pszServerIP, int nServerPort, int nBlockSec = BLOCKSECONDS, bool bKeepAlive = false);
bool SendMsg(void* pBuf, int nSize);
bool ReceiveMsg(void* pBuf, int& nSize);
bool Flush(void);
bool Check(void);
void Destroy(void);
SOCKET GetSocket(void) const { return m_sockClient; }
private:
bool recvFromSock(void); // 從網絡中讀取盡可能多的數據
bool hasError(); // 是否發生錯誤,注意,異步模式未完成非錯誤
void closeSocket();
SOCKET m_sockClient;
// 發送數據緩沖
char m_bufOutput[OUTBUFSIZE]; //? 可優化為指針數組
int m_nOutbufLen;
// 環形緩沖區
char m_bufInput[INBUFSIZE];
int m_nInbufLen;
int m_nInbufStart; // INBUF使用循環式隊列,該變量為隊列起點,0 - (SIZE-1)
};
- #include "stdafx.h"
- #include "Socket.h"
- CGameSocket::CGameSocket()
- {
- // 初始化
- memset(m_bufOutput, 0, sizeof(m_bufOutput));
- memset(m_bufInput, 0, sizeof(m_bufInput));
- }
- void CGameSocket::closeSocket()
- {
- #ifdef WIN32
- closesocket(m_sockClient);
- WSACleanup();
- #else
- close(m_sockClient);
- #endif
- }
- bool CGameSocket::Create(constchar* pszServerIP, int nServerPort, int nBlockSec, bool bKeepAlive /*= FALSE*/)
- {
- // 檢查參數
- if(pszServerIP == 0 || strlen(pszServerIP) > 15) {
- returnfalse;
- }
- #ifdef WIN32
- WSADATA wsaData;
- WORD version = MAKEWORD(2, 0);
- int ret = WSAStartup(version, &wsaData);//win sock start up
- if (ret != 0) {
- returnfalse;
- }
- #endif
- // 創建主套接字
- m_sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if(m_sockClient == INVALID_SOCKET) {
- closeSocket();
- returnfalse;
- }
- // 設置SOCKET為KEEPALIVE
- if(bKeepAlive)
- {
- int optval=1;
- if(setsockopt(m_sockClient, SOL_SOCKET, SO_KEEPALIVE, (char *) &optval, sizeof(optval)))
- {
- closeSocket();
- returnfalse;
- }
- }
- #ifdef WIN32
- DWORD nMode = 1;
- int nRes = ioctlsocket(m_sockClient, FIONBIO, &nMode);
- if (nRes == SOCKET_ERROR) {
- closeSocket();
- returnfalse;
- }
- #else
- // 設置為非阻塞方式
- fcntl(m_sockClient, F_SETFL, O_NONBLOCK);
- #endif
- unsigned long serveraddr = inet_addr(pszServerIP);
- if(serveraddr == INADDR_NONE) // 檢查IP地址格式錯誤
- {
- closeSocket();
- returnfalse;
- }
- sockaddr_in addr_in;
- memset((void *)&addr_in, 0, sizeof(addr_in));
- addr_in.sin_family = AF_INET;
- addr_in.sin_port = htons(nServerPort);
- addr_in.sin_addr.s_addr = serveraddr;
- if(connect(m_sockClient, (sockaddr *)&addr_in, sizeof(addr_in)) == SOCKET_ERROR) {
- if (hasError()) {
- closeSocket();
- returnfalse;
- }
- else // WSAWOLDBLOCK
- {
- timeval timeout;
- timeout.tv_sec = nBlockSec;
- timeout.tv_usec = 0;
- fd_set writeset, exceptset;
- FD_ZERO(&writeset);
- FD_ZERO(&exceptset);
- FD_SET(m_sockClient, &writeset);
- FD_SET(m_sockClient, &exceptset);
- int ret = select(FD_SETSIZE, NULL, &writeset, &exceptset, &timeout);
- if (ret == 0 || ret < 0) {
- closeSocket();
- returnfalse;
- } else // ret > 0
- {
- ret = FD_ISSET(m_sockClient, &exceptset);
- if(ret) // or (!FD_ISSET(m_sockClient, &writeset)
- {
- closeSocket();
- returnfalse;
- }
- }
- }
- }
- m_nInbufLen = 0;
- m_nInbufStart = 0;
- m_nOutbufLen = 0;
- struct linger so_linger;
- so_linger.l_onoff = 1;
- so_linger.l_linger = 500;
- setsockopt(m_sockClient, SOL_SOCKET, SO_LINGER, (constchar*)&so_linger, sizeof(so_linger));
- returntrue;
- }
- bool CGameSocket::SendMsg(void* pBuf, int nSize)
- {
- if(pBuf == 0 || nSize <= 0) {
- returnfalse;
- }
- if (m_sockClient == INVALID_SOCKET) {
- returnfalse;
- }
- // 檢查通訊消息包長度
- int packsize = 0;
- packsize = nSize;
- // 檢測BUF溢出
- if(m_nOutbufLen + nSize > OUTBUFSIZE) {
- // 立即發送OUTBUF中的數據,以清空OUTBUF。
- Flush();
- if(m_nOutbufLen + nSize > OUTBUFSIZE) {
- // 出錯了
- Destroy();
- returnfalse;
- }
- }
- // 數據添加到BUF尾
- memcpy(m_bufOutput + m_nOutbufLen, pBuf, nSize);
- m_nOutbufLen += nSize;
- returntrue;
- }
- bool CGameSocket::ReceiveMsg(void* pBuf, int& nSize)
- {
- //檢查參數
- if(pBuf == NULL || nSize <= 0) {
- returnfalse;
- }
- if (m_sockClient == INVALID_SOCKET) {
- returnfalse;
- }
- // 檢查是否有一個消息(小於2則無法獲取到消息長度)
- if(m_nInbufLen < 2) {
- // 如果沒有請求成功 或者 如果沒有數據則直接返回
- if(!recvFromSock() || m_nInbufLen < 2) { // 這個m_nInbufLen更新了
- returnfalse;
- }
- }
- // 計算要拷貝的消息的大小(一個消息,大小為整個消息的第一個16字節),因為環形緩沖區,所以要分開計算
- int packsize = (unsigned char)m_bufInput[m_nInbufStart] +
- (unsigned char)m_bufInput[(m_nInbufStart + 1) % INBUFSIZE] * 256; // 注意字節序,高位+低位
- // 檢測消息包尺寸錯誤 暫定最大16k
- if (packsize <= 0 || packsize > _MAX_MSGSIZE) {
- m_nInbufLen = 0; // 直接清空INBUF
- m_nInbufStart = 0;
- returnfalse;
- }
- // 檢查消息是否完整(如果將要拷貝的消息大於此時緩沖區數據長度,需要再次請求接收剩余數據)
- if (packsize > m_nInbufLen) {
- // 如果沒有請求成功 或者 依然無法獲取到完整的數據包 則返回,直到取得完整包
- if (!recvFromSock() || packsize > m_nInbufLen) { // 這個m_nInbufLen已更新
- returnfalse;
- }
- }
- // 復制出一個消息
- if(m_nInbufStart + packsize > INBUFSIZE) {
- // 如果一個消息有回卷(被拆成兩份在環形緩沖區的頭尾)
- // 先拷貝環形緩沖區末尾的數據
- int copylen = INBUFSIZE - m_nInbufStart;
- memcpy(pBuf, m_bufInput + m_nInbufStart, copylen);
- // 再拷貝環形緩沖區頭部的剩余部分
- memcpy((unsigned char *)pBuf + copylen, m_bufInput, packsize - copylen);
- nSize = packsize;
- } else {
- // 消息沒有回卷,可以一次拷貝出去
- memcpy(pBuf, m_bufInput + m_nInbufStart, packsize);
- nSize = packsize;
- }
- // 重新計算環形緩沖區頭部位置
- m_nInbufStart = (m_nInbufStart + packsize) % INBUFSIZE;
- m_nInbufLen -= packsize;
- return true;
- }
- bool CGameSocket::hasError()
- {
- #ifdef WIN32
- int err = WSAGetLastError();
- if(err != WSAEWOULDBLOCK) {
- #else
- int err = errno;
- if(err != EINPROGRESS && err != EAGAIN) {
- #endif
- returntrue;
- }
- returnfalse;
- }
- // 從網絡中讀取盡可能多的數據,實際向服務器請求數據的地方
- bool CGameSocket::recvFromSock(void)
- {
- if (m_nInbufLen >= INBUFSIZE || m_sockClient == INVALID_SOCKET) {
- returnfalse;
- }
- // 接收第一段數據
- int savelen, savepos; // 數據要保存的長度和位置
- if(m_nInbufStart + m_nInbufLen < INBUFSIZE) { // INBUF中的剩余空間有回繞
- savelen = INBUFSIZE - (m_nInbufStart + m_nInbufLen); // 后部空間長度,最大接收數據的長度
- } else {
- savelen = INBUFSIZE - m_nInbufLen;
- }
- // 緩沖區數據的末尾
- savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;
- CHECKF(savepos + savelen <= INBUFSIZE);
- int inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);
- if(inlen > 0) {
- // 有接收到數據
- m_nInbufLen += inlen;
- if (m_nInbufLen > INBUFSIZE) {
- returnfalse;
- }
- // 接收第二段數據(一次接收沒有完成,接收第二段數據)
- if(inlen == savelen && m_nInbufLen < INBUFSIZE) {
- int savelen = INBUFSIZE - m_nInbufLen;
- int savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;
- CHECKF(savepos + savelen <= INBUFSIZE);
- inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);
- if(inlen > 0) {
- m_nInbufLen += inlen;
- if (m_nInbufLen > INBUFSIZE) {
- returnfalse;
- }
- } elseif(inlen == 0) {
- Destroy();
- returnfalse;
- } else {
- // 連接已斷開或者錯誤(包括阻塞)
- if (hasError()) {
- Destroy();
- returnfalse;
- }
- }
- }
- } elseif(inlen == 0) {
- Destroy();
- returnfalse;
- } else {
- // 連接已斷開或者錯誤(包括阻塞)
- if (hasError()) {
- Destroy();
- returnfalse;
- }
- }
- returntrue;
- }
- bool CGameSocket::Flush(void) //? 如果 OUTBUF > SENDBUF 則需要多次SEND()
- {
- if (m_sockClient == INVALID_SOCKET) {
- returnfalse;
- }
- if(m_nOutbufLen <= 0) {
- returntrue;
- }
- // 發送一段數據
- int outsize;
- outsize = send(m_sockClient, m_bufOutput, m_nOutbufLen, 0);
- if(outsize > 0) {
- // 刪除已發送的部分
- if(m_nOutbufLen - outsize > 0) {
- memcpy(m_bufOutput, m_bufOutput + outsize, m_nOutbufLen - outsize);
- }
- m_nOutbufLen -= outsize;
- if (m_nOutbufLen < 0) {
- returnfalse;
- }
- } else {
- if (hasError()) {
- Destroy();
- returnfalse;
- }
- }
- returntrue;
- }
- bool CGameSocket::Check(void)
- {
- // 檢查狀態
- if (m_sockClient == INVALID_SOCKET) {
- returnfalse;
- }
- char buf[1];
- int ret = recv(m_sockClient, buf, 1, MSG_PEEK);
- if(ret == 0) {
- Destroy();
- returnfalse;
- } elseif(ret < 0) {
- if (hasError()) {
- Destroy();
- returnfalse;
- } else { // 阻塞
- returntrue;
- }
- } else { // 有數據
- returntrue;
- }
- returntrue;
- }
- void CGameSocket::Destroy(void)
- {
- // 關閉
- struct linger so_linger;
- so_linger.l_onoff = 1;
- so_linger.l_linger = 500;
- int ret = setsockopt(m_sockClient, SOL_SOCKET, SO_LINGER, (constchar*)&so_linger, sizeof(so_linger));
- closeSocket();
- m_sockClient = INVALID_SOCKET;
- m_nInbufLen = 0;
- m_nInbufStart = 0;
- m_nOutbufLen = 0;
- memset(m_bufOutput, 0, sizeof(m_bufOutput));
- memset(m_bufInput, 0, sizeof(m_bufInput));
- }
#include "stdafx.h"
#include "Socket.h"
CGameSocket::CGameSocket()
{
// 初始化
memset(m_bufOutput, 0, sizeof(m_bufOutput));
memset(m_bufInput, 0, sizeof(m_bufInput));
}
void CGameSocket::closeSocket()
{
#ifdef WIN32
closesocket(m_sockClient);
WSACleanup();
#else
close(m_sockClient);
#endif
}
bool CGameSocket::Create(const char* pszServerIP, int nServerPort, int nBlockSec, bool bKeepAlive /*= FALSE*/)
{
// 檢查參數
if(pszServerIP == 0 || strlen(pszServerIP) > 15) {
return false;
}
#ifdef WIN32
WSADATA wsaData;
WORD version = MAKEWORD(2, 0);
int ret = WSAStartup(version, &wsaData);//win sock start up
if (ret != 0) {
return false;
}
#endif
// 創建主套接字
m_sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(m_sockClient == INVALID_SOCKET) {
closeSocket();
return false;
}
// 設置SOCKET為KEEPALIVE
if(bKeepAlive)
{
int optval=1;
if(setsockopt(m_sockClient, SOL_SOCKET, SO_KEEPALIVE, (char *) &optval, sizeof(optval)))
{
closeSocket();
return false;
}
}
#ifdef WIN32
DWORD nMode = 1;
int nRes = ioctlsocket(m_sockClient, FIONBIO, &nMode);
if (nRes == SOCKET_ERROR) {
closeSocket();
return false;
}
#else
// 設置為非阻塞方式
fcntl(m_sockClient, F_SETFL, O_NONBLOCK);
#endif
unsigned long serveraddr = inet_addr(pszServerIP);
if(serveraddr == INADDR_NONE) // 檢查IP地址格式錯誤
{
closeSocket();
return false;
}
sockaddr_in addr_in;
memset((void *)&addr_in, 0, sizeof(addr_in));
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(nServerPort);
addr_in.sin_addr.s_addr = serveraddr;
if(connect(m_sockClient, (sockaddr *)&addr_in, sizeof(addr_in)) == SOCKET_ERROR) {
if (hasError()) {
closeSocket();
return false;
}
else // WSAWOLDBLOCK
{
timeval timeout;
timeout.tv_sec = nBlockSec;
timeout.tv_usec = 0;
fd_set writeset, exceptset;
FD_ZERO(&writeset);
FD_ZERO(&exceptset);
FD_SET(m_sockClient, &writeset);
FD_SET(m_sockClient, &exceptset);
int ret = select(FD_SETSIZE, NULL, &writeset, &exceptset, &timeout);
if (ret == 0 || ret < 0) {
closeSocket();
return false;
} else // ret > 0
{
ret = FD_ISSET(m_sockClient, &exceptset);
if(ret) // or (!FD_ISSET(m_sockClient, &writeset)
{
closeSocket();
return false;
}
}
}
}
m_nInbufLen = 0;
m_nInbufStart = 0;
m_nOutbufLen = 0;
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 500;
setsockopt(m_sockClient, SOL_SOCKET, SO_LINGER, (const char*)&so_linger, sizeof(so_linger));
return true;
}
bool CGameSocket::SendMsg(void* pBuf, int nSize)
{
if(pBuf == 0 || nSize <= 0) {
return false;
}
if (m_sockClient == INVALID_SOCKET) {
return false;
}
// 檢查通訊消息包長度
int packsize = 0;
packsize = nSize;
// 檢測BUF溢出
if(m_nOutbufLen + nSize > OUTBUFSIZE) {
// 立即發送OUTBUF中的數據,以清空OUTBUF。
Flush();
if(m_nOutbufLen + nSize > OUTBUFSIZE) {
// 出錯了
Destroy();
return false;
}
}
// 數據添加到BUF尾
memcpy(m_bufOutput + m_nOutbufLen, pBuf, nSize);
m_nOutbufLen += nSize;
return true;
}
bool CGameSocket::ReceiveMsg(void* pBuf, int& nSize)
{
//檢查參數
if(pBuf == NULL || nSize <= 0) {
return false;
}
if (m_sockClient == INVALID_SOCKET) {
return false;
}
// 檢查是否有一個消息(小於2則無法獲取到消息長度)
if(m_nInbufLen < 2) {
// 如果沒有請求成功 或者 如果沒有數據則直接返回
if(!recvFromSock() || m_nInbufLen < 2) { // 這個m_nInbufLen更新了
return false;
}
}
// 計算要拷貝的消息的大小(一個消息,大小為整個消息的第一個16字節),因為環形緩沖區,所以要分開計算
int packsize = (unsigned char)m_bufInput[m_nInbufStart] +
(unsigned char)m_bufInput[(m_nInbufStart + 1) % INBUFSIZE] * 256; // 注意字節序,高位+低位
// 檢測消息包尺寸錯誤 暫定最大16k
if (packsize <= 0 || packsize > _MAX_MSGSIZE) {
m_nInbufLen = 0; // 直接清空INBUF
m_nInbufStart = 0;
return false;
}
// 檢查消息是否完整(如果將要拷貝的消息大於此時緩沖區數據長度,需要再次請求接收剩余數據)
if (packsize > m_nInbufLen) {
// 如果沒有請求成功 或者 依然無法獲取到完整的數據包 則返回,直到取得完整包
if (!recvFromSock() || packsize > m_nInbufLen) { // 這個m_nInbufLen已更新
return false;
}
}
// 復制出一個消息
if(m_nInbufStart + packsize > INBUFSIZE) {
// 如果一個消息有回卷(被拆成兩份在環形緩沖區的頭尾)
// 先拷貝環形緩沖區末尾的數據
int copylen = INBUFSIZE - m_nInbufStart;
memcpy(pBuf, m_bufInput + m_nInbufStart, copylen);
// 再拷貝環形緩沖區頭部的剩余部分
memcpy((unsigned char *)pBuf + copylen, m_bufInput, packsize - copylen);
nSize = packsize;
} else {
// 消息沒有回卷,可以一次拷貝出去
memcpy(pBuf, m_bufInput + m_nInbufStart, packsize);
nSize = packsize;
}
// 重新計算環形緩沖區頭部位置
m_nInbufStart = (m_nInbufStart + packsize) % INBUFSIZE;
m_nInbufLen -= packsize;
return true;
}
bool CGameSocket::hasError()
{
#ifdef WIN32
int err = WSAGetLastError();
if(err != WSAEWOULDBLOCK) {
#else
int err = errno;
if(err != EINPROGRESS && err != EAGAIN) {
#endif
return true;
}
return false;
}
// 從網絡中讀取盡可能多的數據,實際向服務器請求數據的地方
bool CGameSocket::recvFromSock(void)
{
if (m_nInbufLen >= INBUFSIZE || m_sockClient == INVALID_SOCKET) {
return false;
}
// 接收第一段數據
int savelen, savepos; // 數據要保存的長度和位置
if(m_nInbufStart + m_nInbufLen < INBUFSIZE) { // INBUF中的剩余空間有回繞
savelen = INBUFSIZE - (m_nInbufStart + m_nInbufLen); // 后部空間長度,最大接收數據的長度
} else {
savelen = INBUFSIZE - m_nInbufLen;
}
// 緩沖區數據的末尾
savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;
CHECKF(savepos + savelen <= INBUFSIZE);
int inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);
if(inlen > 0) {
// 有接收到數據
m_nInbufLen += inlen;
if (m_nInbufLen > INBUFSIZE) {
return false;
}
// 接收第二段數據(一次接收沒有完成,接收第二段數據)
if(inlen == savelen && m_nInbufLen < INBUFSIZE) {
int savelen = INBUFSIZE - m_nInbufLen;
int savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;
CHECKF(savepos + savelen <= INBUFSIZE);
inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);
if(inlen > 0) {
m_nInbufLen += inlen;
if (m_nInbufLen > INBUFSIZE) {
return false;
}
} else if(inlen == 0) {
Destroy();
return false;
} else {
// 連接已斷開或者錯誤(包括阻塞)
if (hasError()) {
Destroy();
return false;
}
}
}
} else if(inlen == 0) {
Destroy();
return false;
} else {
// 連接已斷開或者錯誤(包括阻塞)
if (hasError()) {
Destroy();
return false;
}
}
return true;
}
bool CGameSocket::Flush(void) //? 如果 OUTBUF > SENDBUF 則需要多次SEND()
{
if (m_sockClient == INVALID_SOCKET) {
return false;
}
if(m_nOutbufLen <= 0) {
return true;
}
// 發送一段數據
int outsize;
outsize = send(m_sockClient, m_bufOutput, m_nOutbufLen, 0);
if(outsize > 0) {
// 刪除已發送的部分
if(m_nOutbufLen - outsize > 0) {
memcpy(m_bufOutput, m_bufOutput + outsize, m_nOutbufLen - outsize);
}
m_nOutbufLen -= outsize;
if (m_nOutbufLen < 0) {
return false;
}
} else {
if (hasError()) {
Destroy();
return false;
}
}
return true;
}
bool CGameSocket::Check(void)
{
// 檢查狀態
if (m_sockClient == INVALID_SOCKET) {
return false;
}
char buf[1];
int ret = recv(m_sockClient, buf, 1, MSG_PEEK);
if(ret == 0) {
Destroy();
return false;
} else if(ret < 0) {
if (hasError()) {
Destroy();
return false;
} else { // 阻塞
return true;
}
} else { // 有數據
return true;
}
return true;
}
void CGameSocket::Destroy(void)
{
// 關閉
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 500;
int ret = setsockopt(m_sockClient, SOL_SOCKET, SO_LINGER, (const char*)&so_linger, sizeof(so_linger));
closeSocket();
m_sockClient = INVALID_SOCKET;
m_nInbufLen = 0;
m_nInbufStart = 0;
m_nOutbufLen = 0;
memset(m_bufOutput, 0, sizeof(m_bufOutput));
memset(m_bufInput, 0, sizeof(m_bufInput));
}
- // 發送消息
- bSucSend = m_pSocket->SendMsg(buf, nLen);
- // 接收消息處理(放到游戲主循環中,每幀處理)
- if (!m_pSocket) {
- return;
- }
- if (!m_pSocket->Check()) {
- m_pSocket = NULL;
- // 掉線了
- onConnectionAbort();
- return;
- }
- // 發送數據(向服務器發送消息)
- m_pSocket->Flush();
- // 接收數據(取得緩沖區中的所有消息,直到緩沖區為空)
- while (true)
- {
- char buffer[_MAX_MSGSIZE] = { 0 };
- int nSize = sizeof(buffer);
- char* pbufMsg = buffer;
- if(m_pSocket == NULL)
- {
- break;
- }
- if (!m_pSocket->ReceiveMsg(pbufMsg, nSize)) {
- break;
- }
- while (true)
- {
- MsgHead* pReceiveMsg = (MsgHead*)(pbufMsg);
- uint16 dwCurMsgSize = pReceiveMsg->usSize;
- // CCLOG("msgsize: %d", dwCurMsgSize);
- if((int)dwCurMsgSize > nSize || dwCurMsgSize <= 0) { // broken msg
- break;
- }
- CMessageSubject::instance().OnMessage((constchar*)pReceiveMsg, pReceiveMsg->usSize);
- pbufMsg += dwCurMsgSize;
- nSize -= dwCurMsgSize;
- if(nSize <= 0) {
- break;
- }
- }
- }
// 發送消息
bSucSend = m_pSocket->SendMsg(buf, nLen);
// 接收消息處理(放到游戲主循環中,每幀處理)
if (!m_pSocket) {
return;
}
if (!m_pSocket->Check()) {
m_pSocket = NULL;
// 掉線了
onConnectionAbort();
return;
}
// 發送數據(向服務器發送消息)
m_pSocket->Flush();
// 接收數據(取得緩沖區中的所有消息,直到緩沖區為空)
while (true)
{
char buffer[_MAX_MSGSIZE] = { 0 };
int nSize = sizeof(buffer);
char* pbufMsg = buffer;
if(m_pSocket == NULL)
{
break;
}
if (!m_pSocket->ReceiveMsg(pbufMsg, nSize)) {
break;
}
while (true)
{
MsgHead* pReceiveMsg = (MsgHead*)(pbufMsg);
uint16 dwCurMsgSize = pReceiveMsg->usSize;
// CCLOG("msgsize: %d", dwCurMsgSize);
if((int)dwCurMsgSize > nSize || dwCurMsgSize <= 0) { // broken msg
break;
}
CMessageSubject::instance().OnMessage((const char*)pReceiveMsg, pReceiveMsg->usSize);
pbufMsg += dwCurMsgSize;
nSize -= dwCurMsgSize;
if(nSize <= 0) {
break;
}
}
}
這樣的一個Socket封裝,適用於windows mac ios android等平台, Socket處理是異步非阻塞的,所以可以放心的放到主線程處理消息, 最大支持64k的接收消息緩沖(一般一個消息不可能大於3k)。
這里展示這個,目的並不是說這個封裝有多么優異,多么高科技,多么牛x。 恰恰是想表達它的簡單。 這個簡單的封裝完全可以勝任一個mmo客戶端的消息底層(注意是客戶端,服務器對消息底層的性能要求要遠遠大於客戶端),甚至是魔獸世界這類的大型mmo都可以用這么一個小的封裝來做消息底層。
對於游戲客戶端消息底層的要求非常簡單,根本不需要boost::asio什么的開源庫。
1、非阻塞模型,這樣我才放心把消息處理放到主線程,多線程處理消息其實很浪費。不知道得多大型的mmo才會用到。
2、消息接收緩存處理,避免大消息被截掉。
3、沒了,剩下的一些特殊處理應該是上層邏輯來考慮的。比如掉線重連等。
