游戏服务器之连接接收

本文内容是游戏服务器之连接接收。

主要功能是接收和创建连接会话并添加到服务器被动连接线程池(到验证连接线程).功能的运行是在进程的主线程,

代码的实现是在服务器的基类。


1、服务器网络的初始化

(1)初始化epoll

(2)初始化接收socket

2、服务器循环,创建会话


1、服务器网络的初始化

(1)初始化epol 

volatile bool shutdown_server = false;//多线程使用的标识关闭网络服务

server_base::server_base(const std::string& s_name)
{
    sock_handler = -1;
    epoll_handler = epoll_create(1);
    assert(-1 != epoll_handler);
    id = 0;
    ServerType = 0;
    name = s_name;
    taskPool = NULL ;
}

(2)初始化监听socket

bool server_base::bind_socket(uint16 port)
{

  .....
    struct sockaddr_in addr;
    if (-1 != sock_handler) 
    {
        g_log->error("服务器可能已经初始化");;
        return false;
    }
(2-1)创建接收socket
//监听socket设置
    sock_handler = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (-1 == sock_handler) 
    {
        g_log->error("创建套接口失败");
        return false;
    }
(2-2)设置监听socket可重用
    //设置套接口为可重用状态
    int reuse = 1;
    if (-1 == ::setsockopt(sock_handler, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) 
    {
        g_log->error("不能设置套接口为可重用状态");
        TEMP_FAILURE_RETRY(::close(sock_handler));
        sock_handler = -1;
        return false;
    }
(2-3)设置监听socket发送接收缓冲(128k)
    //设置套接口发送接收缓冲(128k),并且服务器的必须在accept之前设置
    socklen_t window_size = 128 * 1024;
    if (-1 == ::setsockopt(sock_handler, SOL_SOCKET, SO_RCVBUF, &window_size, sizeof(window_size)))
    {
        TEMP_FAILURE_RETRY(::close(sock_handler));
        return false;
    }
    if (-1 == ::setsockopt(sock_handler, SOL_SOCKET, SO_SNDBUF, &window_size, sizeof(window_size)))
    {
        TEMP_FAILURE_RETRY(::close(sock_handler));
        return false;
    }

(2-4)监听socket绑定地址和端口

//设置网络服务绑定的地址和端口
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(port);
    int retcode = ::bind(sock_handler, (struct sockaddr *) &addr, sizeof(addr));
    if (-1 == retcode) 
    {
        g_log->error("不能绑定服务器端口:%u",port);
        TEMP_FAILURE_RETRY(::close(sock_handler));
        sock_handler = -1;
        return false;
    }
(2-5)监听socket开始监听
    retcode = ::listen(sock_handler, MAX_WAITQUEUE);
    if (-1 == retcode) 
    {
        g_log->error("监听套接口失败");
        TEMP_FAILURE_RETRY(::close(sock_handler));
        sock_handler = -1;
        return false;
    }

(2-6)把监听socket加入到epoll的监听事件里

//把监听socket加入到epoll的监听事件里
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.ptr = NULL;
    assert(0 == epoll_ctl(epoll_handler, EPOLL_CTL_ADD, sock_handler, &ev));
    g_log->info("绑定服务器 %s:%u 成功", this->name.c_str(), port);
    return true;
}

2、服务器网络接收功能

(1)服务器循环

//服务器的循环。
void server_base::loop()
{

//初始化服务器,并等待依赖的服务器都启动后,中心服务器发来的可以启动该消息后,开始进入服务器循环
    if(init() && start_ok())
    {
        while(!shutdown_server)
        {
            if (!service_callback())//一直接收连接 
            {   
                break;
            }   
        }
    }
    else
    {
        g_log->error("服务器还没初始化完毕");
    }
   
    final();
}
(2)接收连接
//回调处理网络连接事件
//处理接收socket,有连接事件的话就建立新的 连接session
bool server_base::service_callback()
{
   struct sockaddr_in addr;
    bzero(&addr, sizeof(struct sockaddr_in));
    int retcode = -1; 
    socklen_t len = sizeof(struct sockaddr_in);
    struct epoll_event ev; 
    int rc = epoll_wait(epoll_handler, &ev, 1, T_MSEC);// epoll描述符来监听事件(每次只监听一个事件)
    if (1 == rc && (ev.events & EPOLLIN))//有接收事件才接收监听socket的连接
    {
        retcode = TEMP_FAILURE_RETRY(::accept(sock_handler, (struct sockaddr *)&addr, &len));//如果
    }
   if (retcode >= 0) 
 {
  newTCPTask(retcode, &addr);//把接收的socket来建立新的会话
 }
 return true;
}

btw:

TEMP_FAILURE_RETRY 是一直尝试执行指定的函数,

如果返回-1并且errno被设置为中断(就是收到的是中断信号)就仍继续该表达式,

使用场景是对于处理中断信号会返回-1的表达式,中断过后可以继续该表达式的逻辑(这里处理的是系统函数::accept)

/* Evaluate EXPRESSION, and repeat as long as it returns -1 with `errno'
   set to EINTR.  */

# define TEMP_FAILURE_RETRY(expression) \
  (__extension__      \
    ({ long int __result;      \
       do __result = (long int) (expression);      \
       while (__result == -1L && errno == EINTR);      \
       __result; }))
#endif


(3)创建连接会话

创建连接会话后会添加到被动连接线程池的验证线程里。

例如,在场景服务器创建会话(在子类实现创建新的会话,场景服务器继承服务器基类)。

void scene_server::newTCPTask(const int sock, const struct sockaddr_in *addr)
{
//g_log->debug(__PRETTY_FUNCTION__);
scene_session *tcpTask = new scene_session(sock, addr);
if(!tcpTask)
//内存不足,直接关闭连接
TEMP_FAILURE_RETRY(::close(sock));
else if(!taskPool->addVerify(tcpTask))//得到了一个正确连接,添加到验证队列中(验证队列在验证线程中,添加时要加锁)
{
SAFE_DELETE(tcpTask);
}
}

//添加到验证队列

bool ::addVerify(tcp_session *task)
{
verify_thread *pThread = verifyThreads.getOne();
if(pThread)
{
// state_notuse -> state_verify
/*
* cjy
* 先设置状态再添加容器
*/
task->getNextState();
pThread->add(task);
}
else
{
g_log->error("没有找到验证线程添加任务");
}
return true;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值