这里使用客户端用的是C++的简单封装,参考http://blog.youkuaiyun.com/langresser_king/article/details/8646088这篇文章。
原文地址:http://blog.youkuaiyun.com/qqmcy/article/details/39155541
代码下载:http://download.youkuaiyun.com/detail/qqmcy/7884273
服务器端用的JAVA编写。测试服务器代码:http://download.youkuaiyun.com/detail/qqmcy/7884327 这个服务器代码只适合测试使用。
使用方法:
HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "Tools/SocketModel/DJAsyncSocket.h"
class HelloWorld : public cocos2d::Layer ,public DJAsyncSocketDelegate
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
void menuCloseCallback(Ref* sender);
private:
DJAsyncSocket* socket_asy;
virtual void update(float delta);
virtual void onSocketDidReadData(DJAsyncSocket* sock ,char* data);
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp
#include "HelloWorldScene.h"
#include "Tools/JsonData/MakeJson.h"
USING_NS_CC;
#define IP "127.0.0.1";
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//
// 1. super init first
if ( !Layer::init() )
{
return false;
}
socket_asy = new DJAsyncSocket();
socket_asy->setDelegate(this);
socket_asy->create("127.0.0.1", 8000);
auto visibleSize = Director::getInstance()->getVisibleSize();
auto origin = Director::getInstance()->getVisibleOrigin();
auto closeItem = MenuItemImage::create(
"choose_btn_light.png",
"choose_btn_light.png",
CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));
closeItem->setPosition(origin + Vec2(visibleSize) - Vec2(closeItem->getContentSize() / 2));
// create menu, it's an autorelease object
auto menu = Menu::create(closeItem, NULL);
menu->setPosition(Vec2::ZERO);
this->addChild(menu, 1);
this->scheduleUpdate();
return true;
}
void HelloWorld::menuCloseCallback(Ref* sender)
{
MakeJson* mjson = MakeJson::create();
std::string content = mjson->getTestJson();
content.append("\n");
// const char* str_cnt = content.c_str();
int len = (int)content.length();
//"{ \"hello\" : \"world\" }";
// char* str = nullptr;//"{\"reciver\":\"555\",\"sender\":\"f\"}\n";
char* str = (char*)malloc((len)* sizeof(char));
content.copy(str, len,0);
// log("content = %s,length = %lu ,Len = %d",content.c_str(),strlen(str),len);
socket_asy->sendMsg(str, len);
socket_asy->flush();
// free(str);
// content = nullptr;
}
void HelloWorld::update(float delta)
{
// 接收消息处理(放到游戏主循环中,每帧处理)
if (!socket_asy) {
return;
}
// 接收数据(取得缓冲区中的所有消息,直到缓冲区为空)
while (true)
{
char buffer[_MAX_MSGSIZE] = { 0 };
int nSize = sizeof(buffer);
char* pbufMsg = buffer;
if(socket_asy == NULL)
{
break;
}
if (!socket_asy->receiveMsg(pbufMsg, nSize)) {
break;
}
}
}
void HelloWorld::onSocketDidReadData(DJAsyncSocket *sock, char *data)
{
log("data = %s",data);
auto path = FileUtils::getInstance()->getWritablePath();
log("%s",path.c_str());
//在这个路径下添加一个json文件
path.append("test.json");
FILE* file = fopen(path.c_str(), "wb");
if (file) {
fputs(data, file);
fclose(file);
}
}
DJAsyncSocket.h
//
// DJAsyncSocket.h
// cpp4
//
// Created by 杜甲 on 14-9-5.
//
//
#ifndef __cpp4__DJAsyncSocket__
#define __cpp4__DJAsyncSocket__
#if WIN32
#include <windows.h>
#include <WinSock.h>
#else
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define SOCKET int
#define SOCKET_ERROR -1
#define INVALID_SOCKET -1
#endif
#ifndef CHECHF
#define CHECKF(x) \
do \
{ \
if(!(x)) {\
log("CHECK",#x,__FILE__,__LINE__);\
return 0; \
}\
} while(0)
#endif
#define _MAX_MSGSIZE 8 * 1024 //暂定一个消息最大为16K
#define BLOCKSECONDS 30 //INIT函数阻塞时间
#define INBUFSIZE (64 * 1024) // 接收数据缓存
#define OUTBUFSIZE (8 * 1024) // 发送数据的缓存,当不超过8K时,FLUSH只需要SEND一次
#include "cocos2d.h"
USING_NS_CC;
class DJAsyncSocket;
class DJAsyncSocketDelegate
{
public:
virtual void onSocketDidReadData(DJAsyncSocket* sock ,char* data) = 0;
};
class DJAsyncSocket :public Ref
{
public:
DJAsyncSocket();
bool create(const char* pszServerIP , int nServerPort , int nBlockSec = BLOCKSECONDS , bool bKeepAlice = false);
bool sendMsg(void* pBuf , int nSize);
bool receiveMsg(void* pBuf , int& nSize);
bool flush(void);
bool check(void);
void destory(void);
SOCKET getSocket(void) const ;
private:
CC_SYNTHESIZE(DJAsyncSocketDelegate*, _delegate, Delegate);
bool recvFromSock(void); //从网络中读取尽可能多的数据
bool hasError(void); //是否发生错误,注意,异步模式未完成非错误
void closeSocket(void);
SOCKET m_sockClient;
//发送数据缓冲
char m_bufOutput[OUTBUFSIZE];
int m_nOutbufLen;
//环形缓冲区
char m_bufInput[INBUFSIZE];
int m_nInbufLen;
int m_nInbufStart; //INBUF使用循环式队列,该变量为队列起点,0 - (SIZE - 1)
};
#endif /* defined(__cpp4__DJAsyncSocket__) */
//
// DJAsyncSocket.cpp
// cpp4
//
// Created by 杜甲 on 14-9-5.
//
//
#include "DJAsyncSocket.h"
DJAsyncSocket::DJAsyncSocket()
{
//初始化
memset(m_bufOutput, 0, sizeof(m_bufOutput));
memset(m_bufInput, 0, sizeof(m_bufInput));
}
void DJAsyncSocket::closeSocket()
{
#ifdef WIN32
closeSocket(m_sockClient);
WSACleanup();
#else
close(m_sockClient);
#endif
}
bool DJAsyncSocket::create(const char *pszServerIP, int nServerPort, int nBlockSec, bool bKeepAlive /*= FALSE*/)
{
if (pszServerIP == 0 || strlen(pszServerIP) > 15) {
return false;
}
#ifdef WIN32
WSADATA wsaData;
WORD version = MAKEWORD(2, 0);
int ret = WSAStartup(version, &wsaData);//win sock start up
if (ret != 0) {
return false;
}
#endif
// 创建主套接字
m_sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_sockClient == INVALID_SOCKET) {
closeSocket();
return false;
}
//设置Socket为KEEPALIVE
if (bKeepAlive)
{
int optval = 1;
if (setsockopt(m_sockClient, SOL_SOCKET, SO_KEEPALIVE, (char*)& optval , sizeof(optval))) {
closeSocket();
return false;
}
}
#ifdef WIN32
DWORD nMode = 1;
int nRes = ioctlsocket(m_sockClient, FIONBIO, &nMode);
if (nRes == SOCKET_ERROR) {
closeSocket();
return false;
}
#else
// 设置为非阻塞方式
fcntl(m_sockClient, F_SETFL, O_NONBLOCK);
#endif
unsigned long serveraddr = inet_addr(pszServerIP);
if (serveraddr == INADDR_NONE) { //检查IP地址格式错误
closeSocket();
return false;
}
sockaddr_in addr_in;
memset((void*)& addr_in, 0, sizeof(addr_in));
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(nServerPort);
addr_in.sin_addr.s_addr = serveraddr;
if (connect(m_sockClient, (sockaddr*)& addr_in, sizeof(addr_in)) == SOCKET_ERROR) {
if (hasError()) {
closeSocket();
return false;
}
else // WSAWOLDBLOCK
{
timeval timeout;
timeout.tv_sec = nBlockSec;
timeout.tv_usec = 0;
fd_set writeset,exceptset;
FD_ZERO(&writeset);
FD_ZERO(&exceptset);
FD_SET(m_sockClient, &writeset);
FD_SET(m_sockClient, &exceptset);
int ret = select(FD_SETSIZE, NULL, &writeset, &exceptset, &timeout);
if (ret == 0 || ret < 0) {
closeSocket();
return false;
}else //ret > 0
{
ret = FD_ISSET(m_sockClient, &exceptset);
if (ret) {
closeSocket();
return false;
}
}
}
}
m_nInbufLen = 0;
m_nInbufStart = 0;
m_nOutbufLen = 0;
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 500;
setsockopt(m_sockClient, SOL_SOCKET, SO_LINGER, (const char*)& so_linger, sizeof(so_linger));
return true;
}
bool DJAsyncSocket::hasError()
{
#ifdef WIN32
int err = WSAGetLastError();
if(err != WSAEWOULDBLOCK) {
return true;
}
#else
int err = errno;
if(err != EINPROGRESS && err != EAGAIN) {
return true;
}
#endif
return false;
}
bool DJAsyncSocket::sendMsg(void *pBuf, int nSize)
{
if (pBuf == 0 || nSize <= 0) {
return false;
}
if (m_sockClient == INVALID_SOCKET) {
return false;
}
//检查通讯消息包长度
int packsize = 0;
packsize = nSize;
//检测BUF溢出
if (m_nOutbufLen + nSize > OUTBUFSIZE) {
//立即发送OUTBUF中的数据,以清空OUTBUF
flush();
if (m_nOutbufLen + nSize > OUTBUFSIZE) {
destory();
return false;
}
}
memcpy(m_bufOutput + m_nOutbufLen, pBuf, nSize);
m_nOutbufLen += nSize;
return true;
}
bool DJAsyncSocket::flush()
{
if (m_sockClient == INVALID_SOCKET) {
return false;
}
if (m_nOutbufLen <= 0) {
return true;
}
//发送一段数据
int outsize;
outsize = send(m_sockClient, m_bufOutput, m_nOutbufLen, 0);
if (outsize > 0){
//删除已发送的部分
if (m_nOutbufLen - outsize > 0) {
memcpy(m_bufOutput, m_bufOutput + outsize, m_nOutbufLen - outsize);
}
m_nOutbufLen -= outsize;
if (m_nOutbufLen < 0) {
return false;
}
}else{
if (hasError()) {
destory();
return false;
}
}
return true;
}
bool DJAsyncSocket::check()
{
//检查状态
if (m_sockClient == INVALID_SOCKET) {
return false;
}
char buf[1];
int ret = recv(m_sockClient, buf, 1, MSG_PEEK);
if (ret == 0) {
destory();
return false;
}else if (ret < 0){
if (hasError()) {
destory();
return false;
}else{
return true;
}
}else{
return true;
}
return true;
}
bool DJAsyncSocket::receiveMsg(void *pBuf, int &nSize)
{
//检查参数
if (pBuf == NULL || nSize <= 0) {
return false;
}
if (m_sockClient == INVALID_SOCKET) {
return false;
}
// log("m_bufInput = %s,m_nInbufLen = %d",m_bufInput,m_nInbufLen);
//检查是否有一个消息(小于2则无法获取到消息长度)
if (m_nInbufLen < 2) {
// 如果没有请求成功 或者 如果没有数据则直接返回
if (!recvFromSock() || m_nInbufLen < 2) { //这个m_nInbufLen更新了
return false;
}
}
if (_delegate) {
_delegate->onSocketDidReadData(this, m_bufInput);
}
//计算要拷贝的消息的大小(一个消息,大小为整个消息的第一个16字节),因为环形缓冲区,所以要分开结算
int packsize = (unsigned char) m_bufInput[m_nInbufStart] + (unsigned char)m_bufInput[(m_nInbufStart + 1) % INBUFSIZE] * 256; //注意字节序,高位 + 低位
//检测消息包尺寸错误 暂定最大16k
if (packsize <= 0 || packsize > _MAX_MSGSIZE) {
m_nInbufLen = 0; //直接清空INBUF
m_nInbufStart = 0;
return false;
}
//检查消息是否完整(如果将要拷贝的消息大于此时缓冲区数据长度,需要再次请求接收剩余数据)
if (packsize > m_nInbufLen) {
//如果没有请求成功 或者 依然无法获取到完整的数据包 则返回,直到取得wanzbao
if (!recvFromSock() || packsize > m_nInbufLen) { //这个m_nInbufLen已更新
return false;
}
}
// 复制出一个消息
if (m_nInbufStart + packsize > INBUFSIZE) {
// 如果一个消息有回卷(被拆成两份在环形缓冲区的头尾)
// 先拷贝环形缓冲区末尾的数据
int copylen = INBUFSIZE - m_nInbufStart;
memcpy(pBuf, m_bufInput + m_nInbufStart, copylen);
//再考贝环形缓冲区头部的剩余部分
memcpy((unsigned char*) pBuf + copylen, m_bufInput, packsize - copylen);
nSize = packsize;
}else
{
// 消息没有回卷,可以一次拷贝出去
memcpy(pBuf, m_bufInput + m_nInbufStart, packsize);
nSize = packsize;
}
// log("m_bufInput = %s",m_bufInput);
m_nInbufStart = (m_nInbufStart + packsize) % INBUFSIZE;
m_nOutbufLen -= packsize;
return true;
}
bool DJAsyncSocket::recvFromSock()
{
if (m_nInbufLen >= INBUFSIZE || m_sockClient == INVALID_SOCKET) {
return false;
}
//接收第一段数据
int savelen , savepos; //数据要保存的长度和位置
if (m_nInbufStart + m_nInbufLen < INBUFSIZE) { //INBUF中的剩余空间有回绕
savelen = INBUFSIZE - (m_nInbufStart + m_nInbufLen); //后部空间长度,最大接收数据的长度
}else{
savelen = INBUFSIZE - m_nInbufLen;
}
// 缓冲区数据的末尾
savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;
// CHECKF(savepos + savelen < INBUFSIZE);
int inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);
if (inlen > 0) {
//有接收到数据
m_nInbufLen += inlen;
if (m_nInbufLen > INBUFSIZE) {
return false;
}
//接收第二段数据(一次接收没有完成,接收第二段数据)
if (inlen == savelen && m_nInbufLen < INBUFSIZE) {
int savelen = INBUFSIZE - m_nInbufLen;
int savepos = (m_nInbufStart + m_nInbufLen) % INBUFSIZE;
// CHECKF(savepos + savelen <= INBUFSIZE);
inlen = recv(m_sockClient, m_bufInput + savepos, savelen, 0);
if (inlen > 0) {
m_nInbufLen += inlen;
if (m_nInbufLen > INBUFSIZE) {
return false;
}
}else if (inlen == 0){
destory();
return false;
}else{
if (hasError()) {
destory();
return false;
}
}
}
}
return true;
}
void DJAsyncSocket::destory()
{
// 关闭
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 500;
int ret = setsockopt(m_sockClient, SOL_SOCKET, SO_LINGER, (const char*)& so_linger, sizeof(so_linger));
closeSocket();
m_sockClient = INVALID_SOCKET;
m_nInbufLen = 0;
m_nInbufStart = 0;
m_nOutbufLen = 0;
memset(m_bufOutput, 0, sizeof(m_bufOutput));
memset(m_bufInput, 0, sizeof(m_bufInput));
}
下面是制作JSON数据的类
MakeJson.h
//
// MakeJson.h
// cpp4
//
// Created by 杜甲 on 14-9-9.
//
//
#ifndef __cpp4__MakeJson__
#define __cpp4__MakeJson__
#include "cocos2d.h"
#include "json/document.h"
#include "json/writer.h"
#include "json/stringbuffer.h"
USING_NS_CC;
class MakeJson : public Ref
{
public:
CREATE_FUNC(MakeJson);
virtual bool init();
std::string getTestJson();
};
#endif /* defined(__cpp4__MakeJson__) */
MakeJson.cpp
//
// MakeJson.cpp
// cpp4
//
// Created by 杜甲 on 14-9-9.
//
//
#include "MakeJson.h"
bool MakeJson::init()
{
bool bRef = false;
do {
bRef = true;
} while (0);
return bRef;
}
std::string MakeJson::getTestJson()
{
rapidjson::Document d1;
d1.SetObject();
rapidjson::Document::AllocatorType& allocator = d1.GetAllocator();
rapidjson::Value array(rapidjson::kArrayType);
rapidjson::Value object(rapidjson::kObjectType);
// object.AddMember("id", 1, allocator);
// object.AddMember("name", "xiaonan", allocator);
// object.AddMember("age", "111", allocator);
// array.PushBack(object, allocator);
d1.AddMember("reciver", "111", allocator);
d1.AddMember("sender", "111", allocator);
d1.AddMember("content", "神奇dfsa", allocator);
// d1.AddMember("player", array, allocator);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> write(buffer);
d1.Accept(write);
return StringUtils::format("%s",buffer.GetString());
}