您的位置:首页 > 理论基础 > 计算机网络

cocos2d-x 通过socket实现http下载及断点续传的实现

2013-09-10 22:12 585 查看
代码未经进一步的整理,可能比较混乱。

首先,2dx的socket库由BSSocket组成。可跨平台,在windows上已验证。

#ifndef _NET_BSSOCKET_H_
#define _NET_BSSOCKET_H_

#ifdef WIN32
#include <winsock.h>
#include <windows.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 "pthread/pthread.h"
#include <iostream>
#include <vector>

class BSSocket;
using namespace std;

static BSSocket* bsSocket = NULL;

const int MAX_BSSOCKETMSG_BUFF = 1024 * 64;
class BSSocket {

private:

static unsigned char socketBuff[MAX_BSSOCKETMSG_BUFF];
static unsigned long socketBuffLen;

bool need_quit;
bool isConnected;

char* connectIp;
unsigned int connectPort;
// Send socket

public:
int Send(const char* buf, int len, int flags = 0);
BSSocket(SOCKET sock = INVALID_SOCKET);
static BSSocket* getInstance();
void initConnect(const char* ip, unsigned short port);

~BSSocket();

// Create socket object for snd/recv data
bool Create(int af, int type, int protocol = 0);

// Connect socket
bool Connect(const char* ip, unsigned short port);
bool ConnectSyn(const char* ip, unsigned short port);
//#region server
// Bind socket
bool Bind(unsigned short port);

// Listen socket
bool Listen(int backlog = 5);

// Accept socket
bool Accept(BSSocket& s, char* fromip = NULL);
//#endregion

// 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

BSSocket& operator = (SOCKET s);

operator SOCKET ();

int        m_nRecvBufLen;
char    m_szRecvBuf[MAX_BSSOCKETMSG_BUFF + 1];    /*接收缓存区*/

int getFd() { return m_sock; };
SOCKET m_sock;

};

#endif // !_NET_BSSOCKET_H_


及cpp的实现

#include "BSSocket.h"

#ifdef WIN32
#pragma comment(lib, "wsock32")
#endif

BSSocket::BSSocket(SOCKET sock)
:need_quit(false),isConnected(false), connectIp(NULL), connectPort(0)
{
m_sock = sock;
}

BSSocket::~BSSocket()
{
}

int BSSocket::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 BSSocket::Clean()
{
#ifdef WIN32
return (WSACleanup());
#endif
return 0;
}

BSSocket& BSSocket::operator = (SOCKET s)
{
m_sock = s;
return (*this);
}

BSSocket::operator SOCKET ()
{
return m_sock;
}
//create a socket object win/lin is the same
// af:
bool BSSocket::Create(int af, int type, int protocol)
{
m_sock = socket(af, type, protocol);
std::cout << "the errro info" << GetError();
if ( m_sock == INVALID_SOCKET ) {
return false;
}
return true;
}

bool BSSocket::Connect(const char* ip, unsigned short port)
{
struct sockaddr_in svraddr;
svraddr.sin_family = AF_INET;
svraddr.sin_addr.s_addr = inet_addr(ip);
svraddr.sin_port = htons(port);
int ret = connect(m_sock, (struct sockaddr*)&svraddr, sizeof(svraddr));
if ( ret == SOCKET_ERROR ) {
return false;
}
std::cout << "you are connected" << std::endl;
isConnected = true;
return true;
}

bool BSSocket::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;
}
//for server
bool BSSocket::Listen(int backlog)
{
int ret = listen(m_sock, backlog);
if ( ret == SOCKET_ERROR ) {
return false;
}
return true;
}

bool BSSocket::Accept(BSSocket& 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 BSSocket::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 ) {
std::cout << "the send errro info" << GetError();
return -1;
}
count += bytes;
}

return count;
}

int BSSocket::Recv(char* buf, int len, int flags)
{
return (recv(m_sock, buf, len, flags));
}

int BSSocket::Close()
{
#ifdef WIN32
return (closesocket(m_sock));
#else
return (close(m_sock));
#endif
}

int BSSocket::GetError()
{
#ifdef WIN32
return (WSAGetLastError());
#else
return (errno);
#endif
}

void BSSocket::initConnect( const char* ip, unsigned short port )
{
connectIp = new char[sizeof(ip)];
strcpy(connectIp, ip);
connectPort = port;
pthread_t initThread;
}

BSSocket* BSSocket::getInstance()
{
if(bsSocket == NULL) {
bsSocket = new BSSocket;
}
return bsSocket;
}

bool BSSocket::ConnectSyn( const char* ip, unsigned short port )
{
Init();
Create(AF_INET, SOCK_STREAM, 0);
bool success = Connect(ip, port);
std::cout << "connect status is = " << success << std::endl;
return success;
}


通过ConnectSyn实现连接,若失败则返回false

其次在其基础上封装了一层HttpSocket通过这一层的封装实现对http连接的下载

主要由静态方法

bool CHttpSocket::downFile( string strServer, string strObject, int nPort, string saveFile, int from, int to )
{
CHttpSocket httpSocket;
long nLength;
const char *pRequestHeader = NULL;
pRequestHeader = httpSocket.FormatRequestHeader((char *)strServer.c_str(),(char *)strObject.c_str(),nLength, NULL, NULL, from, to);
httpSocket.Connect((char *)strServer.c_str(), nPort);
httpSocket.SendRequest();
httpSocket.SetTimeout(10000,0);
char szValue[100];
httpSocket.GetField("Content-Length",szValue,30);
int nFileSize = atoi(szValue);

int downFrom = 0, downTo = 0, fileSize;
httpSocket.GetField("Content-Range",szValue,100);
if(getRange(string(szValue), downFrom, downTo, fileSize)) {
int nCompletedSize = 0;
int nDownloadSize = downTo - downFrom + 1;
ensureFile(saveFile, fileSize);
fstream outFile;
outFile.open(saveFile.c_str(), ios::out|ios::in|ios::binary);
outFile.seekp(downFrom);
char pData[8192];
int nReceSize = 0;
long dwStartTime,dwEndTime;
while(nCompletedSize < nDownloadSize)
{
dwStartTime = GetTickCount();
nReceSize = httpSocket.Receive(pData,8192);
if(nReceSize == 0)
{
printf("服务器已经关闭连接.");
break;
}
if(nReceSize == -1)
{
printf("接收数据超时.");
break;
}
dwEndTime = GetTickCount();
outFile.write(pData, nReceSize);
nCompletedSize += nReceSize;
printf("write count is %d", outFile.gcount());
printf("download size is %d, all size is %d", nCompletedSize, nFileSize);
}
outFile.close();
} else {
int nCompletedSize = 0;
fstream outFile;
outFile.open(saveFile.c_str(), ios::out|ios::binary);

char pData[8192];
int nReceSize = 0;
long dwStartTime,dwEndTime;
while(nCompletedSize < nFileSize)
{
dwStartTime = GetTickCount();
nReceSize = httpSocket.Receive(pData,8192);
if(nReceSize == 0)
{
printf("服务器已经关闭连接.");
break;
}
if(nReceSize == -1)
{
printf("接收数据超时.");
break;
}
dwEndTime = GetTickCount();
outFile.write(pData, nReceSize);
nCompletedSize += nReceSize;
printf("write count is %d", outFile.gcount());
printf("download size is %d, all size is %d", nCompletedSize, nFileSize);
}
outFile.close();
}

return true;
}


其中 getRange(string(szValue), downFrom, downTo, fileSize) 解析http头中是否是断点续传的连接信息。

其中断点续传用C++的fstream用普通的ios::out打开会清除文件内容,所以需要用outFile.open(saveFile.c_str(), ios::out|ios::binary);

而其中如果不设置 ios::binary 这种模式的话,在下载的资源文件中如果碰到\r\n 此时进行写入的话,如果用16进制打开就会发现从0D0A变成了0D0D0A,这样导致了下载的文件与实际的文件相差甚远。

在下载过程中应该在一个临时文件里记录下载的信息,以保证下次打开可断点续传。

调用的方式为

比如下载超级兔子链接 http://dd.pctutu.com/soft/srramdisk.exe 则完整下载的话可调用

CHttpSocket::downFile("dd.pctutu.com", "/soft/srramdisk.exe", 80, "F:/srramdisk.exe");

如果想多次断点续传下载的话可以调用如下

CHttpSocket::downFile("dd.pctutu.com", "/soft/srramdisk.exe", 80, "F:/srramdisk.exe", 0, 10);

0表示最开始的字节位置,10表示结束的字节位置

CHttpSocket::downFile("dd.pctutu.com", "/soft/srramdisk.exe", 80, "F:/srramdisk.exe", 11, 0);

11表示最开始的字节位置,0表示不输入,则默认从起始位置到最后一个位置。

完整源码已上传到 http://download.csdn.net/detail/w273732573/6245965,初版代码,如果有什么不对的话请指证。


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: