libuv在cocos2d-x中的使用

libuv经过Node.js的实践和应用,已经证明非常之成熟,本来之前项目用的是这个:clsocket https://github.com/DFHack/clsocket  当初选它的主要原因是它支持Windows、Linux、Mac OSX(我猜测的),但致命的缺点就是仅支持阻塞的TCP,这样就会导致一个问题,在连接游戏服务器、聊天服务器的时候游戏主界面会直接被卡死,等连接成功后才能恢复正常。而LuaSocket之前游戏也替换过,发现的问题主要是依赖lua的循环检测是否有新的数据(定时器),从而导致明显的界面延时。Cocos2d-x 3.x版本因为性能大幅提升,似乎此问题感受并不明显,而我们因为项目历史明显,lua 与 C++结合的很死,本身跑起来就一卡一卡的。

 

当然还有很多优秀的C++ TCP网络库,不过大部分似乎写的时候就只准备支持Linux/Unix,压根就没想支持Windows。而我们开发人员首先肯定是先在Windows下进行开发,神马?用Mac,公司连给iMac换个512G的SSD审批就很难,就别做梦了。自己出钱或者自带mac笔记本行么?不行!不允许使用外置USB,想用吗?走个OA单申请一下,一般开发人员的申请是直接被拒绝的,主程除外(好吧,我算比较幸运的 - “主程”)

 

吐槽归吐槽,活还是要干活的滴。libuv在实际使用中我发现的几个问题,如果连接socket时后台主动断开连接,那么后台最后发送出来的消息有可能会接收不到(概率性的,解决方法就是让后台发送消息完之后延时几秒再关闭socket连接)。iOS设备在关闭电源后,socket立马就断掉了,游戏从后台切换到前台时需要能自动重连一次。而libuv因为本身是用纯C实现的,它的回调方法基本上都是static函数,用C++封装的话有点小麻烦,网上也有人用C++11封装的比较好,可惜我使用的NDK版本比较低,支持不了C++11的特性只好放弃,但人家的思路还是可以借鉴的,觉得很赞。

 

以客户端为例,先了解下libuv的基本使用,示例来自gist (所有的libuv函数都以uv_开头)

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <uv.h> #define log(x) printf("%s\n", x); uv_loop_t *loop; void on_connect(uv_connect_t *req, int status);void on_write_end(uv_write_t *req, int status);uv_buf_t alloc_buffer(uv_handle_t *handle, size_t suggested_size);void echo_read(uv_stream_t *server, ssize_t nread, uv_buf_t buf);

// サーバからのレスポンスを表示void echo_read(uv_stream_t *server, ssize_t nread, uv_buf_t buf) {  if (nread == -1) {    fprintf(stderr, "error echo_read");    return;  }

  // 結果を buf から取得して表示  printf("result: %s\n", buf.base);}

// suggeseted_size で渡された領域を確保uv_buf_t alloc_buffer(uv_handle_t *handle, size_t suggested_size) {  // 読み込みのためのバッファを、サジェストされたサイズで確保  return uv_buf_init((char*) malloc(suggested_size), suggested_size);}

// サーバへデータ送信後, サーバからのレスポンスを読み込むvoid on_write_end(uv_write_t *req, int status) {  if (status == -1) {    fprintf(stderr, "error on_write_end");    return;  }

  // 書き込みが終わったら、すぐに読み込みを開始  uv_read_start(req->handle, alloc_buffer, echo_read);}

// サーバとの接続を確立後, サーバに文字列を送信void on_connect(uv_connect_t *req, int status) {  if (status == -1) {    fprintf(stderr, "error on_write_end");    return;  }

  // 送信メッセージを登録  char *message = "hello.txt";  int len = strlen(message);

  /** これだとセグフォ   * uv_buf_t buf[1];   * buf[0].len = len;   * buf[0].base = message;   */

  // 送信データ用のバッファ  char buffer[100];  uv_buf_t buf = uv_buf_init(buffer, sizeof(buffer));

  // 送信データを載せる  buf.len = len;  buf.base = message;

  // ハンドルを取得  uv_stream_t* tcp = req->handle;

  // 書き込み用構造体  uv_write_t write_req;   int buf_count = 1;  // 書き込み  uv_write(&write_req, tcp, &buf, buf_count, on_write_end);} int main(void) {  // loop 生成  loop = uv_default_loop();

  // Network I/O の構造体  uv_tcp_t client;

  // loop への登録  uv_tcp_init(loop, &client);

  // アドレスの取得  struct sockaddr_in req_addr = uv_ip4_addr("127.0.0.1", 7000);

  // TCP コネクション用の構造体  uv_connect_t connect_req;

  // 接続  uv_tcp_connect(&connect_req, &client, req_addr, on_connect);

  // ループを開始  return uv_run(loop);}

 

libuv使用的基本步骤:

1、生成一个loop (uv_default_loop() 或者 uv_loop_t _loop)

2、初始化一个client,uv_tcp_init

3、连接指定的服务器,uv_tcp_connect

4、开启消息循环,uv_run

通常使用时,我们都需要新启动一个线程,在该线程中来执行uv_run来保证不阻塞当前调用的线程(uv_run是阻塞的,不会立即返回)。

 

使用线程的关键函数:uv_thread_create(创建线程)、uv_async_init、uv_async_send(线程通信),消息的发送是异步的,在另外一个线程中多次(二次或更多)调用了uv_async_send函数后它只会保证uv_async_init回调函数至少被调用一次

uv_async_send是非阻塞的,同样也不是线程安全的,在变量访问时应该尽量和互斥量或读写锁来保证访问顺序。

 

我们游戏服务器是双线的,所以返回给客户端的数据是域名 + 端口,这里需要先将域名转为ip然后进行uv_tcp_connect连接。

示例代码:

uv_getaddrinfo_t* getaddrinfo_handle = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t));getaddrinfo_handle->data = this; int r = uv_getaddrinfo(&loop_, getaddrinfo_handle, &AfterDNSResolved, m_strDomain.c_str(), NULL, NULL); //r 返回0时表示正常,非0则说明出错了可通过 uv_err_name(r)、uv_strerror(r)获得出错信息

 

uvbook的QueryDNS示例:

int main() {    loop = uv_default_loop();

    struct addrinfo hints;    hints.ai_family = PF_INET;    hints.ai_socktype = SOCK_STREAM;    hints.ai_protocol = IPPROTO_TCP;    hints.ai_flags = 0;

    uv_getaddrinfo_t resolver;    fprintf(stderr, "irc.freenode.net is... ");    int r = uv_getaddrinfo(loop, &resolver, on_resolved, "irc.freenode.net", "6667", &hints);

    if (r) {        fprintf(stderr, "getaddrinfo call error %s\n", uv_err_name(r));        return 1;    }    return uv_run(loop, UV_RUN_DEFAULT);}   void on_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) {    if (status < 0) {        fprintf(stderr, "getaddrinfo callback error %s\n", uv_err_name(status));        return;    }

    char addr[17] = {‘\0‘};    uv_ip4_name((struct sockaddr_in*) res->ai_addr, addr, 16);    fprintf(stderr, "%s\n", addr);

    uv_connect_t *connect_req = (uv_connect_t*) malloc(sizeof(uv_connect_t));    uv_tcp_t *socket = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));    uv_tcp_init(loop, socket);

    uv_tcp_connect(connect_req, socket, (const struct sockaddr*) res->ai_addr, on_connect);

    uv_freeaddrinfo(res);}

Windows下的libuv工程构建,使用官方推荐的gyp生成vs的解决方案即可

1、安装并设置python(2.6或2.7版本)

2、源码目录下新建build目录,然后将gyp下载至该目录

3、双击执行vcbuild.bat即可

在其它项目中引用libuv.lib时,需要在VS附加依赖项中添加几个系统的lib,不然会报错

libuv.lib

advapi32.lib

iphlpapi.lib

psapi.lib

shell32.lib

userenv.lib

ws2_32.lib

 

Android下编译libuv.a,我安装了虚拟机然后折腾了好一会,最后放弃了,参考Linux生成的mk自己整了一个完整的mk文件

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)

LOCAL_MODULE := uv_static

LOCAL_MODULE_FILENAME := libuv

#LOCAL_SRC_FILES := proj.android/libuv.a#LOCAL_EXPORT_CFLAGS := -I$(LOCAL_PATH)/include

LOCAL_SRC_FILES := \src/fs-poll.c \src/inet.c \src/threadpool.c \src/uv-common.c \src/version.c \src/unix/async.c \src/unix/core.c \src/unix/dl.c \src/unix/fs.c \src/unix/getaddrinfo.c \src/unix/getnameinfo.c \src/unix/loop.c \src/unix/loop-watcher.c \src/unix/pipe.c \src/unix/poll.c \src/unix/process.c \src/unix/signal.c \src/unix/stream.c \src/unix/tcp.c \src/unix/thread.c \src/unix/timer.c \src/unix/tty.c \src/unix/udp.c \src/unix/proctitle.c \src/unix/linux-core.c \src/unix/linux-inotify.c \src/unix/linux-syscalls.c \src/unix/pthread-fixes.c \src/unix/android-ifaddrs.c

LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \                    $(LOCAL_PATH)/src \                    $(LOCAL_PATH)/src/unix

LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include \                           $(LOCAL_PATH)/src \                           $(LOCAL_PATH)/src/unix

LOCAL_CFLAGS := \-Wall \-fvisibility=hidden \-g \--std=gnu89 \-pedantic \-Wall \-Wextra \-Wno-unused-parameter \-Wstrict-aliasing \-O3 \-fstrict-aliasing \-fomit-frame-pointer \-fdata-sections \-ffunction-sections

include $(BUILD_STATIC_LIBRARY)

在项目的jni/Android.mk中只需要添加

LOCAL_WHOLE_STATIC_LIBRARIES += uv_static

$(call import-module,libuv)

//注意添加NDK的搜索路径

而自己项目中需要用到uv.h时,修改相应的Android.mk文件,在

LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../libuv/include \

//目录自己依照自己的环境进行修改

一切正常的话,在eclipse项目的obj目录下会看到编译成功后的文件

 

 

iOS下的编译,默认libuv只提供了Mac下的编译,修改一下就可以让它支持iOS了

1、下载libuv  https://github.com/joyent/libuv/releases

2、shell进入libuv

$mkdir -p build

$git clone https://git.chromium.org/external/gyp.git  build/gyp

3、$./gyp_uv.py -f xcode

生成xcode项目文件uv.xcodeproj

4、打开xcode,修改architectures

 

XCode7的话,直接先最新的SDK版本,上面的用的比较旧。引用项目的话,将libuv.a加到项目中,然后Framework Search Path以及Header Search Path添加路径即可。

 

到这里,三个平台全部都搞定了。我引入到项目中,主要是参考了

libuv_tcp https://github.com/wqvbjhc/libuv_tcp

也借鉴了

libsourcey  https://github.com/sourcey/libsourcey

uvpp  https://github.com/larroy/uvpp

 

我自己收藏的一个libuv for lua的项目,觉得也挺不错的。luv https://github.com/luvit/luv  有兴趣可以去捣鼓一下,跟Node.js差不多

 

附带贴一下uvbook的二个链接:

英文版(最新V1.3.0)  https://nikhilm.github.io/uvbook/introduction.html

中文版(V0.9.8) http://www.nowx.org/uvbook/index.html

时间: 2024-10-08 14:54:56

libuv在cocos2d-x中的使用的相关文章

如何在Cocos2D游戏中实现A*寻路算法(一)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 该篇博客由iOS课程团队的Johann Fradj发布,他现在是一个全职开发iOS的开发者.他是Hot Apps Factory(其是App Cooker的创造者)的共同创建

如何在Cocos2D游戏中实现A*寻路算法(三)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 创建开放和闭合列表 接下来我们将使用2个NSMutableArray来跟踪保存我们的开放和闭合列表. 你可能奇怪为什么不用NSMutableSet代替.好吧,这里有2个原因:

如何在Cocos2D游戏中实现A*寻路算法(五)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 跟随着黄色砖块前进 现在我们已经找到了我们的路径,我们只需要让猫咪跟随它. 我们接下来要做的是记住整个路径,并且使得猫咪根据路径一步一步的移动. 在CatSprite.h中建

如何在Cocos2D游戏中实现A*寻路算法(八)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 就拿上图中左上角的情况举个栗子. 这只猫咪想要从原点(O)到左下角的对角线方块中去.如果在左边或底下(或全部都有)有墙壁并且测试穿过对角线将会切入一个墙角(或2个).所以左下

cocos2d JS 中的数组拼接与排序

1 var arrA = [];//创建三个局部变量的新数组 2 var arrB = []; 3 var arrC = []; 4 var newCards = this.MyMahjong;//创建一个新的局部变量等于一个全局变量的数组:并把里面的内容依次拆开再全部拼接 5 for(var t=0;t<newCards.length;t++){ 6 var char = newCards[t].charAt(0); 7 if(char == "a"){ 8 arrA.push

在libuv中使用openssl建立ssl连接

在libuv中使用openssl建立ssl连接 @(blogs) 使用openssl进行加密通信时,通常是先建立socket连接,然后使用SSL_XXX系列函数在普通socket之上建立安全连接,然后发送和接收数据.openssl的这些函数可以支持底层的socket是非阻塞模式的.但当将openssl和libuv进行结合时,会遇到一些问题: 1. openssl在进行数据读写之前,需要进行若干次"握手"."握手"中会有若干次的数据读写.这个在普通的socket连接中

关于 Android ios cocos2d 中的广播监听

1. 关于广播监听 第一次使用是在Android中,broadcast.主要用在2个activity之间进行传递数据,发出一个广播,对这个广播有兴趣的就去监听它,做出相应的回应即可.主要是传递数据,触发机制比较好,跟全局变量或者单例有点像,但是使用场合有区别,比如2个activity之间传递数据,activity这种有生命周期的弄成全局变量和单例就不合适了. 2.Android的广播 发送广播: Intent intent = new Intent("OUR_BLE_CENTRAL_MANAGE

Node.js中的异步I/O是如何进行的?

Node.js的异步I/O通过事件循环的方式实现.其中异步I/O又分磁盘I/O和网络I/O.在磁盘I/O的调用中,当发起异步调用后,会将异步操作送进libuv提供的队列中,然后返回.当磁盘I/O执行完成之后,会形成一个事件,事件循环的过程中发现该事件后,会将其消费.消费过程就是将得到的数据和传入的回调函数执行. 网络I/O与磁盘I/O的差异在于它不需要线程池来进行处理,而是在每次时间循环的过程中通过IOCP/epoll/kqueue/event ports来获取网络I/O的 事件队列. 摘自:<

Cocos2d坐标系转换

Cocos2d-x坐标系和OpenGL坐标系相同,都是起源于笛卡尔坐标系(高中数学里面那种). 笛卡尔坐标系 笛卡尔坐标系中定义右手系原点在左下角,x向右,y向上,z向外,OpenGL坐标系为笛卡尔右手系. 屏幕坐标系和Cocos2d坐标系 标准屏幕坐标系使用和OpenGL不同的坐标系,而Cocos2d则使用和OpenGL相同的坐标系. iOS, Android, Windows Phone等在开发应用时使用的是标准屏幕坐标系,原点为屏幕左上角,x向右,y向下. Cocos2d坐标系和OpenG

Cocos2d之Texture2D类详解之将文件加载成Texture2D对象

一.声明 笔者以cocos2d框架cocos2d-x-3.3rc0版本的源码做分析.本文为笔者原创,允许转载和分享,只要注明文章出处即可. 二.简介 Texture2D类简介 Texture2D类允许开发者用图像.文本信息和简单的数据来创建OpenGL2D纹理.被创建的纹理拥有两个维度.根据开发者创建Texture2D对象方式的不同,实际图像的尺寸可能比生成的纹理的尺寸要小,而且纹理的内容是倒置的. 像素格式 在计算机图形学中,人们用每个像素在内存中的总位数以及分别存储红.蓝.绿和alpha(阿