一、Socket网络通信一般流程
服务器Socket的一般流程:
(1)创建服务器Socket(Create);
(2)绑定端口(Bind);
(3)开启监听(Listener);
(4)接收客户端请求(Accept)。
(5)给客户端发送和接收数据(Send,Recv)
客户端的一般流程:
(1)创建客户端Socket(Create);
(2)连接服务器(Connect);
(3)发送和接收数据(Send,Recv)。
二、代码实例:
客户端:
HelloWorldScene.h:
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "ODSocket.h"
class HelloWorld : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
~HelloWorld();
std::thread * t1;
ODSocket * sock_client;
string strmsg;
void update(float t);
static void * getMessage( );
CREATE_FUNC(HelloWorld);
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp:
#include "HelloWorldScene.h"
#include "cocos-ext.h"
#include "ODSocket.h"
#include <thread>
#include <iostream>
using namespace cocos2d::extension;
USING_NS_CC;
using namespace std;
static HelloWorld * nowHelloWorld;
HelloWorld::~HelloWorld()
{
t1->join(); //调用join阻塞,等待线程执行完毕。
//或者调用detach放到后台,不过放到后台你就没法控制它了。
CC_SAFE_DELETE(t1);//相当于delete t1;t1 = nullptr;
}
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;
}
bool HelloWorld::init()
{
if (!Layer::init())
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
//背景图
Sprite * bg = Sprite::create("common/MainScene.jpg");
bg->setScaleX(visibleSize.width / bg->getContentSize().width);
bg->setScaleY(visibleSize.height / bg->getContentSize().height);
bg->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y));
addChild(bg);
CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24);
pLabel->setTag(111);
// position the label on the center of the screen
pLabel->setPosition(ccp(origin.x + visibleSize.width / 2,
origin.y + visibleSize.height - pLabel->getContentSize().height));
//add the label as a child to this layer
this->addChild(pLabel, 1);
nowHelloWorld = this;
//初始化Socket对象
sock_client = new ODSocket();
//连结到Socket服务器,显示从服务器发送过来的内容到场景
sock_client->Init();
bool res = sock_client->Create(AF_INET, SOCK_STREAM, 0);
log("\nCocos2dx_socket_created:%d", res);
//连接服务器
res = sock_client->Connect("192.168.1.211", 8889);
log("\nconnect_succeed:%d", res);
//接收从服务器发过来的消息
if (res == 1)
{
//创建线程对象
t1 = new std::thread(HelloWorld::getMessage);
log("thread create succeed!");
}
this->scheduleUpdate();
return true;
}
void * HelloWorld::getMessage( ) {
char buf[1024];
while (1) {
buf[0] = 0;
nowHelloWorld->sock_client->Recv(buf, sizeof(buf));
if (strlen(buf)>10)
{
log("buf:%s", buf);
nowHelloWorld->strmsg = buf;
}
}
}
void HelloWorld::update(float t) {
//在当前场景中显示从服务器发送过来的消息
CCLabelTTF * labelMsg = (CCLabelTTF *) nowHelloWorld->getChildByTag(111);
labelMsg->setString(strmsg.c_str());
}
服务器端:
SocketServer.cpp:
#include <iostream>
#include "ODSocket.h"
#include <string.h>
using namespace std;
int main(int argc,const char* argv[])
{
ODSocket *sockServer = new ODSocket();
sockServer->Init();
bool res = sockServer->Create(AF_INET,SOCK_STREAM,0);
cout<<"\nsocket_create"<<res<<endl; //返回值为1表示成功,下同。
res = sockServer->Bind(8889);
cout<<"port:%d"<<res<<endl;
res = sockServer->Listen();
cout<<"port_listen:%d"<<res<<endl;
char message[30];
ODSocket * p_sockClient = new ODSocket();
char * address = new char[20];
sockServer->Accept(*p_sockClient,address);
cout<<"\n%s come!"<<address<<endl;
while(1)
{
cin>>address;
if(strcmp(address,"exit")==0)
{
break;
}
sprintf(message,"welcome %s",address);
p_sockClient->Send(message,sizeof(message));
}
p_sockClient->Close();
sockServer->Clean();
return 0;
}
附录:
ODSocket.h:
#ifndef _ODSOCKET_H_
#define _ODSOCKET_H_
#ifdef WIN32
#include <winsock.h>
typedef int socklen_t;
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <arpa/inet.h>
typedef int SOCKET;
//#pragma region define win32 const variable in linux
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
//#pragma endregion
#endif
#include <iostream>
using namespace std;
class ODSocket {
public:
ODSocket(SOCKET sock = INVALID_SOCKET);
~ODSocket();
// Create socket object for snd/recv data
bool Create(int af, int type, int protocol = 0);
// Connect socket
bool Connect(const string ip, unsigned short port);
//#region server
// Bind socket
bool Bind(unsigned short port);
// Listen socket
bool Listen(int backlog = 5);
// Accept socket
bool Accept(ODSocket& s, char* fromip = NULL);
//#endregion
// Send socket
int Send(const char* buf, int len, int flags = 0);
// Recv socket
int Recv(char* buf, int len, int flags = 0);
// Close socket
int Close();
// Get errno
int GetError();
//#pragma region just for win32
// Init winsock DLL
static int Init();
// Clean winsock DLL
static int Clean();
//#pragma endregion
// Domain parse
static bool DnsParse(const char* domain, char* ip);
ODSocket& operator = (SOCKET s);
operator SOCKET ();
public:
SOCKET m_sock;
};
#endif
ODSocket.cpp:
#include "ODSocket.h"
#ifdef WIN32
#pragma comment(lib, "wsock32")
#endif
ODSocket::ODSocket(SOCKET sock)
{
m_sock = sock;
}
ODSocket::~ODSocket()
{
}
int ODSocket::Init()
{
#ifdef WIN32
/*
http://msdn.microsoft.com/zh-cn/vstudio/ms741563(en-us,VS.85).aspx
typedef struct WSAData {
WORD wVersion; //winsock version
WORD wHighVersion; //The highest version of the Windows Sockets specification that the Ws2_32.dll can support
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYSSTATUS_LEN+1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
}WSADATA, *LPWSADATA;
*/
WSADATA wsaData;
//#define MAKEWORD(a,b) ((WORD) (((BYTE) (a)) | ((WORD) ((BYTE) (b))) << 8))
WORD version = MAKEWORD(2, 0);
int ret = WSAStartup(version, &wsaData);//win sock start up
if (ret) {
//cerr << "Initilize winsock error !" << endl;
return -1;
}
#endif
return 0;
}
//this is just for windows
int ODSocket::Clean()
{
#ifdef WIN32
return (WSACleanup());
#endif
return 0;
}
ODSocket& ODSocket::operator = (SOCKET s)
{
m_sock = s;
return (*this);
}
ODSocket::operator SOCKET ()
{
return m_sock;
}
//create a socket object win/lin is the same
// af:
bool ODSocket::Create(int af, int type, int protocol)
{
m_sock = socket(af, type, protocol);
if (m_sock == INVALID_SOCKET) {
return false;
}
return true;
}
bool ODSocket::Connect(const string ip, unsigned short port)
{
struct sockaddr_in svraddr;
svraddr.sin_family = AF_INET;
svraddr.sin_addr.s_addr = inet_addr(ip.c_str());
svraddr.sin_port = htons(port);
int ret = connect(m_sock, (struct sockaddr*)&svraddr, sizeof(svraddr));
if (ret == SOCKET_ERROR) {
return false;
}
return true;
}
//for server
bool ODSocket::Bind(unsigned short port)
{
struct sockaddr_in svraddr;
svraddr.sin_family = AF_INET;
svraddr.sin_addr.s_addr = INADDR_ANY;
svraddr.sin_port = htons(port);
int opt = 1;
if (setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
return false;
int ret = bind(m_sock, (struct sockaddr*)&svraddr, sizeof(svraddr));
if (ret == SOCKET_ERROR) {
return false;
}
return true;
}
bool ODSocket::Listen(int backlog)
{
int ret = listen(m_sock, backlog);
if (ret == SOCKET_ERROR) {
return false;
}
return true;
}
bool ODSocket::Accept(ODSocket& s, char* fromip)
{
struct sockaddr_in cliaddr;
socklen_t addrlen = sizeof(cliaddr);
SOCKET sock = accept(m_sock, (struct sockaddr*)&cliaddr, &addrlen);
if (sock == SOCKET_ERROR) {
return false;
}
s = sock;
if (fromip != NULL)
sprintf(fromip, "%s", inet_ntoa(cliaddr.sin_addr));
return true;
}
int ODSocket::Send(const char* buf, int len, int flags)
{
int bytes;
int count = 0;
while (count < len) {
bytes = send(m_sock, buf + count, len - count, flags);
if (bytes == -1 || bytes == 0)
return -1;
count += bytes;
}
return count;
}
int ODSocket::Recv(char* buf, int len, int flags)
{
return (recv(m_sock, buf, len, flags));
}
int ODSocket::Close()
{
#ifdef WIN32
return (closesocket(m_sock));
#else
return (close(m_sock));
#endif
}
int ODSocket::GetError()
{
#ifdef WIN32
return (WSAGetLastError());
#else
return (SOCKET_ERROR);
#endif
}
bool ODSocket::DnsParse(const char* domain, char* ip)
{
struct hostent* p;
if ((p = gethostbyname(domain)) == NULL)
return false;
sprintf(ip,
"%u.%u.%u.%u",
(unsigned char)p->h_addr_list[0][0],
(unsigned char)p->h_addr_list[0][1],
(unsigned char)p->h_addr_list[0][2],
(unsigned char)p->h_addr_list[0][3]);
return true;
}
三、常见错误:
1、code:blocks编译出错:终端上以root身份创建的文件夹,以other身份(普通用户身份)登录的图形界面,code:blocks是不能在该文件夹中创建工程的,而且打开该文件夹中的文件也是无法成功运行的,因为权限不够。
解决方法:更改文件夹权限,让other用户也有读写执行权限。
chmod 777 SocketDir/
2、客户端connect失败。很有可能是linux防火墙造成的。apache等服务器通信和socket通信都应该先配置好iptables和selinux。初学者也可直接关闭iptables和selinux以方便做测试。
3、绑定(Bind)或者监听(Listen)端口失败。可以利用netstat命令查看绑定的端口状态。
netstat -pan|grep 8889
kill -9 2116 //关闭占用该端口的进程