内存对齐
#pragma pack(push,1)
VC中提供了#pragma pack(n)来设定变量以n字节对齐方式。如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。
字符数组清0
char name[12] = {0};
或
memset(name, 0, 12);
若不清零,字符数组内为随机值,可能导致字符串编码问题,一定要先清零
标准字符数组使用方法
#define CHAR_LEN 12
char name[CHAR_LEN + 1];
memset(name, 0x00, sizeof(name));
strncmp(name, "xxxx", CHAR_LEN) == 0
strncpy(name, "xxxx", CHAR_LEN);
name[CHAR_LEN] = 0;
字符串比较(不区分大小写)
stricmp
屏蔽编译警告
# pragma warning(disable:4267)
// 'var' : conversion from 'size_t' to 'type', possible loss of data
时间枚举
enum TimeConstants
{
MINUTE = 60,
HOUR = MINUTE*60,
DAY = HOUR*24,
MONTH = DAY*30,
YEAR = MONTH*12,
IN_MILISECONDS = 1000
};
OS定义
#define PLATFORM_WINDOWS 0
#define PLATFORM_UNIX 1
#define PLATFORM_APPLE 2
#define PLATFORM_INTEL 3
编译器定义
#define COMPILER_MICROSOFT 0
#define COMPILER_GNU 1
#define COMPILER_BORLAND 2
#define COMPILER_INTEL 3
LockedQueue 加锁队列
template <class T, class LockType, typename StorageType=std::deque<T> >
class LockedQueue
{
LockType _lock;
StorageType _queue;
volatile bool _canceled;
LockedQueue(): _canceled(false){ }
virtual ~LockedQueue(){ }
void add(const T& item)
bool next(T& result)
T& peek()
void cancel()
bool cancelled()
void lock()
void unlock()
};
通过在函数起始和结尾处添加lock(); 和 unlock(); 实现加锁和解锁,或者使用自动锁定模版 ACE_Guard<LockType> g(this->_lock);
创建对象模版类
template <class T>
class TRINITY_DLL_DECL OperatorNew
{
public:
static T* Create(void) { return (new T); }
static void Destroy(T *obj) { delete obj; }
};
单例模式Singleton
template
<
typename T,
class ThreadingModel = Trinity::SingleThreaded<T>,
class CreatePolicy = Trinity::OperatorNew<T>,
class LifeTimePolicy = Trinity::ObjectLifeTime<T>
>
class TRINITY_DLL_DECL Singleton
{
public:
static T& Instance();
protected:
Singleton() {};
private:
// Prohibited actions...this does not prevent hijacking.
Singleton(const Singleton &);
Singleton& operator=(const Singleton &);
// Singleton Helpers
static void DestroySingleton();
// data structure
typedef typename ThreadingModel::Lock Guard;
static T *si_instance;
static bool si_destroyed;
};
ThreadingModel负责加锁,CreatePolicy负责创建和释放对象,LifeTimePolicy负责生命周期。隐藏复制构造和拷贝构造函数
socket预定义
#ifdef _MSC_VER
#pragma comment(lib, "wsock32.lib")
#endif
typedef unsigned long ipaddr_t;
typedef unsigned short port_t;
typedef int socklen_t;
// 1.8.6: define FD_SETSIZE to something bigger than 64 if there are a lot of
// simultaneous connections (must be done before including winsock.h)
#define FD_SETSIZE 1024
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <ws2tcpip.h>
class WSAInitializer // Winsock Initializer
{
public:
WSAInitializer() {
if (WSAStartup(0x101,&m_wsadata))
{
exit(-1);
}
}
~WSAInitializer() {
WSACleanup();
}
private:
WSADATA m_wsadata;
};
typedef std::list<SOCKET> socket_v;
字符串分割
Tokens StrSplit(const std::string &src, const std::string &sep)
{
Tokens r;
std::string s;
for (std::string::const_iterator i = src.begin(); i != src.end(); i++)
{
if (sep.find(*i) != std::string::npos)
{
if (s.length()) r.push_back(s);
s = "";
}
else
{
s += *i;
}
}
if (s.length()) r.push_back(s);
return r;
}
typedef std::vector<std::string> Tokens;
src为拼接字符串,sep为分割标识符,可以有多个分割标志,结果存入Tokens中
字符串是否ip地址
bool IsIPAddress(char const* ipaddress)
{
if(!ipaddress)
return false;
// all valid ip address formats are recognized e.g.: 12.23,121234,0xABCD)
return inet_addr(ipaddress) != INADDR_NONE;
}
字符串对称交换
template<size_t T>
inline void convert(char *val)
{
std::swap(*val, *(val + T - 1));
convert<T - 2>(val + 1);
}
传入如”abcde”,处理后得到”edcba”。T为char*的长度,swap交换两个参数的值,convert递归调用
vector的reserve和resize
reserve增加了vector的capacity,但没有改变size;resize改变了vector的capacity和size。 reserve是容器预留空间,不真正创建元素,在添加对象之前,不能引用容器内元素(相当于一块未开发的土地),加入元素时要调用push_bak或insert函数。
clock 进程运行时间
clock_t __cdecl clock(void);
//返回进程已运行的cpu计时单元数,转换为秒数:
#define CLOCKS_PER_SEC 1000
clock()/CLOCKS_PER_SEC
signal 设置信号动作
void (* signal(int _SigNum, void (* _Func)(int)))(int);
void OnSignal(int s)
void HookSignals()
{
signal(SIGINT, OnSignal);
signal(SIGTERM, OnSignal);
}
void UnhookSignals()
{
signal(SIGINT, 0);
signal(SIGTERM, 0);
}
HookSignals绑定信号,UnhookSignals取消绑定
设置处理器数目
uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
if (Aff > 0)
{
ULONG_PTR appAff;
ULONG_PTR sysAff;
if (GetProcessAffinityMask(hProcess,&appAff,&sysAff))
{
ULONG_PTR curAff = Aff & appAff;
// remove non accessible processors
if (curAff)
SetProcessAffinityMask(hProcess,curAff);
}
}
select函数
select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一Socket或文件可读。
select(int nfds, fd_set * readfds,fd_set * writefds,fd_set *exceptfds,const struct timeval* timeout);
ndfs:监视的文件句柄数,一般设为监视的最大文件号加一。
readfds:监视的可读文件句柄集合。
writefds:select监视的可写文件句柄集合。
exceptfds:select监视的异常文件句柄集合。
timeout:本次select()的超时结束时间。
当readfds或writefds中映象的文件可读或可写或超时,本次select()就结束返回。程序员利用一组系统提供的宏在select()结束时便可判断哪一文件可读或可写。
struct timeval* timeout是select的超时时间,有两个成员,一个是秒数,另一个是毫秒数。它可以使select处于三种状态:
(1)若将NULL以形参传入,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
(2)若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
(3)timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
FD_ZERO(fd_set * fdset) 清空fdset与所有文件句柄的联系。
FD_SET(int fd,fd_set *fdset)建立文件句柄fd与fdset的联系。
FD_CLR(int fd,fd_set *fdset)清除文件句柄fd与fdset的联系。
FD_ISSET(int fd, fdset *fdset)检查fdset联系的文件句柄fd是否可读写,当>0表示可读写。
socket消息处理
SocketHandler h;
ListenSocket<AuthSocket> authListenSocket(h);
authListenSocket.Bind(bind_ip.c_str(),rmport);
h.Add(&authListenSocket);
while (!stopEvent)
{
h.Select(0, 100000);
}
SocketHandler类
class SocketHandler : public ISocketHandler
(1)记录日志
SocketHandler(StdLog *log = NULL); // log对象指针SocketHandler::SocketHandler(StdLog *p):m_stdlog(p)
void SocketHandler::LogError(Socket *p,const std::string& user_text,int err,const std::string& sys_err,loglevel_t t)
{
if (m_stdlog)
m_stdlog -> error(this, p, user_text, err, sys_err, t);
}
(2)socket map,存放socket句柄和对象指针
typedef std::map<SOCKET,Socket *> socket_m;
(3)线程加锁
SocketHandler(Mutex& mutex,StdLog *log = NULL);
SocketHandler::SocketHandler(Mutex& mutex,StdLog *p)
:m_stdlog(p)
,m_mutex(mutex)
{
m_mutex.Lock();
FD_ZERO(&m_rfds);
FD_ZERO(&m_wfds);
FD_ZERO(&m_efds);
}
SocketHandler::~SocketHandler()
{
if (m_b_use_mutex)
m_mutex.Unlock();
}
if (m_b_use_mutex)
{
m_mutex.Unlock();
n = select( (int)(m_maxsock + 1),&rfds,&wfds,&efds,tsel);
m_mutex.Lock();
}
构造函数里加锁,析构里解锁,每次执行函数时先解锁再加锁
(4)设置socket读写状态
void SocketHandler::Get(SOCKET s,bool& r,bool& w,bool& e)
{
if (s >= 0)
{
r = FD_ISSET(s, &m_rfds) ? true : false;
w = FD_ISSET(s, &m_wfds) ? true : false;
e = FD_ISSET(s, &m_efds) ? true : false;
}
}
void SocketHandler::Set(SOCKET s,bool bRead,bool bWrite,bool bException)
{
if (bRead)
{
if (!FD_ISSET(s, &m_rfds)) FD_SET(s, &m_rfds);
}
else
FD_CLR(s, &m_rfds);
if (bWrite)
{
if (!FD_ISSET(s, &m_wfds)) FD_SET(s, &m_wfds);
}
else
FD_CLR(s, &m_wfds);
if (bException)
{
if (!FD_ISSET(s, &m_efds)) FD_SET(s, &m_efds);
}
else
FD_CLR(s, &m_efds);
}
(5)select函数
/** Wait for events, generate callbacks. */
int Select(long sec,long usec);
/** This method will not return until an event has been detected. */
int Select();
int Select(struct timeval *tsel);
int SocketHandler::Select(long sec,long usec)
{
struct timeval tv;
tv.tv_sec = sec;
tv.tv_usec = usec;
return Select(&tv);
}
int SocketHandler::Select(struct timeval *tsel)
{
fd_set rfds = m_rfds;
fd_set wfds = m_wfds;
fd_set efds = m_efds;
int n;
n = select( (int)(m_maxsock + 1),&rfds,&wfds,&efds,tsel);
for (socket_v::iterator it2 = m_fds.begin(); it2 != m_fds.end() && n; it2++)
{
SOCKET i = *it2;
if (FD_ISSET(i, &rfds))
{
socket_m::iterator itmp = m_sockets.find(i);
if (itmp != m_sockets.end())
{
Socket *p = itmp -> second;
p -> OnRead();
}
n--;
}
if (FD_ISSET(i, &wfds))
{
socket_m::iterator itmp = m_sockets.find(i);
if (itmp != m_sockets.end())
{
Socket *p = itmp -> second;
p -> OnWrite();
}
n--;
}
if (FD_ISSET(i, &efds))
{
socket_m::iterator itmp = m_sockets.find(i);
if (itmp != m_sockets.end())
{
Socket *p = itmp -> second;
p -> OnException();
}
n--;
}
}
}
AuthSocket类
class AuthSocket: public TcpSocket
继承顺序为
AuthSocket – TcpSocket – StreamSocket – Socket
(1)构造
AuthSocket::AuthSocket(ISocketHandler &h) : TcpSocket(h)
(2)读取
CircularBuffer ibuf;
void AuthSocket::OnRead()
{
TcpSocket::OnRead();
while (1)
{
if (!ibuf.GetLength())
return;
uint8 _cmd;
ibuf.SoftRead((char *)&_cmd, 1);
size_t i;
for (i = 0; i < AUTH_TOTAL_COMMANDS; ++i)
{
if ((uint8)table[i].cmd == _cmd &&
(table[i].status == STATUS_CONNECTED ||
(_authed && table[i].status == STATUS_AUTHED)))
{
(*this.*table[i].handler)();
break;
}
}
}
}
(3)消息绑定函数
typedef struct AuthHandler
{
eAuthCmd cmd;
uint32 status;
bool (AuthSocket::*handler)(void);
}AuthHandler;
const AuthHandler table[] =
{
{
AUTH_LOGON_CHALLENGE,
STATUS_CONNECTED,
&AuthSocket::_HandleLogonChallenge
},
{
AUTH_LOGON_PROOF,
STATUS_CONNECTED,
&AuthSocket::_HandleLogonProof
}
// ...
};
(4)消息包
#pragma pack(push,1)
typedef struct AUTH_LOGON_PROOF_S
{
uint8 cmd;
uint8 error;
uint8 M2[20];
uint32 unk1;
uint32 unk2;
uint16 unk3;
} sAuthLogonProof_S;
#pragma pack(pop)
环形缓冲区CircularBuffer
char *buf; // 缓冲区数组
size_t m_max; // 最大长度
size_t m_q; // 读写起始位置间长度
size_t m_b; // 读取起始位置
size_t m_t; // 写入起始位置
unsigned long m_count; // 实际存储大小
m_b = 3 | 读位置 | m_q = 3 读写间长度 | 写位置 |
|
| ||||
|
|
| -> |
|
|
| -> |
|
|
m_count = 7 | m_t |
|
| ||||||
m_max = 10 |
(1)构造析构
CircularBuffer::CircularBuffer(size_t size)
: buf(new char[2 * size]), m_max(size), m_q(0)
, m_b(0), m_t(0), m_count(0)
{}
CircularBuffer::~CircularBuffer()
{
delete[] buf;
}
此处设置buf大小为2 * size,不理解,估计只使用size个大小,另一半闲置
(2)写数据
bool CircularBuffer::Write(const char *s,size_t wLen)
{
// 已存长度+ 新增长度> 最大长度,溢出
if (m_RWLen + wLen > m_max)
return false;
m_count += (unsigned long)wLen; // 实际长度增加
// 写位置+ 新增> 最大,跳回头部
if (m_W + wLen > m_max)
{
// 写位置到尾部的可写长度,先拷贝一部分
size_t w_End = m_max - m_W;
memcpy(buf + m_W, s, wLen);
// 头部开始,再拷贝剩余长度
memcpy(buf, s + w_End, wLen - w_End);
m_W = wLen - w_End; // 写位置
m_RWLen += wLen; // 读写间长度
}
else // 到尾部足够
{
memcpy(buf + m_W, s, wLen); // 直接拷贝
memcpy(buf + m_max + m_W, s, wLen); // 不理解??
m_W += wLen; // 写位置
if (m_W >= m_max)
m_W -= m_max;
m_RWLen += wLen;
}
return true;
}
写数据后,读取间长度增加,写位置改变,实际长度改变。 其中 m_q 对应 m_RWLen,m_t 对应m_W, m_b 对应 m_R
(3)读数据
bool CircularBuffer::Read(char *s,size_t rLen)
{
if (rLen > m_RWLen) // 没有足够的数据可读
return false;
// 读位置+ 读长度> 最大长度
if (m_R + rLen > m_max)
{
// 到尾部可读长度
size_t r_End = m_max - m_R;
if (s)
{
// 先读到尾部的一部分,再读首部的一部分
memcpy(s, buf + m_R, r_End);
memcpy(s + r_End, buf, rLen - r_End);
}
m_R = rLen - r_End; // 读位置
m_RWLen -= rLen; // 读写间隔
}
else // 不超出尾部,直接读
{
if (s)
memcpy(s, buf + m_R, rLen);
m_R += rLen; // 读位置后移
if (m_R >= m_max)
m_R -= m_max;
m_RWLen -= rLen;
}
if (!m_RWLen) // 读写间长度为
m_R = m_W = 0;
return true;
}
读数据后,读位置改变,读写间长度改变
bool CircularBuffer::SoftRead(char *s, size_t l)
该函数类似Read,但不改变读位置
(4)删除
bool CircularBuffer::Remove(size_t l)
{
return Read(NULL, l);
}
相当于只改变读位置和长度,没有数据拷贝
(5)获取各种长度
获取读写间长度
size_t TcpSocket::CircularBuffer::GetLength()
{
return m_q;
}
获取读指针
const char *TcpSocket::CircularBuffer::GetStart()
{
return buf + m_b;
}
获取读位置到尾部的长度
size_t TcpSocket::CircularBuffer::GetL()
{
return (m_b + m_q > m_max) ? m_max - m_b : m_q;
}
获取空闲长度
size_t TcpSocket::CircularBuffer::Space()
{
return m_max - m_q;
}
获取实际存储长度
unsigned long TcpSocket::CircularBuffer::ByteCounter(bool clear)
{
if (clear)
{
unsigned long x = m_count;
m_count = 0;
return x;
}
return m_count;
}