实对于此篇算是对于这段时间网络研究的一个总结。
对于手游网络通信的交互,一般情况下,Socket长连接直接使用Mina框架即可,对于Http短连接使用Servlet 入口即可(那么对于后期将陆续更新Servlet博文)
那么本篇主要介绍Socket长连接,当然与此配对的跨平台通信则选择了BSD Socket,当然还有其他的,这里只说BSD Socket;
对于BSD Socket不是很熟悉的请自行google学习下,Himi需要提醒大家的是BSD Socket不是第三方类库,而是UNIX/Linux系统中通用的网络接口;
首先连接到Server端,这里Himi简单封装一个函数提供大家使用;
导入 #include <netdb.h>
两个参数:1:IP地址 2:端口
其中有个socket成员变量:
int socketHandle = 0;
- int HSocket::connect(const char* ip, unsigned short port){
- struct sockaddr_in sa;
- struct hostent* hp;
- hp = gethostbyname(ip);
- if(!hp){
- return -1;
- }
- memset(&sa, 0, sizeof(sa));
- memcpy((char*)&sa.sin_addr, hp->h_addr, hp->h_length);
- sa.sin_family = hp->h_addrtype;
- sa.sin_port = htons(port);
- socketHandle = socket(sa.sin_family, SOCK_STREAM, 0);
- if(socketHandle < 0){
- printf( "failed to create socket\n" );
- return -1;
- }
- if(::connect(socketHandle, (sockaddr*)&sa, sizeof(sa)) < 0){
- printf( "failed to connect socket\n" );
- ::close(socketHandle);
- return -1;
- }
- CCLOG("Client connect OK ! IP: %s:%d ",ip,port);
- return 0;
- }
两点注意:
1. 对于bsd socket 的 ::connect()函数进行连接服务器的时候会阻塞你的主线程,所以将Himi封装好的connect()函数在另一个线程调用则是一个好的处理方式;否则一旦网络比较差,你的游戏就假死ing~ 悲剧;
2. 对于线程我们直接使用 pThread 就可以了,那么这里Himi就给一个创建线程的例子吧:
定义一个线程成员变量:
pthread_t threadHimi;
然后Himi也为大家封装一个函数:
- int HSocket::threadStart(){
- int errCode = 0;
- do{
- pthread_attr_t tAttr;
- errCode = pthread_attr_init(&tAttr);
- CC_BREAK_IF(errCode!=0)
- errCode = pthread_attr_setdetachstate(&tAttr, PTHREAD_CREATE_DETACHED);
- if (errCode!=0) {
- pthread_attr_destroy(&tAttr);
- break;
- }
- errCode = pthread_create(&threadHimi, &tAttr, thread_function, this);
- }while (0);
- return errCode;
- }
1)创建线程其实就是pthread_create()函数,但是上面这个函数其他内容则主要为你创建的线程设定为分离式;这里 thread_function 是个函数;童鞋们对于pthread不太熟悉的请自行百度和google;
2)当然这里Himi要提醒大家,pthread是c库,不是c++库,它要求是全局函数,所以得static的!
那么连接到Server端之后我们就应该关心, BSD Socket 对于数据的发送和接收!
1.发送: send 函数:
send(socketHandle,buffer,length,0)
socketHandle : 你已经连接的socket
buffer:发送的缓存数据
length:数据长度
2. 接收: recv 函数:
recv(socketHandle, p, length, 0)
socketHandle : 你已经连接的socket
p : 存放数据的容器
length:获取服务器数据的长度;
注意:
1. 对于recv 函数的其中参数 length长度,大家务必要仔细,很清楚服务器应该发来的数据长度,因为一旦recv函数执行,那么不从Server端读取出length长度就不会罢休的!
2. 如果你的Server端是Java的,那么要注意大端 ,小端的问题!Java属于大端模式,c++属于小端模式;(对于大小端不熟悉的,也请自行google,这里仍旧不赘述)
所以:
Client->recv到数据后->数据转换成小端
Client->send数据时->数据转换成大端
这样才能保证Java服务器与cocos2dx的Client端正常交互;
-----上面一直在介绍Client端的知识,那么下面简单说下Server端Mina的相关知识吧------
其实对于Mina框架而言,功能强大使用简单,我们不需要关心通信,而只是需要关心数据的处理;当然对于数据的处理在Mina中最主要的就是取决于自定义的Decode 和 Encode,编码解码;
一般情况下定义好通信的数据结构,是比较关键的一点;
1. 比如一般数据都有数据头的概念,其中数据头用来标识当前通信的数据的版本,标识和真实数据的长度等等。(至于如何设计这个看大家自己的想法了);
2. 数据结构中更不能少的肯定还是协议号!根据协议号,客户端和服务器才能做同一件事情;
3. 当然其中我们还会使用MD5 或者 CRC等进行数据较验等,对于MD5和CRC校验不太熟悉的也请自行google = =。
定义好数据结构后,如同Client端与Server签订了合同,彼此遵循此结构进行交互;
Server端对于收发数据的处理其实在Mina中比较容易,通过Himi的Mina博文也可以清晰容易的懂得;但是如何能让服务器根据协议号找到对应的编码解码类去处理那么才是重点;Himi这里只简单提醒你创建一个抽象类;
OK,关于数据的存放,当然Himi这里使用的Hibernate 的Annotation映射到Mysql中。比较轻松愉快~
其实Himi以上说的虽然不是很详细,比如Client端对于Server端数据的细节处理等等;但是大概的手机网游框架和大家需要去掌握的知识点基本都没有遗漏,只要童鞋们对于文章中的所有知识点都了如指掌,OK。你Socket C/S手游框架即可开工;
下面Himi放出一张近来对于Server端的成绩图:
(Client 端:cocos2dx / Server端:Mina)
Client 特点: 当服务器端有数据发送给Client端,Client端自动将收到的数据索引到对应等待数据的类中;
Server 特点: 当客户端有数据发送Server端,Server端 能自动识别找到对应的编码解码类;