网络游戏由于对消息的响应速度有着比较高的要求,常常需要将tcp数据流分割成一个个的数据包。将数据流转换成数据包,常用的格式就是给数据包加一个长度组装在数据流中。
数据包长度的选择,你可以选择2个字节的长度也就是64K的字节信息表示一个包长,也可以选择4字节的包长作为长度的信息。
但是前面已经说过,由于游戏要求比较快速的响应,那么如果你允许一个tcp链接中插入一个太大的数据块,在网络比较脆弱的时候处理这个大块的时间就十分的长了,而且在业务的处理中,大块会阻塞信道,导致需要速度的操作得不到快色的响应,大块后面的数据都被延迟了。
在实际的业务中也有少许的场景是数据量比较大的,在这种场景中,我们最好是在业务的上层再添加一个协议,在具体的业务中去解析。比如查看游戏中的交易信息,如果交易信息很大,你可以先分割之后再在业务层去重新组合,你只需要在上层业务协议中加一个消息的长度即可。
也可以直接在长度 + 内容的基础上加一个大包的协议,具体可以是这种 所有的包都是id + 长度 + 内容,发送的时候将大包分割,接受的时候按照id重组,其实这样一来还不如在业务的内容中添加一个消息的长度,在业务的上层进行重组。
关于组包的思路:
这里skynet里面的代码可以给我们一个很好的启示:
#define MESSAGEPOOL 1023
struct message {
char * buffer;
int size;
struct message * next;
};
struct databuffer {
int header;
int offset;
int size;
struct message * head;
struct message * tail;
};
struct messagepool_list {
struct messagepool_list *next;
struct message pool[MESSAGEPOOL];
};
struct messagepool {
struct messagepool_list * pool;
struct message * freelist;
};
// use memset init struct
static void
messagepool_free(struct messagepool *pool) {
struct messagepool_list *p = pool->pool;
while(p) {
struct messagepool_list *tmp = p;
p=p->next;
skynet_free(tmp);
}
pool->pool = NULL;
pool->freelist = NULL;
}
static inline void
_return_message(struct databuffer *db, struct messagepool *mp) {
struct message *m = db->head;
if (m->next == NULL) {
assert(db->tail == m);
db->head = db->tail = NULL;
} else {
db->head = m->next;
}
skynet_free(m->buffer);
m->buffer = NULL;
m->size = 0;
m->next = mp->freelist;
mp->freelist = m;
}
static void
databuffer_read(struct databuffer *db, struct messagepool *mp, void * buffer, int sz) {
assert(db->size >= sz);
db->size -= sz;
for (;;) {
struct message *current = db->head;
int bsz = current->size - db->offset;
if (bsz > sz) {
memcpy(buffer, current->buffer + db->offset, sz);
db->offset += sz;
return;
}
if (bsz == sz) {
memcpy(buffer, current->buffer + db->offset, sz);
db->offset = 0;
_return_message(db, mp);
return;
} else {
memcpy(buffer, current->buffer + db->offset, bsz);
_return_message(db, mp);
db->offset = 0;
buffer+=bsz;
sz-=bsz;
}
}
}
static void
databuffer_push(struct databuffer *db, struct messagepool *mp, void *data, int sz) {
struct message * m;
if (mp->freelist) {
m = mp->freelist;
mp->freelist = m->next;
} else {
struct messagepool_list * mpl = skynet_malloc(sizeof(*mpl));
struct message * temp = mpl->pool;
int i;
for (i=1;i<MESSAGEPOOL;i++) {
temp[i].buffer = NULL;
temp[i].size = 0;
temp[i].next = &temp[i+1];
}
temp[MESSAGEPOOL-1].next = NULL;
mpl->next = mp->pool;
mp->pool = mpl;
m = &temp[0];
mp->freelist = &temp[1];
}
m->buffer = data;
m->size = sz;
m->next = NULL;
db->size += sz;
if (db->head == NULL) {
assert(db->tail == NULL);
db->head = db->tail = m;
} else {
db->tail->next