[原创]一点一滴学习网络游戏开发,第一章节:初步了解ACE和SOCKET

来源:互联网 发布:数据挖掘算法pdf 编辑:程序博客网 时间:2024/06/05 19:44

要说起Ace框架,它的跨平台性,效率还有开发的便捷使我选择Ace框架作为服务器端的首选。当然对于我坚持以C++为主的人来说,我就减少了学习和熟悉Java语言和类库。Java中有非常多适合做服务器端的开源利器,比如Mina就非常适合做服务器端框架。闲话少说我们先来使用Ace制作一个简单的C/S实例。当然Ace的安装也很简单参考网上的教程即可。

 

服务器端代码:

ServerHandler.h

 

#include"ace/Svc_Handler.h"

#include"ace/SOCK_Stream.h"       

 

#defineMAXHOSTNAMELEN 100

classServerHandler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>

{

private:

           ACE_TCHAR peer_name[MAXHOSTNAMELEN];

 

public:

  virtualint handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE);

 

  virtualint open (void *acceptor_or_connector = 0);

 

  virtualint close (u_long flags = 0);

};

#endif

 

从头文件定义可以看出,当一个新的套接字连接服务器时,就会执行open函数,open函数是ACE_Svc_Handler的虚函数,而ServerHandler重写了该函数。你可以在一个新的连接加入服务器时进行某些操作,比如将玩家加入服务器中的玩家列表中。

 

ServerHandler.cpp

 

#include"stdafx.h"

#include"ServerHandler.h"

 

intServerHandler::handle_input (ACE_HANDLE handle)

{

const int INPUT_SIZE = 4096;

char buffer[INPUT_SIZE];

memset(buffer, 0, INPUT_SIZE);

int recv_cnt = this->peer().recv( buffer,sizeof(buffer));

if (recv_cnt <= 0 )

{

           printf("Connectionclose\n");

           return -1;

}

 

return 0;

}

 

intServerHandler::close (u_long flags){

if (ACE_Svc_Handler::close(flags) == -1){

           return -1;

}

 

/*

*/

 

return 0;

}

 

intServerHandler::open (void *acceptor_or_connector){

if(ACE_Svc_Handler::open(acceptor_or_connector) == -1){

           return -1;

}

 

return 0;

}

以上是具体函数的执行代码,你可以打印代码去查看或体验这个框架。

 

GameServer.cpp

 

//GameServer.cpp : 定义控制台应用程序的入口点。

//

 

#include"stdafx.h"

#include"ServerHandler.h"

#include"ace/Reactor.h"

#include"ace/Acceptor.h"

#include"ace/SOCK_Acceptor.h"

 

int_tmain(int argc, _TCHAR* argv[])

{

   WORDwVersionRequested;

       WSADATA wsaData; 

       int err; 

         

       wVersionRequested = MAKEWORD( 2, 2); 

         

       err = WSAStartup( wVersionRequested,&wsaData ); 

       if ( err != 0 ) { 

              return 0; 

       } 

         

       if ( LOBYTE( wsaData.wVersion ) != 2|| 

                      HIBYTE( wsaData.wVersion) != 2 ) { 

              WSACleanup( ); 

              return 0; 

       } 

  

int port = 50001;

ACE_INET_Addr port_to_accept(port);

ACE_Acceptor<ServerHandler,ACE_SOCK_ACCEPTOR> server;

if(server.open(port_to_accept) == -1)

{

           return -1;

}

ACE_Reactor::instance()->run_reactor_event_loop();

 

return 0; 

}

 

//上面就是Main函数了,只需要定义网络地址和端口,不需要知道太多底层的socket原理就可以编写一个简单的服务器了。

 

下面来探讨下客户端:

对于客户端,因为是游戏开发所以有必要选择一个游戏框架。我一般都使用cocos2d-x。在使用cocos2d-x中的时候为了方便跨平台我都会使用ODSocket.下面是ODSocket的代码。

 

ODSocket.h

 

/*

 * define file about portable socket class.

 * description:this sock is suit both windowsand linux

 * design:odison

 * e-mail:odison@126.com>

 *

 */

 

#ifndef_ODSOCKET_H_

#define_ODSOCKET_H_

 

#ifdefWIN32

#include <winsock2.h>

typedef int                                    socklen_t;

#else

#include <sys/socket.h>

#include <netinet/in.h>

#include <netdb.h>

#include <fcntl.h>

#include <unistd.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <arpa/inet.h>

typedef int                                    SOCKET;

 

//#pragma region define win32 const variable inlinux

#define INVALID_SOCKET        -1

#define SOCKET_ERROR -1

//#pragma endregion

#endif

 

 

classODSocket {

 

public:

ODSocket(SOCKET sock = INVALID_SOCKET);

~ODSocket();

 

// Create socket object for snd/recv data

bool Create(int af, int type, int protocol =0);

 

// Connect socket

bool Connect(const char* ip, unsigned shortport);

//#region server

// Bind socket

bool Bind(unsigned short port);

 

// Listen socket

bool Listen(int backlog = 5);

 

// Accept socket

bool Accept(ODSocket& s, char* fromip =NULL);

//#endregion

int Select();

// Send socket

int Send(const char* buf, int len, int flags =0);

 

// Recv socket

int Recv(char* buf, int len, int flags = 0);

 

// Close socket

int Close();

 

// Get errno

int GetError();

 

//#pragma region just for win32

// Init winsock DLL

static int Init();

// Clean winsock DLL

static int Clean();

//#pragma endregion

 

// Domain parse

static bool DnsParse(const char* domain, char*ip);

 

ODSocket& operator = (SOCKET s);

 

operator SOCKET ();

 

protected:

SOCKET m_sock;

fd_set fdR;

};

 

#endif

 

ODSocket.cpp

 

#include<stdio.h>

#include"ODSocket.h"

 

#ifdefWIN32

#pragmacomment(lib, "wsock32")

#endif

 

ODSocket::ODSocket(SOCKETsock) {

m_sock = sock;

}

 

ODSocket::~ODSocket(){

}

 

intODSocket::Init() {

#ifdefWIN32


 

 typedefstruct WSAData {

 WORDwVersion;                                                                    //winsockversion

 WORDwHighVersion;                                                           //Thehighest version of the Windows Sockets specification that the Ws2_32.dll cansupport

 charszDescription[WSADESCRIPTION_LEN+1];

 charszSystemStatus[WSASYSSTATUS_LEN+1];

 unsignedshort iMaxSockets;

 unsignedshort iMaxUdpDg;

 char FAR* lpVendorInfo;

 }WSADATA, *LPWSADATA;


WSADATA wsaData;

//#define MAKEWORD(a,b) ((WORD) (((BYTE) (a)) |((WORD) ((BYTE) (b))) << 8))

WORD version = MAKEWORD(2, 0);

int ret = WSAStartup(version, &wsaData);//win sock start up

if (ret) {

//                 cerr << "Initilizewinsock error !" << endl;

           return -1;

}

#endif

 

return 0;

}

//this isjust for windows

intODSocket::Clean() {

#ifdefWIN32

return (WSACleanup());

#endif

return 0;

}

 

ODSocket&ODSocket::operator =(SOCKET s) {

m_sock = s;

return (*this);

}

 

ODSocket::operatorSOCKET() {

return m_sock;

}

//createa socket object win/lin is the same

// af:

boolODSocket::Create(int af, int type, int protocol) {

m_sock = socket(af, type, protocol);

if (m_sock == INVALID_SOCKET) {

           return false;

}

return true;

}

 

boolODSocket::Connect(const char* ip, unsigned short port) {

struct sockaddr_in svraddr;

svraddr.sin_family = AF_INET;

svraddr.sin_addr.s_addr = inet_addr(ip);

svraddr.sin_port = htons(port);

int ret = connect(m_sock, (struct sockaddr*)&svraddr, sizeof(svraddr));

if (ret == SOCKET_ERROR) {

           return false;

}

return true;

}

 

boolODSocket::Bind(unsigned short port) {

struct sockaddr_in svraddr;

svraddr.sin_family = AF_INET;

svraddr.sin_addr.s_addr = INADDR_ANY;

svraddr.sin_port = htons(port);

 

int opt = 1;

if (setsockopt(m_sock, SOL_SOCKET,SO_REUSEADDR, (char*) &opt, sizeof(opt))

                    < 0)

           return false;

 

int ret = bind(m_sock, (struct sockaddr*)&svraddr, sizeof(svraddr));

if (ret == SOCKET_ERROR) {

           return false;

}

return true;

}

//forserver

boolODSocket::Listen(int backlog) {

int ret = listen(m_sock, backlog);

if (ret == SOCKET_ERROR) {

           return false;

}

return true;

}

 

boolODSocket::Accept(ODSocket& s, char* fromip) {

struct sockaddr_in cliaddr;

socklen_t addrlen = sizeof(cliaddr);

SOCKET sock = accept(m_sock, (struct sockaddr*)&cliaddr, &addrlen);

if (sock == SOCKET_ERROR) {

           return false;

}

 

s = sock;

if (fromip != NULL)

           sprintf(fromip, "%s",inet_ntoa(cliaddr.sin_addr));

 

return true;

}

 

intODSocket::Select(){

FD_ZERO(&fdR);

FD_SET(m_sock, &fdR);

struct timeval mytimeout;

mytimeout.tv_sec=3;

mytimeout.tv_usec=0;

int result=select(m_sock,&fdR,NULL,NULL,NULL);

// 第一个参数是 0 和 sockfd 中的最大值加一

// 第二个参数是 读集, 也就是 sockset

// 第三, 四个参数是写集和异常集, 在本程序中都为空

// 第五个参数是超时时间, 即在指定时间内仍没有可读,则出错

 

//case -1:                            error handled by u;

if(result==-1){

           return -1;

}

/*else if(result==0){

return -4;

}*/

else {

           if(FD_ISSET(m_sock,&fdR)){

                    return -2;

           }else {

                    return -3;

           }

}

}

 

 

 

intODSocket::Send(const char* buf, int len, int flags) {

int bytes;

int count = 0;

 

while (count < len) {

           const char* a= buf + count;

           bytes = send(m_sock, buf + count, len- count, flags);

           if (bytes == -1 || bytes == 0)

                    return -1;

           count += bytes;

}

 

return count;

}

 

intODSocket::Recv(char* buf, int len, int flags) {

return (recv(m_sock, buf, len, flags));

}

 

intODSocket::Close() {

#ifdefWIN32

return (closesocket(m_sock));

#else

return (close(m_sock));

#endif

}

 

intODSocket::GetError() {

#ifdefWIN32

return (WSAGetLastError());

#else

return -1;

#endif

}

 

bool ODSocket::DnsParse(constchar* domain, char* ip) {

struct hostent* p;

if ((p = gethostbyname(domain)) == NULL)

           return false;

 

sprintf(ip, "%u.%u.%u.%u", (unsignedchar) p->h_addr_list[0][0],

                    (unsigned char)p->h_addr_list[0][1],

                    (unsigned char) p->h_addr_list[0][2],

                    (unsigned char)p->h_addr_list[0][3]);

 

return true;

}

 

接下来就是编写ODSocket连接服务器的代码:

ODSocket *socket = new ODSocket();

socket->Init();

socket ->Create(AF_INET,SOCK_STREAM,0);

socket->Connect("127.0.0.1",50001);

 

你通过先运行服务器端再运行客户端就可以看到效果了。另外你可以设置断点可以更好的明白Ace框架和ODSocket的运行机制。

 

0 0
原创粉丝点击