Socket工作的示意图:
程序实例:在Cocos2d-X中使用Socket
创建一个Sock类,用于处理Socket
在Sock.h中添加下面的代码
#ifndef __Sock_H__
#define __Sock_H__
#ifdef WIN32
#include
#define SOCK_TYPE SOCKET
#else
#define SOCK_TYPE int
#define INVALID_SOCKET -1
#endif
#define MyLog(...)
//创建Sock类
class Sock
{
public:
//构造函数
Sock();
//析构函数
virtual ~Sock();
//设置成非阻塞模式
bool setNonBlock()
{
SOCK_TYPE fd = _fd;
#ifdef WIN32
BOOL flg = TRUE;
//控制Socket的模式
if(ioctlsocket(fd, FIONBIO, (unsigned long *)&flg) != 0)
{
return false;
}
return true;
#else
int flags = fcntl(fd, F_GETFL, 0);
flags |= O_NONBLOCK;
return fcntl(fd, F_SETFL, flags) != -1;
#endif
}
//关闭Sock
void close()
{
#ifdef WIN32
closesocket(_fd);
#else
::close(_fd);
#endif
}
void attach(SOCK_TYPE fd)
{
_fd = fd;
}
//判断Sock是否合法
bool isValidSocket()
{
return _fd != INVALID_SOCKET;
}
protected:
SOCK_TYPE _fd;
};
//客户端的Sock
class SockClient : public Sock
{
public:
//构造函数
SockClient(unsigned short port = 0, const char* ip = NULL)
{
if (port == 0 && ip == NULL)
{
return;
}
//连接
connect(port, ip);
}
//析构函数
~SockClient(){}
//连接
//第一个参数:端口
//第二个参数:ip地址
int connect(unsigned short port, const char* ip)
{
//分配一个Socket
//第一个参数:AF_INET表示指定地址族(地址描述)
//第二个参数:SOCK_STREAM表示流式套接字TCP(Socket类型)
//第三个参数:0(协议)
_fd = socket(AF_INET, SOCK_STREAM, 0);
//地址信息结构
struct sockaddr_in addr;
//地址家族
addr.sin_family = AF_INET;
//端口号
addr.sin_port = htons(port);
//主机地址
addr.sin_addr.s_addr = inet_addr(ip);
//连接
int ret = ::connect(_fd, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0)
{
MyLog("connect error errno=%d", errno);
return -1;
}
return 0;
}
//接收
int recv(char* buf, int len)
{
return ::recv(_fd, buf, len, 0);
}
//发送
int send(const char* buf, int len)
{
return ::send(_fd, buf, len, 0);
}
};
//服务器端的Sock
class SockServer :public Sock
{
public:
//构造函数
SockServer(unsigned short port, const char* ip = NULL)
{
//监听
listen(port, ip);
}
//虚构函数
~SockServer(){}
//接受连接
SockClient* accept()
{
//接受客户端的发送请求,等待客户端发送connect请求
SOCK_TYPE fd = ::accept(_fd, NULL, NULL);
if (fd != INVALID_SOCKET)
{
//创建一个SockClient
SockClient* ret = new SockClient;
ret->attach(fd);
return ret;
}
return NULL;
}
protected:
//监听
//第一个参数:端口
//第二个参数:ip地址
int listen(unsigned short port, const char* ip = NULL)
{
//分配一个Socket
//第一个参数:AF_INET表示指定地址族(地址描述)
//第二个参数:SOCK_STREAM表示流式套接字TCP(Socket类型)
//第三个参数:0(协议)
_fd = socket(AF_INET, SOCK_STREAM, 0);
//地址信息结果
struct sockaddr_in addr;
//地址家族
addr.sin_family = AF_INET;
//端口号
addr.sin_port = htons(port);
if (ip == NULL)
{
//设置一个不确定的ip地址
addr.sin_addr.s_addr = INADDR_ANY;
}
else
{
//将ip地址转换为32位二进制网络字节序的IPV4地址
addr.sin_addr.s_addr = inet_addr(ip);
}
//绑定
int ret = bind(_fd, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0)
{
MyLog("bind error");
return -1;
}
//设置成非阻塞
this->setNonBlock();
//监听
::listen(_fd, 10);
return 0;
}
};
#endif
在Sock.cpp中添加下面的代码
#include "Sock.h"
//构造函数
Sock::Sock() :_fd(INVALID_SOCKET)
{
#ifdef WIN32
//初始化Windoes下的Sock
static bool winInit = false;
if (!winInit)
{
winInit = true;
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
}
#endif
}
//虚构函数
Sock::~Sock()
{
if (isValidSocket())
{
close();
}
}
再创建一个SocketTest类,用于测试Socket
在SocketTest.h中添加下面的代码
#ifndef __SocketTest_H__
#define __SocketTest_H__
#include "cocos2d.h"
#include"Sock.h"
USING_NS_CC;
class SocketTest : public CCLayer
{
public:
static CCScene* scene();
CREATE_FUNC(SocketTest);
bool init();
SockServer* _server;
SockClient* _client;
//启动服务器
void makeServer(CCObject*);
//启动客服端
void makeClient(CCObject*);
//接受连接
void Accept(CCObject*);
//发送
void Send(CCObject*);
//接收
void Recv(CCObject*);
};
#endif
在SocketTest.cpp中添加下面的代码
#include "SocketTest.h"
CCScene* SocketTest::scene()
{
CCScene* s = CCScene::create();
SocketTest* layer = SocketTest::create();
s->addChild(layer);
return s;
}
bool SocketTest::init()
{
CCLayer::init();
CCMenuItemFont* item1 = CCMenuItemFont::create("MakeServer", this, menu_selector(SocketTest::makeServer));
CCMenuItemFont* item2 = CCMenuItemFont::create("MakeClient", this, menu_selector(SocketTest::makeClient));
CCMenuItemFont* item3 = CCMenuItemFont::create("Send", this, menu_selector(SocketTest::Send));
CCMenuItemFont* item4 = CCMenuItemFont::create("Recv", this, menu_selector(SocketTest::Recv));
CCMenuItemFont* item5 = CCMenuItemFont::create("Accept", this, menu_selector(SocketTest::Accept));
CCMenu* menu = CCMenu::create(item1, item2, item3, item4, item5, NULL);
addChild(menu);
menu->alignItemsVertically();
return true;
}
//启动服务器
void SocketTest::makeServer(CCObject*)
{
this->_server = new SockServer(9888);
if (!this->_server->isValidSocket())
{
CCLog("server ERR");
}
else
{
CCLog("server OK");
}
}
//启动客服端
void SocketTest::makeClient(CCObject*)
{
this->_client = new SockClient(9888, "127.0.0.1");
if (!this->_client->isValidSocket())
{
CCLog("Client ERR");
}
else
{
CCLog("Client OK");
}
}
//接受连接
void SocketTest::Accept(CCObject*)
{
this->_client = this->_server->accept();
if (!this->_client->isValidSocket())
{
CCLog("Accept ERR");
}
else
{
CCLog("Accept OK");
}
}
//发送
void SocketTest::Send(CCObject*)
{
//发送信息
int ret = this->_client->send("Hello", 6);
CCLog("send: ret is %d", ret);
}
//接收
void SocketTest::Recv(CCObject*)
{
char buf[128];
//接收信息
int ret = this->_client->recv(buf, sizeof(buf));
CCLog("recv buf is %s", buf);
}
执行结果:
测试SocketTest:
启动两个SocketTest程序,一个做服务器一个做客户端
服务器上单击makeServer,启动服务器
打印Server Ok表示服务器启动成功
客户端单击makeClient,启动客户端
服务器上单击Accept,连接客户端
打印Accept Ok表示接受连接
客户端单击Send,客户端发送消息到服务器
服务器上单击Recv,接收服务器上发来的消息
打印出了"recv buf is Hello"表示服务器上收到了客户端发送的消息
服务器上单击Send,服务器发送消息到客户端
打印出了“send: ret is 6”表示服务器成功的向客户端发送了一个消息
客服端上单击Recv,客户端接收服务器发来的消息