|

楼主 |
发表于 2009-10-22 17:40:00
|
显示全部楼层
Re:最近突然感觉自己很强。
Socket类 : 封装的关于socket的基本功能,
一.socket读写缓存管理
//缓存大小
uint32 m_readBufferSize;
uint32 m_writeBufferSize;
2.socket
SOCKET m_fd;
3.当前socket发送与接收数据大小
uint32 m_readByteCount;
uint32 m_writeByteCount;
3.读写缓存内存地址
uint8 * m_readBuffer;
uint8 * m_writeBuffer;
4.读写缓存的同步锁,用了临界区
Mutex m_writeMutex;
Mutex m_readMutex;
二. socket地址
sockaddr_in m_client;
三. 是否连接
bool m_connected;
四。销毁
//是否删除
bool m_deleted;
五。下边定义了与平台相关的一些属性 由于我只对windows平台熟悉,所以只简单介绍windows平台的
iocp
//让socket与iocp端口相关联
ASCENT_INLINE void SetCompletionPort(HANDLE cp) { m_completionPort = cp; }
//高效的发送记数同步
ASCENT_INLINE void IncSendLock() { InterlockedIncrement(&m_writeLock); }
ASCENT_INLINE void DecSendLock() { InterlockedDecrement(&m_writeLock); }
//这里可以看到如果当前发送投递还没成功,就不允许继续投递,简直的规则,因为简单所以简单。哈哈:)
ASCENT_INLINE bool AcquireSendLock()
{
if(m_writeLock)
return false;
else
{
IncSendLock();
return true;
}
}
//
OverlappedStruct m_readEvent;
OverlappedStruct m_writeEvent;
两个异步IO对象由于完成端口是异步的,完成的时候需要这两个io带回socket具体事件信息
这个大家具体看完成端口机制,也可以来与一起交流,对iocp机制,多测试,多思考,多交流
慢慢就明白了。
这里类很简单,需要一点说明就是通过windows同步函数InterlockedCompareExchange 测试一下,是否重复使用了
但是我觉得这个好像没什么用呢。
class OverlappedStruct
{
public:
OVERLAPPED m_overlap;
SocketIOEvent m_event;
volatile long m_inUse;
OverlappedStruct(SocketIOEvent ev) : m_event(ev)
{
memset(&m_overlap, 0, sizeof(OVERLAPPED));
m_inUse = 0;
};
OverlappedStruct()
{
memset(&m_overlap, 0, sizeof(OVERLAPPED));
m_inUse = 0;
}
__forceinline void Reset(SocketIOEvent ev)
{
memset(&m_overlap, 0, sizeof(OVERLAPPED));
m_event = ev;
}
void Mark()
{
long val = InterlockedCompareExchange(&m_inUse, 1, 0);
if(val != 0)
printf("!!!! Network: Detected double use of read/write event! Previous event was %u.\n", m_event);
}
void Unmark()
{
InterlockedExchange(&m_inUse, 0);
}
};
//与socket关联的完成端口
HANDLE m_completionPort;
//socket写锁
volatile long m_writeLock;
socket 类的相关操作
//构造
Socket(SOCKET fd, uint32 sendbuffersize, uint32 recvbuffersize);
//析构
~Socket();
//连接
bool Connect(const char * Address, uint32 Port);
//断开
void Disconnect();
//接收
void Accept(sockaddr_in * address);
//为派生类处理socket事件的虚函数
virtual void OnRead() {}
virtual void OnConnect() {}
virtual void OnDisconnect() {}
//读写数据后把后边的数据移动前边(更高效的可以用环形缓存,或者环形队列)
void __fastcall RemoveReadBufferBytes(uint32 size, bool lock);
void __fastcall RemoveWriteBufferBytes(uint32 size, bool lock);
//得到接收缓存某个偏移的地址
ASCENT_INLINE uint8 * GetReadBuffer(uint32 offset) { return m_readBuffer + offset; }
//接收到数据改变接收数据的大小
ASCENT_INLINE void AddRecvData(uint32 size) { m_readByteCount += size; }
//读很简单,就是把数据读出来,然后移动数据,改变缓存数据的大小
void __fastcall Read(uint32 size, uint8 * buffer);
//写的时候比较复杂,需要把缓存锁定进行同步,统一要对投递的io进行计数
bool Send(const uint8 * Bytes, uint32 Size);
//锁定
ASCENT_INLINE void BurstBegin() { m_writeMutex.Acquire(); }
//copy数据
bool BurstSend(const uint8 * Bytes, uint32 Size);
//计数,并且投递IO
void BurstPush();
//解锁
ASCENT_INLINE void BurstEnd() { m_writeMutex.Release(); }
//得到IP端口,socket 相关的信息
string GetRemoteIP();
ASCENT_INLINE uint32 GetRemotePort() { return ntohs(m_client.sin_port); }
ASCENT_INLINE SOCKET GetFd() { return m_fd; }
//IP相关的投递异步IP
//投递一个异步读IO
void SetupReadEvent();
//异步IO读读完成时的回调,但是不知道为什么没有用,
void HandleReadComplete(Socket * s, uint32 len)
{
//s->m_readEvent=NULL;
if(!s->IsDeleted())
{
s->m_readEvent.Unmark();
if(len)
{
s->AddRecvData(len);
s->OnRead();
s->SetupReadEvent();
}
else
s->Delete(); // Queue deletion.
}
}
void ReadCallback(uint32 len);
//投递一个异步写IO,这里如果数据都发送完了,就减少IO计数,处理了一次没有发完的情况。
void WriteCallback();
ASCENT_INLINE bool IsDeleted() { return m_deleted; }
ASCENT_INLINE bool IsConnected() { return m_connected; }
ASCENT_INLINE uint32 GetReadBufferSize() { return m_readByteCount; }
ASCENT_INLINE uint32 GetWriteBufferSize() { return m_writeByteCount; }
ASCENT_INLINE sockaddr_in & GetRemoteStruct() { return m_client; }
void Delete();
ASCENT_INLINE in_addr GetRemoteAddress() { return m_client.sin_addr; }
//连接完成时的回调
void _OnConnect();
关联socket到完成端口
void AssignToCompletionPort();
其他还有
ConnectTCPSocket
//垃圾回收
SocketGarbageCollector
基本上都很简单,一看就明白
基本上socket类就是封装了socke 基本操作,与读写缓存的管理,读没有进行同步。写进行同步和计数。通过虚函数来实现网络事件基类与派生类的传递
socket部分设计的没有什么特色。
//管理socket类,基本上有个set列表存放socket对象指针,初始化的时候创建了个socket,和一个接口void SpawnWorkerThreads();启动
void SocketMgr::SpawnWorkerThreads()
{
SYSTEM_INFO si;
GetSystemInfo(&si);
threadcount = si.dwNumberOfProcessors;
printf("IOCP: Spawning %u worker threads.\n", threadcount);
for(long x = 0; x < threadcount; ++x)
ThreadPool.ExecuteTask(new SocketWorkerThread());
}
class SocketWorkerThread : public ThreadBase
{
public:
bool run();
};
bool SocketWorkerThread::run()
{
THREAD_TRY_EXECUTION2
HANDLE cp = sSocketMgr.GetCompletionPort();
DWORD len;
Socket * s;
OverlappedStruct * ov;
LPOVERLAPPED ol_ptr;
while(true)
{
#ifndef _WIN64
if(!GetQueuedCompletionStatus(cp, &len, (LPDWORD)&s, &ol_ptr, 10000))
#else
if(!GetQueuedCompletionStatus(cp, &len, (PULONG_PTR)&s, &ol_ptr, 10000))
#endif
continue;
ov = CONTAINING_RECORD(ol_ptr, OverlappedStruct, m_overlap);
if(ov->m_event == SOCKET_IO_THREAD_SHUTDOWN)
{
delete ov;
return true;
}
if(ov->m_event < NUM_SOCKET_IO_EVENTS)
ophandlers[ov->m_event](s, len);
}
THREAD_HANDLE_CRASH2
return true;
}
就是一个工作线程池
class SERVER_DECL SocketMgr : public Singleton<SocketMgr>
整个类就是一个set结构存socket 一个工作线程池,和一个接口取完成端口,
typedef void(*OperationHandler)(Socket * s, uint32 len);
void SERVER_DECL HandleReadComplete(Socket * s, uint32 len);
void SERVER_DECL HandleWriteComplete(Socket * s, uint32 len);
void SERVER_DECL HandleShutdown(Socket * s, uint32 len);
static OperationHandler ophandlers[NUM_SOCKET_IO_EVENTS] = {
&HandleReadComplete,
&HandleWriteComplete,
&HandleShutdown };
函数指针数组。
工作线程里调用的。
在cpp文件Ascent\branches\3_8_stable\src\ascent-realmserver\Master.cpp 里有这样几据,开启了服务器的网络基础。
new SocketMgr;
new SocketGarbageCollector;
sSocketMgr.SpawnWorkerThreads();
Log.Success("Network", "Network Subsystem Started.");
Log.Notice("Network", "Opening Client Port...");
ListenSocket<WorldSocket> * wsl = new ListenSocket<WorldSocket>("0.0.0.0", 8129);
里边有listen类,很简单,就是一个服务器端监听程序。
template<class T>
class SERVER_DECL ListenSocket : public ThreadBase
{
public:
ListenSocket(const char * ListenAddress, uint32 Port)
{
m_socket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
SocketOps::ReuseAddr(m_socket);
SocketOps::Blocking(m_socket);
m_address.sin_family = AF_INET;
m_address.sin_port = ntohs((u_short)Port);
m_address.sin_addr.s_addr = htonl(INADDR_ANY);
m_opened = false;
if(strcmp(ListenAddress, "0.0.0.0"))
{
struct hostent * hostname = gethostbyname(ListenAddress);
if(hostname != 0)
memcpy(&m_address.sin_addr.s_addr, hostname->h_addr_list[0], hostname->h_length);
}
// bind.. well attempt to.
int ret = bind(m_socket, (const sockaddr*)&m_address, sizeof(m_address));
if(ret != 0)
{
printf("Bind unsuccessful on port %u.", Port);
return;
}
ret = listen(m_socket, 5);
if(ret != 0)
{
printf("Unable to listen on port %u.", Port);
return;
}
m_opened = true;
len = sizeof(sockaddr_in);
m_cp = sSocketMgr.GetCompletionPort();
}
~ListenSocket()
{
Close();
}
bool run()
{
while(m_opened)
{
//aSocket = accept(m_socket, (sockaddr*)&m_tempAddress, (socklen_t*)&len);
aSocket = WSAAccept(m_socket, (sockaddr*)&m_tempAddress, (socklen_t*)&len, NULL, NULL);
if(aSocket == INVALID_SOCKET)
continue; // shouldn't happen, we are blocking.
socket = new T(aSocket);
socket->SetCompletionPort(m_cp);
socket->Accept(&m_tempAddress);
}
return true;
/*aSocket = WSAAccept(m_socket, (sockaddr*)&m_tempAddress, (socklen_t*)&len, NULL, NULL);
if(aSocket == INVALID_SOCKET)
return;
socket = new T(aSocket);
socket->SetCompletionPort(m_cp);
socket->Accept(&m_tempAddress);*/
}
void Close()
{
// prevent a race condition here.
bool mo = m_opened;
m_opened = false;
if(mo)
SocketOps::CloseSocket(m_socket);
}
ASCENT_INLINE bool IsOpen() { return m_opened; }
private:
SOCKET m_socket;
struct sockaddr_in m_address;
struct sockaddr_in m_tempAddress;
bool m_opened;
uint32 len;
SOCKET aSocket;
T * socket;
HANDLE m_cp;
};
还有游戏世界socket
class WorldSocket : public Socket
{
public:
bool Authed;
WorldSocket(SOCKET fd);
~WorldSocket();
//发送数据
ASCENT_INLINE void SendPacket(WorldPacket* packet) { if(!packet) return; OutPacket(packet->GetOpcode(), packet->size(), (packet->size() ? (const void*)packet->contents() : NULL)); }
ASCENT_INLINE void SendPacket(StackBufferBase * packet) { if(!packet) return; OutPacket(packet->GetOpcode(), packet->GetSize(), (packet->GetSize() ? (const void*)packet->GetBufferPointer() : NULL)); }
void __fastcall OutPacket(uint16 opcode, size_t len, const void* data);
//得到网络延迟
ASCENT_INLINE uint32 GetLatency() { return _latency; }
//验证
void Authenticate();
//返回
void InformationRetreiveCallback(WorldPacket & recvData, uint32 requestid);
//网络事件的虚函数
void OnRead();
void OnConnect();
void OnDisconnect();
protected:
void _HandleAuthSession(WorldPacket* recvPacket);
void _HandlePing(WorldPacket* recvPacket);
private:
uint32 mOpcode;
uint32 mRemaining;
uint32 mSize;
uint32 mSeed;
uint32 mClientSeed;
uint32 mClientBuild;
uint32 mRequestID;
WorldPacket * pAuthenticationPacket;
WowCrypt _crypt;
uint32 _latency;
Session * m_session;
};
//连接和断开很简单
1.连接到发个消息
void WorldSocket::OnConnect()
{
OutPacket(SMSG_AUTH_CHALLENGE, 4, &mSeed);
_latency = getMSTime();
}
void WorldSocket::OnDisconnect()
{
if(mRequestID != 0)
{
sLogonCommHandler.UnauthedSocketClose(mRequestID);
mRequestID = 0;
}
}
2.收到数据
void WorldSocket::OnRead()
{
for(;;)
{
//收到多少数据是否大于0
if(mRemaining == 0)
{
//收到多少数据,包头是6个字节,所以必须大于等于6
if(GetReadBufferSize() < 6)
{
return;
}
//读入包头数据
ClientPktHeader Header;
Read(6, (uint8*)&Header);
//解密包头
_crypt.DecryptSixRecv((uint8*)&Header);
//得到数据包的大小
mRemaining = mSize = ntohs(Header.size) - 4;
//包的标记
mOpcode = Header.cmd;
}
WorldPacket * Packet;
//判断诗句石头接收完
if(mRemaining > 0)
{
if( GetReadBufferSize() < mRemaining )
{
// We have a fragmented packet. Wait for the complete one before proceeding.
return;
}
}
//读入包,然后感觉包类型分别处理 前变两个包是与验证相关的,放在worldsocket里处理其余的与游戏相关的
wordsesion处理
if(m_session) m_session->m_readQueue.Push(Packet);
Packet = new WorldPacket(mOpcode, mSize);
Packet->resize(mSize);
if(mRemaining > 0)
{
// Copy from packet buffer into our actual buffer.
Read(mRemaining, (uint8*)Packet->contents());
}
/*sWorldLog.LogPacket(mSize, mOpcode, mSize ? Packet->contents() : NULL, 0);*/
mRemaining = mSize = mOpcode = 0;
// Check for packets that we handle
switch(Packet->GetOpcode())
{
case CMSG_PING:
{
if(!m_session->m_currentPlayer)
{
_HandlePing(Packet);
delete Packet;
}
else
m_session->m_readQueue.Push(Packet);
}break;
case CMSG_AUTH_SESSION:
{
_HandleAuthSession(Packet);
}break;
default:
{
if(m_session) m_session->m_readQueue.Push(Packet);
else delete Packet;
}break;
}
}
}
类WorldSocket 与sesion关系
void WorldSocket::Authenticate()
{
WorldSession * pSession = mSession;
ASSERT(pAuthenticationPacket);
mQueued = false;
if(!pSession) return;
pSession->deleteMutex.Acquire();
if(pSession->HasFlag(ACCOUNT_FLAG_XPACK_01))
OutPacket(SMSG_AUTH_RESPONSE, 11, "\x0C\x30\x78\x00\x00\x00\x00\x00\x00\x00\x01");
else
OutPacket(SMSG_AUTH_RESPONSE, 11, "\x0C\x30\x78\x00\x00\x00\x00\x00\x00\x00\x00");
sAddonMgr.SendAddonInfoPacket(pAuthenticationPacket, (uint32)pAuthenticationPacket->rpos(), mSession);
pSession->_latency = _latency;
delete pAuthenticationPacket;
pAuthenticationPacket = 0;
if(mSession)
{
sWorld.AddSession(mSession);
sWorld.AddGlobalSession(mSession);
/* if(pSession->HasFlag(ACCOUNT_FLAG_XTEND_INFO))
sWorld.AddExtendedSession(pSession);*/
if(pSession->HasGMPermissions() && mSession)
sWorld.gmList.insert(pSession);
}
pSession->deleteMutex.Release();
}
这里new出了world sesion 这里登录的时候创建按的
ascent-world\LogonCommClient.cpp
void WorldSocket::InformationRetreiveCallback(WorldPacket & recvData, uint32 requestid)
{
if(requestid != mRequestID)
return;
uint32 error;
recvData >> error;
if(error != 0)
{
// something happened wrong @ the logon server
OutPacket(SMSG_AUTH_RESPONSE, 1, "\x0D");
return;
}
// Extract account information from the packet.
string AccountName;
const string * ForcedPermissions;
uint32 AccountID;
string GMFlags;
uint8 AccountFlags;
string lang = "enUS";
uint32 i;
recvData >> AccountID >> AccountName >> GMFlags >> AccountFlags;
ForcedPermissions = sLogonCommHandler.GetForcedPermissions(AccountName);
if( ForcedPermissions != NULL )
GMFlags.assign(ForcedPermissions->c_str());
sLog.outDebug( " >> got information packet from logon: `%s` ID %u (request %u)", AccountName.c_str(), AccountID, mRequestID);
// sLog.outColor(TNORMAL, "\n");
mRequestID = 0;
// Pull the session key.
uint8 K[40];
recvData.read(K, 40);
BigNumber BNK;
BNK.SetBinary(K, 40);
// Initialize crypto.
_crypt.SetKey(K, 40);
_crypt.Init();
//checking if player is already connected
//disconnect corrent player and login this one(blizzlike)
if(recvData.rpos() != recvData.wpos())
recvData.read((uint8*)lang.data(), 4);
WorldSession *session = sWorld.FindSession( AccountID );
if( session)
{
// AUTH_FAILED = 0x0D
session->Disconnect();
}
Sha1Hash sha;
uint8 digest[20];
pAuthenticationPacket->read(digest, 20);
uint32 t = 0;
sha.UpdateData(AccountName);
sha.UpdateData((uint8 *)&t, 4);
sha.UpdateData((uint8 *)&mClientSeed, 4);
sha.UpdateData((uint8 *)&mSeed, 4);
sha.UpdateBigNumbers(&BNK, NULL);
sha.Finalize();
if (memcmp(sha.GetDigest(), digest, 20))
{
// AUTH_UNKNOWN_ACCOUNT = 21
OutPacket(SMSG_AUTH_RESPONSE, 1, "\x15");
return;
}
// Allocate session
WorldSession * pSession = new WorldSession(AccountID, AccountName, this);
mSession = pSession;
ASSERT(mSession);
pSession->deleteMutex.Acquire();
// Set session properties
pSession->SetClientBuild(mClientBuild);
pSession->LoadSecurity(GMFlags);
pSession->SetAccountFlags(AccountFlags);
pSession->m_lastPing = (uint32)UNIXTIME;
pSession->language = sLocalizationMgr.GetLanguageId(lang);
if(recvData.rpos() != recvData.wpos())
recvData >> pSession->m_muted;
for(uint32 i = 0; i < 8; ++i)
pSession->SetAccountData(i, NULL, true, 0);
// queue the account loading
/*AsyncQuery * aq = new AsyncQuery( new SQLClassCallbackP1<World, uint32>(World::getSingletonPtr(), &World: oadAccountDataProc, AccountID) );
aq->AddQuery("SELECT * FROM account_data WHERE acct = %u", AccountID);
CharacterDatabase.QueueAsyncQuery(aq);*/
if(sWorld.m_useAccountData)
{
QueryResult * pResult = CharacterDatabase.Query("SELECT * FROM account_data WHERE acct = %u", AccountID);
if( pResult == NULL )
CharacterDatabase.Execute("INSERT INTO account_data VALUES(%u, '', '', '', '', '', '', '', '', '')", AccountID);
else
{
size_t len;
const char * data;
char * d;
for(i = 0; i < 8; ++i)
{
data = pResult->Fetch()[1+i].GetString();
len = data ? strlen(data) : 0;
if(len > 1)
{
d = new char[len+1];
memcpy(d, data, len+1);
pSession->SetAccountData(i, d, true, (uint32)len);
}
}
delete pResult;
}
}
Log.Debug("Auth", "%s from %s:%u [%ums]", AccountName.c_str(), GetRemoteIP().c_str(), GetRemotePort(), _latency);
// Check for queue.
if( (sWorld.GetSessionCount() < sWorld.GetPlayerLimit()) || pSession->HasGMPermissions() ) {
Authenticate();
} else {
// Queued, sucker.
uint32 Position = sWorld.AddQueuedSocket(this);
mQueued = true;
Log.Debug("Queue", "%s added to queue in position %u", AccountName.c_str(), Position);
// Send packet so we know what we're doing
UpdateQueuePosition(Position);
}
pSession->deleteMutex.Release();
}
WorldSession * pSession = new WorldSession(AccountID, AccountName, this);
mSession = pSession;
worldsseion加在游戏世界服务器world里
void WorldSocket::Authenticate()
{
WorldSession * pSession = mSession;
ASSERT(pAuthenticationPacket);
mQueued = false;
if(!pSession) return;
pSession->deleteMutex.Acquire();
if(pSession->HasFlag(ACCOUNT_FLAG_XPACK_01))
OutPacket(SMSG_AUTH_RESPONSE, 11, "\x0C\x30\x78\x00\x00\x00\x00\x00\x00\x00\x01");
else
OutPacket(SMSG_AUTH_RESPONSE, 11, "\x0C\x30\x78\x00\x00\x00\x00\x00\x00\x00\x00");
sAddonMgr.SendAddonInfoPacket(pAuthenticationPacket, (uint32)pAuthenticationPacket->rpos(), mSession);
pSession->_latency = _latency;
delete pAuthenticationPacket;
pAuthenticationPacket = 0;
if(mSession)
{
sWorld.AddSession(mSession);
sWorld.AddGlobalSession(mSession);
/* if(pSession->HasFlag(ACCOUNT_FLAG_XTEND_INFO))
sWorld.AddExtendedSession(pSession);*/
if(pSession->HasGMPermissions() && mSession)
sWorld.gmList.insert(pSession);
}
pSession->deleteMutex.Release();
}
加入到游戏世界里
sWorld.AddSession(mSession);
sWorld.AddGlobalSession(mSession);
/*if(pSession->HasFlag(ACCOUNT_FLAG_XTEND_INFO))
sWorld.AddExtendedSession(pSession);*/
if(pSession->HasGMPermissions() && mSession)
sWorld.gmList.insert(pSession);
这里只要网络断开之后,就不向上发消息了
因为void WorldSocket::OnRead()
{
if(mSession) mSession->QueuePacket(Packet);
else delete Packet;
}
而
void WorldSocket::OnDisconnect()
{
//msession 变为=0;所以上边那个函数就不向上发消息了。
if(mSession)
{
mSession->SetSocket(0);
mSession=NULL;
}
//SOCKET无效
if(mRequestID != 0)
{
sLogonCommHandler.UnauthedSocketClose(mRequestID);
mRequestID = 0;
}
//正在排队时候,对里删除
if(mQueued)
{
sWorld.RemoveQueuedSocket(this); // Remove from queued sockets.
mQueued=false;
}
}
对于网络层说明的是 设计的比较粗糙
1.资源释放 对IO接收没有计数处理,释放机制应该有bug.
2.
void OnRead();
void OnConnect();
void OnDisconnect();
外部回调函数,反映网络事件,一定要同步 ,也就是说在网络层不能对一个客户端socket既进行读又进行写。虽然可以同时,但是读写的时候也定要同步。
比如
void WorldSocket::OnRead()
{
if(mSession) mSession->QueuePacket(Packet);
else delete Packet;
}
正在进行这个操作的时候
另一个线程突然
void OnDisconnect()
{
if(mSession)
{
mSession->SetSocket(0);
mSession=NULL;
}
}
马上读写异常。
///////////////////////////////////////////////////////////////////////////////
网络层应该很独立,对高层来透明的下边的我的网络接口
#ifndef _IBasicServer_H_
#define _IBasicServer_H_
#include "IEventCallBack.h"
//接口说明
//1.设定系统发送缓存和接收缓存分别为4096
//2.为每个连接分配4096*2字节逻辑环形缓存,用来缓存逻辑层下发的数据,
//一旦服务器可以发送,立即取逻辑缓存数据进行发送,逻辑环形缓存的结构为
//【消息数据指针1,消息长度1;消息数据指针2,消息长度2.。。。】【数据部分1,数据部分2】
//最大消息个数为100,数据最大4096*2字节
//最大工作线程为5个
class __declspec(dllexport) IBasicServer
{
public:
static IBasicServer* GetBaseServer();
//服务器初始化
virtual void Init(int nIOPlusNum,int hTcpHandleNum,int maxThreadCount)=0;
//服务器释放
virtual void Release()=0;
//开启服务器
virtual bool StartUp(const char *addr,int port)=0;
//关闭服务器
virtual void Shutdown()=0;
//发送数据
virtual bool SendData(int nIndex,int serial, const void * pdata,int len) = 0;
//关闭某个连接
virtual void CloseClientHandle(int nIndex,int serial)=0;
//关闭所有连接
virtual void ClassAllClientHandle()=0;
//得到当前连接数
virtual int GetCurTcpHandle()=0;
//等到当前未使用的IO
virtual int GetCurTcpIOPlus()=0;
// 设置回调接口
virtual void SetEventCallBack(IEventCallBack * EventCallBack)=0;
};
#endif
#ifndef _IEventCallBack_H_
#define _IEventCallBack_H_
//事件回调
//收到底层的网路事件回调
class IEventCallBack
{
public:
// 连接建立
virtual int OnAccept(int nIndex, int nSerial, const char * szRemoteIP, unsigned int nRemotePort) = 0;
// 连接关闭
virtual int OnClose(int nIndex, int nSerial, const char * szRemoteIP, unsigned int nRemotePort) = 0;
// 发送消息(只用作调试版的发送消息记录)
virtual int OnSend( int nIndex, int nSerial, const void * pData, int nLen) = 0;
// 收到消息
virtual int OnReceive(int nIndex, int nSerial, const void * pData,int nLen) = 0;
};
#endif
//很简单
|
|