游戏服务端之C++网络库对外接口

大多数的情况下,游戏的服务端都是在Linux下运行,但是Linux下做开发C/C++的开发相对来说是比较困难的。所以一般来说,游戏的服务端都是做成可移植的,这样方便在Windows下做开发。也就是说,服务端既可在Windows下运行,也可以在Linux下运行。说了这么多,看似与网络链接半毛钱关系都没有。
其实不是这样的,就像Lua一样,既可以在Windows下调用它,也可以在Linux调用它,关键就库不同。熟悉网络编程的都知道,绝大多数的网络API在不同的平台调用是不同的。那如何做到可移植呢?其实无论你选择的网络模型是IOCP、select还是epoll。我们使用的时候只关心Accept(谁连接到服务端?)、Receive(怎样接收消息?)和Send(怎样发送消息?)。也就是说我们只需要3个接口。Send的封装可以写进库的源文件中,我们只需单例调用它就可以了。但是如果Accept和Receive写到了源文件中,那我要怎样调用呢?我要什么时候调用呢?到这里感觉好像很困难!!

gamesocket.h

#ifndef GAMESOCKET_H  
#define GAMESOCKET_H  
  
#include <stdio.h>  
#include <stdlib.h>  
#include <errno.h>  
#include <string.h>  
#include <sys/types.h>  
#include <netinet/in.h>  
#include <sys/socket.h>  
#include <sys/wait.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <fcntl.h>  
#include <sys/epoll.h>  
#include <sys/time.h>  
#include <sys/resource.h>  
#include <list>  
#include "luaengine.h"  
  
using namespace std;  
  
  
#define MAXBUF 4096             //4KB may be enough  
#define MAXEPOLLSIZE 2048  
  
  
  
  
class GameSocket  
{  
public:  
  
  
    static GameSocket& Instance()  
    {  
        static GameSocket instance;  
        return instance;  
    }  
    ~GameSocket();  
    bool    Init();  
    int     Accept(int userID);  
    int     Lose(int userID);  
    int     Listen();  
    int     Recv(int userID, char msg[MAXBUF]);  
    int     Send(int userID, char msg[], short length);  
private:  
    GameSocket();  
    int SetNonBlocking(int socket_fd);  
    int m_epfd;  
    int m_listener;  
    struct sockaddr_in m_their_addr;  
    struct epoll_event ev;  
  
};  
  
#endif // SOCKET_H 


现在要将这个类封装成静态库,而Accept和Recv是被动触发的,是根据监听到的信息(Listen)而触发的。

gamesocket.cpp就像上面的代码一个,在Listen会调用下面的代码,但是在gamesocket.cpp并没有对Accept和Recv的实现,其实这是对外接口的关键。

    #include "gamesocket.h"  
      
      
      
    GameSocket::GameSocket()  
    {  
        Init();  
    }  
      
    GameSocket::~GameSocket()  
    {  
        close(m_listener);  
    }  
      
    /* 
       setnonblocking - 设置هڈ¥وں„ن¸؛é‌‍éک»ه،‍و–¹ه¼ڈ 
       */  
    int GameSocket::SetNonBlocking(int socket_fd)  
    {  
        if (fcntl(socket_fd, F_SETFL, fcntl(socket_fd, F_GETFL, 0)|O_NONBLOCK) == -1)  
        {  
            return -1;  
        }  
        return 0;  
    }  
      
      
      
    bool GameSocket::Init()  
    {  
        LuaEngine lua;  
        int lisnum ;  
        int port ;  
      
      
        lua.LoadLuaFile("Config/socket.lua");  
        lua.GetGlobalProc("SocketConfig",0,2);  
      
        lua.GetResult(-1,&lisnum,LuaEngine::INTEGER);  
        lua.GetResult(-2,&port,LuaEngine::INTEGER);  
      
        struct sockaddr_in my_addr;  
        struct rlimit rt;  
      
         /* 设置و¯ڈن¸ھè؟›ç¨‹ه…پ许و‰“ه¼€çڑ„وœ€ه¤§و–‡ن»¶و•° */  
        rt.rlim_max = rt.rlim_cur = MAXEPOLLSIZE;  
        if (setrlimit(RLIMIT_NOFILE, &rt) == -1)  
        {  
            perror("setrlimit");  
            exit(1);  
        }  
        else  
        {  
            printf("Set System Argument Successï¼پ\n");  
        }  
        /* ه¼€هگ?socket 监هگ¬ */  
        if ((m_listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)  
        {  
            perror("socket create fail\n");  
            exit(1);  
        }  
        else  
        {  
            printf("socket create success!\n");  
        }  
        SetNonBlocking(m_listener);  
        bzero(&my_addr, sizeof(my_addr));  
        my_addr.sin_family = PF_INET;  
        my_addr.sin_port = htons(port);  
        my_addr.sin_addr.s_addr = INADDR_ANY;  
        if (bind(m_listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)  
        {  
            perror("bind");  
            exit(1);  
        }  
        else  
        {  
            printf("Bind ip address and port success\n");  
        }  
        if (listen(m_listener, lisnum) == -1)  
        {  
            perror("socket listen\n");  
            exit(1);  
        }  
        else  
        {  
            printf("socket listen success\n");  
        }  
      
        m_epfd = epoll_create(MAXEPOLLSIZE);  
      
        ev.events = EPOLLIN | EPOLLET;  
        ev.data.fd = m_listener;  
        if (epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_listener, &ev) < 0)  
        {  
            fprintf(stderr, "epoll set insertion error: fd=%d\n", m_listener);  
            return -1;  
        }  
        else  
        {  
            printf("add listener to epoll success\n");  
        }  
    }  
      
      
      
    int GameSocket::Listen()  
    {  
      
            static int  new_fd;  
            static int  nfds;  
            static int  ret;  
            static int  curfds = 1;  
            static socklen_t len;  
            static struct sockaddr_in their_addr;  
            static struct epoll_event events[MAXEPOLLSIZE];  
            static struct epoll_event ev;  
      
            char recv_buf[MAXBUF];  
            nfds = epoll_wait(m_epfd, events, curfds, -1);  
            if (nfds == -1)  
            {  
                 perror("epoll_wait");  
                 return -1;  
            }  
      
            for (int n = 0; n < nfds; ++n)  
            {  
                if (events[n].data.fd == m_listener)  
                {  
                    new_fd = accept(m_listener, (struct sockaddr *) &their_addr,&len);  
                    if(new_fd < 0)  
                    {  
                        printf("link error\n");  
                    }  
      
                    if (new_fd < 0)  
                    {  
                         perror("accept");  
                         continue;  
                    }  
                    else  
                    {  
                        printf("connect from %s,the port is %d,the fd is %d\n",inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);  
                    }  
                    SetNonBlocking(new_fd);  
                    ev.events = EPOLLIN | EPOLLET;  
                    ev.data.fd = new_fd;  
                    if (epoll_ctl(m_epfd, EPOLL_CTL_ADD, new_fd, &ev) < 0)  
                    {  
                         fprintf(stderr, "add socket %d to epoll fail ,%s\n",new_fd, strerror(errno));  
                         continue;  
                    }  
                    else  
                    {  
                        this->Accept(new_fd);  
                    }  
                    curfds++;  
                }  
                else if (events[n].events & EPOLLIN)  
                {  
                    ret = recv(events[n].data.fd, recv_buf, MAXBUF, 0);  
                    if (ret < 1 && errno != 11)  
                    {  
                         epoll_ctl(m_epfd, EPOLL_CTL_DEL, events[n].data.fd, &ev);  
                         curfds--;  
                    }  
                    else  
                    {  
                        this->Recv(events[n].data.fd, recv_buf);  
                    }  
                }  
            }  
    }  
      
    int GameSocket::Send( int userID, char msg[] ,short length)  
    {  
        if(send(userID,msg,length,0) == -1)  
        {  
            printf("%d :msg can not be send\n",userID);  
            return -1;  
        }  
        return 1;  
    }  

this->Accept(new_fd);
this->Recv(events[n].data.fd, recv_buf);
如果在gamesocket.cpp对Accept和Recv方法实现了,那在调用该库的时候,我们根本不知道怎样去调用Accept和Recv。所以只有Accept和Recv在库外实现,才可能去调用这两个方法。现在将两个方法的实现写在了interfacce.cpp中。

interface.cpp

    #include "gamesocket.h"  
      
    int GameSocket::Accept(int userID)  
    {  
        printf("Accept userID = %d\n",userID);  
        return 1;  
    }  
      
    int GameSocket::Recv(int userID, char msg[MAXBUF])  
    {  
        printf("receive a msg\n");  
        printf("%s\n",msg);  
      
        return 1;  
    }  
      
    int GameSocket::Lose(int userID)  
    {  
        printf("Lose userID = %d\n",userID);  
        exit(0);  
        return 1;  
    }  

执行结果:


PS:服务端都是消息响应的,也就是说,当我们获得消息(Recv)的时候,我们可以对下面的逻辑做多线程或者多进程的操作。

原文地址:http://blog.csdn.net/yitouhan/article/details/17252095

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值