游戏开发论坛

 找回密码
 立即注册
搜索
楼主: sea_bug

MMOG网络同步算法揭秘(QQ幻想)

[复制链接]

22

主题

309

帖子

353

积分

中级会员

Rank: 3Rank: 3

积分
353
QQ
发表于 2012-11-18 22:39:00 | 显示全部楼层

Re: Re:MMOG网络同步算法揭秘(QQ幻想)

DogFeet: Re:MMOG网络同步算法揭秘(QQ幻想)

还有个很经典的例子,不知道大家有没有注意到。
在wow中,你卡的时候,一般是通过释放技能感觉出来的。但是...


wow是tcp,war3是udp,传奇是tcp+udp
wow移动是客户端先行移动再通知服务器验证.所以客户端体验很流畅.
war3和传奇是先向服务器请求移动.收到服务器回复同意之后客户端才开始移动.

ps:云风的博客看过,纯粹从技术角度讲,感觉.呵呵...

0

主题

17

帖子

19

积分

新手上路

Rank: 1

积分
19
发表于 2012-11-20 11:00:00 | 显示全部楼层

Re:MMOG网络同步算法揭秘(QQ幻想)

怎么贴附件?

0

主题

17

帖子

19

积分

新手上路

Rank: 1

积分
19
发表于 2012-11-20 11:01:00 | 显示全部楼层

Re:MMOG网络同步算法揭秘(QQ幻想)

/*
        Simple Network Library from "Networking for Game Programmers"
        http://www.gaffer.org/networking-for-game-programmers
        Author: Glenn Fiedler <gaffer@gaffer.org>
*/

#ifndef NET_H
#define NET_H

// platform detection

#define PLATFORM_WINDOWS  1
#define PLATFORM_MAC      2
#define PLATFORM_UNIX     3

#if defined(_WIN32)
#define PLATFORM PLATFORM_WINDOWS
#elif defined(__APPLE__)
#define PLATFORM PLATFORM_MAC
#else
#define PLATFORM PLATFORM_UNIX
#endif

#if PLATFORM == PLATFORM_WINDOWS

        #include <winsock2.h>
        #pragma comment( lib, "wsock32.lib" )

#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX

        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <fcntl.h>

#else

        #error unknown platform!

#endif

#include <assert.h>
#include <vector>
#include <map>
#include <stack>

namespace net
{
        // platform independent wait for n seconds

#if PLATFORM == PLATFORM_WINDOWS

        void wait( float seconds )
        {
                Sleep( (int) ( seconds * 1000.0f ) );
        }

#else

        #include <unistd.h>
        void wait( float seconds ) { usleep( (int) ( seconds * 1000000.0f ) ); }

#endif

#define MAX_PACKET_SIZE 512

        // internet address

        class Address
        {
        public:
       
                Address()
                {
                        address = 0;
                        port = 0;
                }
       
                Address( unsigned char a, unsigned char b, unsigned char c, unsigned char d, unsigned short port )
                {
                        this->address = ( a << 24 ) | ( b << 16 ) | ( c << 8 ) | d;
                        this->port = port;
                }
       
                Address( unsigned int address, unsigned short port )
                {
                        this->address = address;
                        this->port = port;
                }
       
                unsigned int GetAddress() const
                {
                        return address;
                }
       
                unsigned char GetA() const
                {
                        return ( unsigned char ) ( address >> 24 );
                }
       
                unsigned char GetB() const
                {
                        return ( unsigned char ) ( address >> 16 );
                }
       
                unsigned char GetC() const
                {
                        return ( unsigned char ) ( address >> 8 );
                }
       
                unsigned char GetD() const
                {
                        return ( unsigned char ) ( address );
                }
       
                unsigned short GetPort() const
                {
                        return port;
                }
       
                bool operator == ( const Address & other ) const
                {
                        return address == other.address && port == other.port;
                }
       
                bool operator != ( const Address & other ) const
                {
                        return ! ( *this == other );
                }
               
                bool operator < ( const Address & other ) const
                {
                        // note: this is so we can use address as a key in std::map
                        if ( address < other.address )
                                return true;
                        if ( address > other.address )
                                return false;
                        else
                                return port < other.port;
                }
       
        private:
       
                unsigned int address;
                unsigned short port;
        };

        // sockets

        inline bool InitializeSockets()
        {
                #if PLATFORM == PLATFORM_WINDOWS
            WSADATA WsaData;
                return WSAStartup( MAKEWORD(2,2), &WsaData ) == NO_ERROR;
                #else
                return true;
                #endif
        }

        inline void ShutdownSockets()
        {
                #if PLATFORM == PLATFORM_WINDOWS
                WSACleanup();
                #endif
        }

        class Socket
        {
        public:
       
                Socket()
                {
                        socket = 0;
                }
       
                ~Socket()
                {
                        Close();
                }
       
                bool Open( unsigned short port )
                {
                        assert( !IsOpen() );
               
                        // create socket

                        socket = ::socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );

                        if ( socket <= 0 )
                        {
                                printf( "failed to create socket\n" );
                                socket = 0;
                                return false;
                        }

                        // bind to port

                        sockaddr_in address;
                        address.sin_family = AF_INET;
                        address.sin_addr.s_addr = INADDR_ANY;
                        address.sin_port = htons( (unsigned short) port );
               
                        if ( bind( socket, (const sockaddr*) &address, sizeof(sockaddr_in) ) < 0 )
                        {
                                printf( "failed to bind socket\n" );
                                Close();
                                return false;
                        }

                        // set non-blocking io

                        #if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
               
                                int nonBlocking = 1;
                                if ( fcntl( socket, F_SETFL, O_NONBLOCK, nonBlocking ) == -1 )
                                {
                                        printf( "failed to set non-blocking socket\n" );
                                        Close();
                                        return false;
                                }
                       
                        #elif PLATFORM == PLATFORM_WINDOWS
               
                                DWORD nonBlocking = 1;
                                if ( ioctlsocket( socket, FIONBIO, &nonBlocking ) != 0 )
                                {
                                        printf( "failed to set non-blocking socket\n" );
                                        Close();
                                        return false;
                                }

                        #endif
               
                        return true;
                }
       
                void Close()
                {
                        if ( socket != 0 )
                        {
                                #if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
                                close( socket );
                                #elif PLATFORM == PLATFORM_WINDOWS
                                closesocket( socket );
                                #endif
                                socket = 0;
                        }
                }
       
                bool IsOpen() const
                {
                        return socket != 0;
                }
       
                bool Send( const Address & destination, const void * data, int size )
                {
                        assert( data );
                        assert( size > 0 );
               
                        if ( socket == 0 )
                                return false;
               
                        assert( destination.GetAddress() != 0 );
                        assert( destination.GetPort() != 0 );
               
                        sockaddr_in address;
                        address.sin_family = AF_INET;
                        address.sin_addr.s_addr = htonl( destination.GetAddress() );
                        address.sin_port = htons( (unsigned short) destination.GetPort() );

                        int sent_bytes = sendto( socket, (const char*)data, size, 0, (sockaddr*)&address, sizeof(sockaddr_in) );

                        return sent_bytes == size;
                }
       
                int Receive( Address & sender, void * data, int size )
                {
                        assert( data );
                        assert( size > 0 );
               
                        if ( socket == 0 )
                                return false;
                       
                        #if PLATFORM == PLATFORM_WINDOWS
                        typedef int socklen_t;
                        #endif
                       
                        sockaddr_in from;
                        socklen_t fromLength = sizeof( from );

                        int received_bytes = recvfrom( socket, (char*)data, size, 0, (sockaddr*)&from, &fromLength );

                        if ( received_bytes <= 0 )
                                return 0;

                        unsigned int address = ntohl( from.sin_addr.s_addr );
                        unsigned short port = ntohs( from.sin_port );

                        sender = Address( address, port );

                        return received_bytes;
                }
               
        private:
       
                int socket;
        };
       
        // connection
       
        class Connection
        {
        public:
               
                enum Mode
                {
                        None,       
                        Client,
                        Server
                };
               
                Connection( unsigned int protocolId, float timeout )
                {
                        this->protocolId = protocolId;
                        this->timeout = timeout;
                        mode = None;
                        running = false;
                        ClearData();
                }
               
                ~Connection()
                {
                        if ( running )
                                Stop();
                }
               
                bool Start( int port )
                {
                        assert( !running );
                        printf( "start connection on port %d\n", port );
                        if ( !socket.Open( port ) )
                                return false;
                        running = true;
                        return true;
                }
               
                void Stop()
                {
                        assert( running );
                        printf( "stop connection\n" );
                        ClearData();
                        socket.Close();
                        running = false;
                }
               
                void Listen()
                {
                        printf( "server listening for connection\n" );
                        ClearData();
                        mode = Server;
                        state = Listening;
                }
               
                void Connect( const Address & address )
                {
                        printf( "client connecting to %d.%d.%d.%d:%d\n",
                                address.GetA(), address.GetB(), address.GetC(), address.GetD(), address.GetPort() );
                        ClearData();
                        mode = Client;
                        state = Connecting;
                        this->address = address;
                }
               
                bool IsConnecting() const
                {
                        return state == Connecting;
                }
               
                bool ConnectFailed() const
                {
                        return state == ConnectFail;
                }
               
                bool IsConnected() const
                {
                        return state == Connected;
                }
               
                bool IsListening() const
                {
                        return state == Listening;
                }
               
                Mode GetMode() const
                {
                        return mode;
                }
               
                void Update( float deltaTime )
                {
                        assert( running );
                        timeoutAccumulator += deltaTime;
                        if ( timeoutAccumulator > timeout )
                        {
                                if ( state == Connecting )
                                {
                                        printf( "connect timed out\n" );
                                        ClearData();
                                        state = ConnectFail;
                                }
                                else if ( state == Connected )
                                {
                                        printf( "connection timed out\n" );
                                        ClearData();
                                        if ( state == Connecting )
                                                state = ConnectFail;
                                }
                        }
                }
               
                bool SendPacket( const unsigned char data[], int size )
                {
                        assert( running );
                        if ( address.GetAddress() == 0 )
                                return false;

                        if (size > MAX_PACKET_SIZE)
                        {
                                return false;
                        }
                        unsigned char packet[MAX_PACKET_SIZE + 4];
                        //unsigned char packet[size+4];

                        packet[0] = (unsigned char) ( protocolId >> 24 );
                        packet[1] = (unsigned char) ( ( protocolId >> 16 ) & 0xFF );
                        packet[2] = (unsigned char) ( ( protocolId >> 8 ) & 0xFF );
                        packet[3] = (unsigned char) ( ( protocolId ) & 0xFF );
                        memcpy( &packet[4], data, size );
                        return socket.Send( address, packet, size + 4 );
                }
               
                int ReceivePacket( unsigned char data[], int size )
                {
                        assert( running );
                        if (size > MAX_PACKET_SIZE)
                        {
                                return -1;
                        }
                        unsigned char packet[MAX_PACKET_SIZE + 4];
                        //unsigned char packet[size+4];
                        Address sender;
                        int bytes_read = socket.Receive( sender, packet, size + 4 );
                        if ( bytes_read == 0 )
                                return 0;
                        if ( bytes_read <= 4 )
                                return 0;
                        if ( packet[0] != (unsigned char) ( protocolId >> 24 ) ||
                                 packet[1] != (unsigned char) ( ( protocolId >> 16 ) & 0xFF ) ||
                                 packet[2] != (unsigned char) ( ( protocolId >> 8 ) & 0xFF ) ||
                                 packet[3] != (unsigned char) ( protocolId & 0xFF ) )
                                return 0;
                        if ( mode == Server && !IsConnected() )
                        {
                                printf( "server accepts connection from client %d.%d.%d.%d:%d\n",
                                        sender.GetA(), sender.GetB(), sender.GetC(), sender.GetD(), sender.GetPort() );
                                state = Connected;
                                address = sender;
                        }
                        if ( sender == address )
                        {
                                if ( mode == Client && state == Connecting )
                                {
                                        printf( "client completes connection with server\n" );
                                        state = Connected;
                                }
                                timeoutAccumulator = 0.0f;
                                memcpy( data, &packet[4], size - 4 );
                                return size - 4;
                        }
                        return 0;
                }
               
        protected:
               
                void ClearData()
                {
                        state = Disconnected;
                        timeoutAccumulator = 0.0f;
                        address = Address();
                }
       
        private:
               
                enum State
                {
                        Disconnected,
                        Listening,
                        Connecting,
                        ConnectFail,
                        Connected
                };

                unsigned int protocolId;
                float timeout;
               
                bool running;
                Mode mode;
                State state;
                Socket socket;
                float timeoutAccumulator;
                Address address;
        };
}

#endif

0

主题

17

帖子

19

积分

新手上路

Rank: 1

积分
19
发表于 2012-11-20 11:02:00 | 显示全部楼层

Re:MMOG网络同步算法揭秘(QQ幻想)

代码也不是我的,还是引用的那个老外的代码

58

主题

1437

帖子

2207

积分

金牌会员

Rank: 6Rank: 6

积分
2207
发表于 2012-11-20 16:09:00 | 显示全部楼层

Re: Re:MMOG网络同步算法揭秘(QQ幻想)

[em21]贴了个没头没脑的代码也不说清楚,我看到了timeoutAccumulator
我猜你想说的,发出封包如果等待超时不回复就算丢包。
这样还是不解决问题,你怎么判断发出去的包是丢了而不是对方关闭连接呢?
当你开始考虑这些现实问题细节,陷进泥坑的时候,等于最终还是要自己实现一个tcp...
udp当年推出来也不是为了更快,只是当年网络环境不好,如过用tcp相当于每个包都会超时。
udp在非常烂的网络环境下可适应性更高而已。
本来就是一个被滥用的东西,就像现在说汇编比C更好速度更快。
nut799: Re:MMOG网络同步算法揭秘(QQ幻想)

代码也不是我的,还是引用的那个老外的代码

0

主题

17

帖子

19

积分

新手上路

Rank: 1

积分
19
发表于 2012-11-21 13:35:00 | 显示全部楼层

Re:MMOG网络同步算法揭秘(QQ幻想)

我猜你想说的,发出封包如果等待超时不回复就算丢包。
额 是这样子,每个包会有个序号。
接收方接到包之后,会在回复的包里面把之前接收的包的序号带进去。
这样子发送发就知道自己之前发送的包 已经发送成功了。
超时是指一定时间内没有该包的发送成功标志回来。会重发。

这样还是不解决问题,你怎么判断发出去的包是丢了而不是对方关闭连接呢?

一般这个问题,都用心跳包来解决。


12

主题

378

帖子

466

积分

中级会员

Rank: 3Rank: 3

积分
466
发表于 2012-11-21 19:56:00 | 显示全部楼层

Re:MMOG网络同步算法揭秘(QQ幻想)

你发的代码根本不严谨,所以毫无参考意义[em21]

另外,想问下楼上,你所谓发心跳包的频率是如何确定的?用tcp还是udp?方向是如何确定的?

0

主题

14

帖子

112

积分

注册会员

Rank: 2

积分
112
发表于 2013-5-7 14:30:19 | 显示全部楼层
顶一下这个帖子

0

主题

1

帖子

6

积分

新手上路

Rank: 1

积分
6
发表于 2013-5-12 14:13:10 | 显示全部楼层
不得不说,WOW是TCP的。而且流畅与否跟UDP没有任何联系
顺便再说一句,你所处于的当前坐标信息非常重要,并不是随时可以舍弃的。
不过UDP还是有点用,用来同步不重要数据是可行的。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2025-2-26 18:48

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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