多核的网络游戏服务器开发 之 网络库(一)
来源:互联网 发布:微信 代理服务器 知乎 编辑:程序博客网 时间:2024/04/28 03:39
处理数千连接的网络服务器,需要一个强大的服务器体系,我们大概可以采用下面几种方式:
- 多线程的阻塞方式,每个连接一个线程,阻塞接收和发送数据
- 单线程的轮询方式,采用非阻塞的网络接口,轮流查询每个连接是否有数据
- 线程池方式,采用某种IO机制,通知特定的连接上是否有数据,或者是否ready,然后由指定的可用线程进行处理
一般而言,采用第三种方式是效率最高的选择,在通知机制上,有跨平台的 select,windows平台下可用的IO完成端口,linux 平台可用的 epoll,freeBSD可用的 kqueue。
无论采用何种通知机制,我们都需要一个缓冲区临时存放发送/接收的数据,因为网络套接字状态,包括是否可用,数据是否发送完成等等,是由网络状况决定的,和应用程序逻辑注定是异步的,也就是说,应用程序逻辑不能等待套接字可用之后再向套接字发送数据,套接字上数据到达之后,也不能立刻打断应用程序的处理。
我们需要一个内部的缓冲区存放发送和接收的数据,应用程序逻辑需要发送的时候,直接调用发送的函数并立刻返回,网络库负责缓存数据并在套接字可用的时候进行发送,应用程序在合适的时候从网络库读取数据并立刻返回。
所以缓冲区就成了我们算法的第一个核心了
对于多个线程可能要同时访问的缓冲区,我们的第一反应是使用互斥量来保护,但是实际上,对于大多数受限制的应用场景来说,锁不是必须的。
首先我们需要一个限制,缓冲区的大小是固定的,对于网络连接来说这个需求是合理的,因为如果缓冲区内部的数据是在不断的写入和取出,如果缓冲区不够,表示太多的数据没有被处理,那么肯定是出现了问题(比如说非法连接的攻击),这种情况下,断开连接往往是一个合理的选择。
其次,写入线程和读取线程是有限的,这个在我们的例子中也能得到满足,因为大多数情况下,一个线程来处理一个网络连接的数据是合理的。
那么,在大小固定,单个写入者和单个读取者的情况下,下面的算法可以不需要锁
struct zbuf {
int size; //缓冲区的大小
int start; //开始的位置
int end; //结束的位置
};
使用这个结构描述固定大小的缓冲区,增加数据的时候,仅修改end 变量,取出数据的时候,仅修改start变量,对于单个读和单个写线程的情况,不需要锁。
int zbuf_push(struct zbuf *buf, const char *data, int size) { //增加指定大小的缓冲区,适用于单个writer的情况
int old_end, new_end;
int length = _zbuf_length(buf);
if(buf->size - length < size) return -1;
_zbuf_put(buf, buf->end, (char *)data, size);
new_end = buf->end + size;
if(new_end >= buf->size) new_end -= buf->size;
old_end = buf->end;
buf->end = new_end;
return old_end;
}
int zbuf_pop(struct zbuf *buf, char *data, int size) { //弹出指定大小的数据,size为 0 表示弹出全部数据
int length = _zbuf_length(buf);
if(length >= size) {
if(size == 0) size = length;
_zbuf_get(buf, buf->start, data, size);
buf->start += size;
if(buf->start >= buf->size) buf->start -= buf->size;
return size;
}
return 0;
}
其中, _zbuf_length 获取可用数据的长度:
__inline int _zbuf_length(struct zbuf *buf) {
int length;
if(buf->end >= buf->start) length = buf->end - buf->start;
else length = buf->size - buf->end + buf->start;
return length;
}
_zbuf_put 写入数据,需要处理回绕
void _zbuf_put(struct zbuf *buf, int pos, char *data, int size) { //拷贝数据到 zbuf 的 pos 位置处,处理回绕
int copy_size = buf->size - pos;
if(copy_size >= size) memcpy(BUF_PTR(buf, pos), data, size);
else {
memcpy(BUF_PTR(buf, pos), data, copy_size);
size -= copy_size;
memcpy(BUF_PTR(buf, 0), data + copy_size, size);
}
}
_zbuf_get 拷贝数据(需要处理回绕的情况)
void _zbuf_get(struct zbuf *buf, int pos, char *data, int size) {
int copy_size = buf->size - pos;
if(copy_size >= size) memcpy(data, BUF_PTR(buf, pos), size);
else {
memcpy(data, BUF_PTR(buf, pos), copy_size);
size -= copy_size;
memcpy(data + copy_size, BUF_PTR(buf, 0), size);
}
}
- 多核的网络游戏服务器开发 之 网络库(一)
- 多核的网络游戏服务器开发 之 网络库(二)
- 多核的网络游戏服务器开发 之 网络库(三)
- 多核的网络游戏服务器开发 之 设计目标
- 网络游戏服务器开发(一)
- 2D网络游戏开发(网络篇)(一)
- 2D网络游戏开发(网络篇)(一)
- 网络游戏服务器端开发心得之网络
- 大型网络游戏服务器的框架设计(一)
- 网络游戏服务器构架设计(一):前言
- 网络游戏服务器构架设计(一):前言
- 网络游戏服务器构架设计(一):前言
- 网络游戏服务器构架设计(一):前言
- 网络游戏服务器构架设计(一):前言
- 网络游戏服务器构架设计(一):前言
- 网络游戏服务器构架设计(一):前言
- 网络游戏服务器构架设计(一):前言
- 网络游戏服务器构架设计(一):前言
- 浅析OGG VORBIS
- GridView修改表头,并导出Excel(C#版)
- 超多好教程,绝对有你想要的.
- c# 经典试题
- Linux学习
- 多核的网络游戏服务器开发 之 网络库(一)
- 复方配伍研究——中药新药研发的切入点
- SQLServer创建视图
- where条件对查询的影响
- PHP 表单提交
- reactos操作系统实现(191)
- emule的编译与运行
- SQL Server2000 无法打开企业管理器解决办法
- [Python -and or ]Python 中 and or 之 我的理解