游戏开发论坛

 找回密码
 立即注册
搜索
查看: 7766|回复: 17

最近突然感觉自己很强。

[复制链接]

1

主题

17

帖子

18

积分

新手上路

Rank: 1

积分
18
发表于 2009-10-21 21:49:00 | 显示全部楼层 |阅读模式
完了要倒霉了

11

主题

650

帖子

651

积分

高级会员

Rank: 4

积分
651
发表于 2009-10-21 22:14:00 | 显示全部楼层

Re: 最近突然感觉自己很强。

[em11]

6

主题

26

帖子

30

积分

注册会员

Rank: 2

积分
30
发表于 2009-10-21 22:56:00 | 显示全部楼层

Re:最近突然感觉自己很强。

- -!

1

主题

17

帖子

18

积分

新手上路

Rank: 1

积分
18
 楼主| 发表于 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
//很简单

1

主题

17

帖子

18

积分

新手上路

Rank: 1

积分
18
 楼主| 发表于 2009-10-22 17:40:00 | 显示全部楼层

Re:最近突然感觉自己很强。

无聊看了下ascent网络部分

18

主题

493

帖子

494

积分

中级会员

Rank: 3Rank: 3

积分
494
发表于 2009-10-22 18:10:00 | 显示全部楼层

Re:最近突然感觉自己很强。

好吧,你很强,除了单词意思我撒也没看懂

1

主题

17

帖子

18

积分

新手上路

Rank: 1

积分
18
 楼主| 发表于 2009-10-23 13:51:00 | 显示全部楼层

Re:最近突然感觉自己很强。

Asent游戏世界

class SERVER_DECL World : public Singleton<World>, public EventableObject

游戏世界类是个单件类,主要管理所有的连接,和一些全局控制的数据,与操作。配置


2游戏世界运行。游戏世界World派生自EventableObject是一可运行对象。
class WorldRunnable
bool WorldRunnable::run()
{
        SetThreadName("WorldRunnable (non-instance/logon)");
        uint32 LastWorldUpdate=getMSTime();
        uint32 LastSessionsUpdate=getMSTime();

        THREAD_TRY_EXECUTION2

        while(ThreadState != THREADSTATE_TERMINATE)
        {
                // Provision for pausing this thread.
                if(ThreadState == THREADSTATE_PAUSED)
                {
                        while(ThreadState == THREADSTATE_PAUSED)
                        {
                                Sleep(200);
                        }
                }
                if(ThreadState == THREADSTATE_TERMINATE)
                        break;

                ThreadState = THREADSTATE_BUSY;

                uint32 diff;
                //calce time passed
                uint32 now,execution_start;
                now=getMSTime();
                execution_start=now;

                if( now < LastWorldUpdate)//overrun
                        diff=WORLD_UPDATE_DELAY;
                else
                        diff=now-LastWorldUpdate;
               
                LastWorldUpdate=now;
                sWorld.Update( diff );
               
                now=getMSTime();
               
                if( now < LastSessionsUpdate)//overrun
                        diff=WORLD_UPDATE_DELAY;
                else
                        diff=now-LastSessionsUpdate;
               
                LastSessionsUpdate=now;
                sWorld.UpdateSessions( diff );
               
                now=getMSTime();
                //we have to wait now
               
                if(execution_start > now)//overrun
                        diff=WORLD_UPDATE_DELAY-now;
                else
                        diff=now-execution_start;//time used for updating
                if(ThreadState == THREADSTATE_TERMINATE)
                        break;

                ThreadState = THREADSTATE_SLEEPING;

                /*This is execution time compensating system
                        if execution took more than default delay
                        no need to make this sleep*/
                if(diff<WORLD_UPDATE_DELAY)
                Sleep(WORLD_UPDATE_DELAY-diff);
        }

        THREAD_HANDLE_CRASH2
        return true;
}
这里主要是sWorld.Update( diff )
void World::Update(time_t diff)
{
        //更新游戏事件
        eventholder->Update((uint32)diff);
        //更新寄卖所
        sAuctionMgr.Update();
        _UpdateGameTime();
        //处理游戏排队信息的
        UpdateQueuedSessions((uint32)diff);
}
这里
//Ascent事件处理机制里边有两个事件队列m_insertPool,m_events,游戏在处理逻辑过程中把事件插入到
//m_insertPool ,处理的时候把m_insertPool压入到m_events,进行处理,用空间换时间的一种方法。对于
//初学者来讲,这是一个很好的示范,(异步调用,多线程转化单线程)在游戏服务器逻辑设计的过程中,
//当事件发生的时候把处理函数与调用处理函数的参数作为一个整体压入队列之中,然后用一
//单线程去处理,往往可以然模型简单化。而且可以避免复杂的同步问题
void EventableObjectHolder::Update(uint32 time_difference)
{
        m_lock.Acquire();                        // <<<<

        //锁定插入事件队列
        m_insertPoolLock.Acquire();
        InsertableQueue::iterator iqi;
        InsertableQueue::iterator iq2 = m_insertPool.begin();
        //把插入事件列表压入处理事件队列
        while(iq2 != m_insertPool.end())
        {
                iqi = iq2++;
                if((*iqi)->deleted || (*iqi)->instanceId != mInstanceId)
                        (*iqi)->DecRef();
                else
                        m_events.push_back( (*iqi) );

                m_insertPool.erase(iqi);
        }
        //解锁
        m_insertPoolLock.Release();
        //现在m_insertPoolLock已经解锁,我们处理的过程中,如果有其他线程想向其中插入事件
        //就会减少同步的
        EventList::iterator itr = m_events.begin();
        EventList::iterator it2;
        TimedEvent * ev;
        //事件处理
        while(itr != m_events.end())
        {
                it2 = itr++;
                //由于是以场景为化为游戏世界的标准,所以事件处理基于场景*it2)->instanceId != mInstanceId
                if((*it2)->instanceId != mInstanceId || (*it2)->deleted ||
                        ( mInstanceId == WORLD_INSTANCE && (*it2)->eventFlag & EVENT_FLAG_DO_NOT_EXECUTE_IN_WORLD_CONTEXT))
                {
                        //无效事件就删除掉
                        (*it2)->DecRef();
                        m_events.erase(it2);
                        continue;
                }
                ev = *it2;
                //事件相当与心跳一样。看到事件没,没到事件就不处理
                if((uint32)ev->currTime <= time_difference)
                {
                        if(ev->eventFlag & EVENT_FLAG_DELETES_OBJECT)
                        {
                                ev->deleted = true;
                                ev->cb->execute();
                                m_events.erase(it2);
                                ev->DecRef();
                                continue;
                        }
                        else
                                ev->cb->execute();
                        //一个事件可能多次执行,如果执行完全部次数,则删除
            if(ev->repeats && --ev->repeats == 0)
                        {
                                ev->deleted = true;
                                ev->DecRef();
                                m_events.erase(it2);

                                continue;
                        }
                        else if(ev->deleted)
                        {
                                ev->DecRef();
                                m_events.erase(it2);
                                continue;
                        }

                        ev->currTime = ev->msTime;
                }
                else
                {
                        ev->currTime -= time_difference;
                }
        }

        m_lock.Release();
}

//更新寄卖所
sAuctionMgr.Update();
_UpdateGameTime();
//处理游戏排队信息的
UpdateQueuedSessions((uint32)diff);
以上与游戏具体内容相关的,不左讨论

32

主题

235

帖子

235

积分

中级会员

Rank: 3Rank: 3

积分
235
发表于 2009-10-23 16:13:00 | 显示全部楼层

Re:最近突然感觉自己很强。

打不死的小???帷!!

1

主题

17

帖子

18

积分

新手上路

Rank: 1

积分
18
 楼主| 发表于 2009-10-23 18:07:00 | 显示全部楼层

Re:最近突然感觉自己很强。

如果把游戏世界比做一个过国家,那么一个个场景,无论游戏启动时,还是游戏运行过程创建的副本,就是各个省份了,这些省份是怎么管理的呢,分析
Ascent代码看看
Ascent\branches\3_8_stable\src\ascent-world\WorldCreator.h
这个相当于运行中每个场景信息
class Instance
{
public:
        //这个实例ID每个存在的场景有一个
        uint32 m_instanceId;
        //地图ID
        uint32 m_mapId;
        //地图管理
        MapMgr * m_mapMgr;
        //副本相关的
        uint32 m_creatorGuid;
        uint32 m_creatorGroup;
        //难度
        uint32 m_difficulty;
        //被杀的npc表
            set<uint32> m_killedNpcs;
        //创建时间
        time_t m_creation;
        //过期时间
        time_t m_expiration;
        //地图信息
        MapInfo * m_mapInfo;
        //是否是战场
        bool m_isBattleground;

        void LoadFromDB(Field * fields);
        void SaveToDB();
        void DeleteFromDB();
};

二,这里管理这些信息
#define NUM_MAPS 600
typedef HM_NAMESPACE::hash_map<uint32, Instance*> InstanceMap;

class SERVER_DECL InstanceMgr
{
        friend class MapMgr;
public:
        InstanceMgr();       
        ~InstanceMgr();
        //地图信息
        ASCENT_INLINE Map* GetMap(uint32 mapid)
        {
                if(mapid>NUM_MAPS)
                        return NULL;
                else
                        return m_maps[mapid];
        }
        //传送预处理,
        uint32 PreTeleport(uint32 mapid, Player * plr, uint32 instanceid);
        //得到对象所在副本
        MapMgr * GetInstance(Object* obj);
        //返回副本ID
        uint32 GenerateInstanceID();
        //把场景的一些状态打包成xml文件
        void BuildXMLStats(char * m_file);
        //在数据库与内存里读取地图文件,创建场景
        void Load(TaskList * l);
        //删除某个玩家所有副本
        void ResetSavedInstances(Player * plr);
        //删除某个组或者团的所有副本
        void OnGroupDestruction(Group * pGroup);
        void PlayerLeftGroup(Group * pGroup, Player * pPlayer);
         ASCENT_INLINE bool PlayerOwnsInstance(Instance * pInstance, Player * pPlayer)
        {
                // expired?
                if( pInstance->m_expiration && (UNIXTIME+20) >= pInstance->m_expiration)
                {
                        // delete the instance
                        _DeleteInstance(pInstance, true);
                        return false;
                }

                if( (pPlayer->GetGroup() && pInstance->m_creatorGroup == pPlayer->GetGroup()->GetID()) || pPlayer->GetGUIDLow() == pInstance->m_creatorGuid )
                        return true;

                return false;
        }
        ASCENT_INLINE bool HasInstanceExpired(Instance * pInstance)
        {
                // expired?
                if( pInstance->m_expiration && (UNIXTIME+20) >= pInstance->m_expiration)
                        return true;

                return false;
        }
        void CheckForExpiredInstances();

        //删除所有副本
        void Shutdown();
        void BuildRaidSavedInstancesForPlayer(Player * plr);
        void BuildSavedInstancesForPlayer(Player * plr);
        //创建战场副本
        MapMgr * CreateBattlegroundInstance(uint32 mapid);
        //删除战场副本
        void DeleteBattlegroundInstance(uint32 mapid, uint32 instanceid);

private:
        //数据库里加载副本
        void _LoadInstances();
        //创建地图
        void _CreateMap(uint32 mapid);
        //创建地图
        MapMgr* _CreateInstance(Instance * in);
        //创建副本
        MapMgr* _CreateInstance(uint32 mapid, uint32 instanceid);                // only used on main maps!
        //删除副本
        bool _DeleteInstance(Instance * in, bool ForcePlayersOut);

        uint32 m_InstanceHigh;
        地图锁
        Mutex m_mapLock;
        地图信息
        Map * m_maps[NUM_MAPS];
        场景信息
        InstanceMap* m_instances[NUM_MAPS];
        //场景管理
        MapMgr * m_singleMaps[NUM_MAPS];
};

extern SERVER_DECL InstanceMgr sInstanceMgr;

ascent 游戏世界管理
游戏世界包括各个游戏固定的场景和动态创建的副本,副本与固定的场景之间差别就是固定场景只有一份,而副本可以有许多份,相同的地方就是他们的地图原型都是一样的,这样我们就可以这样实现

class InstanceMgr
{
  Mutex m_mapLock;
  //场景的地图信息
  Map * m_maps[NUM_MAPS];
  固定场景与副本
  InstanceMap* m_instances[NUM_MAPS];
  //场景运行是对象,包含运行时候场景中各种对象,已经运行场景的线程。
  MapMgr * m_singleMaps[NUM_MAPS];
}

几点说明
1.Instance 每个场景一个对象。
//这个实例ID每个存在的场景有一个包含这些东西,所有都是与场景有关的一些东西。 场景ID 地图ID 场景运行时对象指针MapMgr * m_mapMgr;
//由于场景有可能是副本,同一原型地图可能有不同的难度m_difficulty,创建时间m_creation;,生命期m_expiration,一些地形信息MapInfo * m_mapInfo;
为什么包含MapMgr * m_mapMgr,InstanceMgr里包含了 MapMgr * m_singleMaps[NUM_MAPS]; 但是这个是固定场景的,如果是副本MapMgr *对象,则只能在
Instance 类型的对象里找到
2
由于游戏世界里,地图与将来运行场景不是一一对应的,同一地图如果普通地图将来运行只有一个场景,如果是副本地图可能有很多场景,
场景数 >= 地图数 .所以这里用typedef HM_NAMESPACE::hash_map<uint32, Instance*> InstanceMap; 是一个map数组 InstanceMap* m_instances[NUM_MAPS];,
数组的下标为地图索引,当玩家要传送到某一个场景的时候,需要线用地图ID做索引,找到这个map,如果对象不存在,
创建一个InstanceMap 然受在map中插入这个InstanceMap对象指针。

3
3. CGameSceneMgr 是场景的运行信
  (1)游戏架构是基于场景的,对象(除玩家与公共数据外),如: NPC 怪物,其生命周期都是基与场景的,所有这些都保存在CGameSceneMgr对象里
  (2)为避免复杂同步,游戏逻辑用单线程来处理。游戏逻辑主要有服务器内部事件,服务器外部收到的客户端消息,还有心跳,这些统一放到一个工作线程去处理。
  (3) MapMgr * m_singleMaps[NUM_MAPS];对象根据Map *静态地形信息,把场景化分成n*m的格子Tiles,游戏场景里对象都包含格子的引用。这样游戏对象发生事件
        需要广播的时候,只需要对进行相邻的8块进行广播。

1

主题

17

帖子

18

积分

新手上路

Rank: 1

积分
18
 楼主| 发表于 2009-10-23 18:08:00 | 显示全部楼层

Re:最近突然感觉自己很强。

下班回家
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-6-3 05:46

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表