cocos2d-x 通过socket实现http下载及断点续传的实现
代码未经进一步的整理,可能比较混乱。
首先,2dx的socket库由BSSocket组成。可跨平台,在windows上已验证。
1 #ifndef _NET_BSSOCKET_H_ 2 #define _NET_BSSOCKET_H_ 3 4 #ifdef WIN32 5 #include <winsock.h> 6 #include <windows.h> 7 typedef int socklen_t; 8 #else 9 #include <sys/socket.h> 10 #include <netinet/in.h> 11 #include <netdb.h> 12 #include <fcntl.h> 13 #include <unistd.h> 14 #include <sys/stat.h> 15 #include <sys/types.h> 16 #include <arpa/inet.h> 17 typedef int SOCKET; 18 19 //#pragma region define win32 const variable in linux 20 #define INVALID_SOCKET -1 21 #define SOCKET_ERROR -1 22 //#pragma endregion 23 #endif 24 25 #include "pthread/pthread.h" 26 #include <iostream> 27 #include <vector> 28 29 class BSSocket; 30 using namespace std; 31 32 static BSSocket* bsSocket = NULL; 33 34 const int MAX_BSSOCKETMSG_BUFF = 1024 * 64; 35 class BSSocket { 36 37 private: 38 39 static unsigned char socketBuff[MAX_BSSOCKETMSG_BUFF]; 40 static unsigned long socketBuffLen; 41 42 bool need_quit; 43 bool isConnected; 44 45 char* connectIp; 46 unsigned int connectPort; 47 // Send socket 48 49 public: 50 int Send(const char* buf, int len, int flags = 0); 51 BSSocket(SOCKET sock = INVALID_SOCKET); 52 static BSSocket* getInstance(); 53 void initConnect(const char* ip, unsigned short port); 54 55 ~BSSocket(); 56 57 // Create socket object for snd/recv data 58 bool Create(int af, int type, int protocol = 0); 59 60 // Connect socket 61 bool Connect(const char* ip, unsigned short port); 62 bool ConnectSyn(const char* ip, unsigned short port); 63 //#region server 64 // Bind socket 65 bool Bind(unsigned short port); 66 67 // Listen socket 68 bool Listen(int backlog = 5); 69 70 // Accept socket 71 bool Accept(BSSocket& s, char* fromip = NULL); 72 //#endregion 73 74 // Recv socket 75 int Recv(char* buf, int len, int flags = 0); 76 77 // Close socket 78 int Close(); 79 80 // Get errno 81 int GetError(); 82 83 //#pragma region just for win32 84 // Init winsock DLL 85 static int Init(); 86 // Clean winsock DLL 87 static int Clean(); 88 //#pragma endregion 89 90 91 92 BSSocket& operator = (SOCKET s); 93 94 operator SOCKET (); 95 96 int m_nRecvBufLen; 97 char m_szRecvBuf[MAX_BSSOCKETMSG_BUFF + 1]; /*接收缓存区*/ 98 99 int getFd() { return m_sock; }; 100 SOCKET m_sock; 101 102 103 }; 104 105 #endif // !_NET_BSSOCKET_H_
及cpp的实现
1 #include "BSSocket.h" 2 3 #ifdef WIN32 4 #pragma comment(lib, "wsock32") 5 #endif 6 7 8 BSSocket::BSSocket(SOCKET sock) 9 :need_quit(false),isConnected(false), connectIp(NULL), connectPort(0) 10 { 11 m_sock = sock; 12 } 13 14 BSSocket::~BSSocket() 15 { 16 } 17 18 int BSSocket::Init() 19 { 20 #ifdef WIN32 21 /* 22 http://msdn.microsoft.com/zh-cn/vstudio/ms741563(en-us,VS.85).aspx 23 24 typedef struct WSAData { 25 WORD wVersion; //winsock version 26 WORD wHighVersion; //The highest version of the Windows Sockets specification that the Ws2_32.dll can support 27 char szDescription[WSADESCRIPTION_LEN+1]; 28 char szSystemStatus[WSASYSSTATUS_LEN+1]; 29 unsigned short iMaxSockets; 30 unsigned short iMaxUdpDg; 31 char FAR * lpVendorInfo; 32 }WSADATA, *LPWSADATA; 33 */ 34 WSADATA wsaData; 35 //#define MAKEWORD(a,b) ((WORD) (((BYTE) (a)) | ((WORD) ((BYTE) (b))) << 8)) 36 WORD version = MAKEWORD(2, 0); 37 int ret = WSAStartup(version, &wsaData);//win sock start up 38 if ( ret ) { 39 // cerr << "Initilize winsock error !" << endl; 40 return -1; 41 } 42 #endif 43 44 return 0; 45 } 46 //this is just for windows 47 int BSSocket::Clean() 48 { 49 #ifdef WIN32 50 return (WSACleanup()); 51 #endif 52 return 0; 53 } 54 55 BSSocket& BSSocket::operator = (SOCKET s) 56 { 57 m_sock = s; 58 return (*this); 59 } 60 61 BSSocket::operator SOCKET () 62 { 63 return m_sock; 64 } 65 //create a socket object win/lin is the same 66 // af: 67 bool BSSocket::Create(int af, int type, int protocol) 68 { 69 m_sock = socket(af, type, protocol); 70 std::cout << "the errro info" << GetError(); 71 if ( m_sock == INVALID_SOCKET ) { 72 return false; 73 } 74 return true; 75 } 76 77 bool BSSocket::Connect(const char* ip, unsigned short port) 78 { 79 struct sockaddr_in svraddr; 80 svraddr.sin_family = AF_INET; 81 svraddr.sin_addr.s_addr = inet_addr(ip); 82 svraddr.sin_port = htons(port); 83 int ret = connect(m_sock, (struct sockaddr*)&svraddr, sizeof(svraddr)); 84 if ( ret == SOCKET_ERROR ) { 85 return false; 86 } 87 std::cout << "you are connected" << std::endl; 88 isConnected = true; 89 return true; 90 } 91 92 bool BSSocket::Bind(unsigned short port) 93 { 94 struct sockaddr_in svraddr; 95 svraddr.sin_family = AF_INET; 96 svraddr.sin_addr.s_addr = INADDR_ANY; 97 svraddr.sin_port = htons(port); 98 99 int opt = 1; 100 if ( setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0 ) 101 return false; 102 103 int ret = bind(m_sock, (struct sockaddr*)&svraddr, sizeof(svraddr)); 104 if ( ret == SOCKET_ERROR ) { 105 return false; 106 } 107 return true; 108 } 109 //for server 110 bool BSSocket::Listen(int backlog) 111 { 112 int ret = listen(m_sock, backlog); 113 if ( ret == SOCKET_ERROR ) { 114 return false; 115 } 116 return true; 117 } 118 119 bool BSSocket::Accept(BSSocket& s, char* fromip) 120 { 121 struct sockaddr_in cliaddr; 122 socklen_t addrlen = sizeof(cliaddr); 123 SOCKET sock = accept(m_sock, (struct sockaddr*)&cliaddr, &addrlen); 124 if ( sock == SOCKET_ERROR ) { 125 return false; 126 } 127 128 s = sock; 129 if ( fromip != NULL ) 130 sprintf(fromip, "%s", inet_ntoa(cliaddr.sin_addr)); 131 132 return true; 133 } 134 135 int BSSocket::Send(const char* buf, int len, int flags) 136 { 137 int bytes; 138 int count = 0; 139 140 while ( count < len ) { 141 bytes = send(m_sock, buf + count, len - count, flags); 142 if ( bytes == -1 || bytes == 0 ) { 143 std::cout << "the send errro info" << GetError(); 144 return -1; 145 } 146 count += bytes; 147 } 148 149 return count; 150 } 151 152 int BSSocket::Recv(char* buf, int len, int flags) 153 { 154 return (recv(m_sock, buf, len, flags)); 155 } 156 157 int BSSocket::Close() 158 { 159 #ifdef WIN32 160 return (closesocket(m_sock)); 161 #else 162 return (close(m_sock)); 163 #endif 164 } 165 166 int BSSocket::GetError() 167 { 168 #ifdef WIN32 169 return (WSAGetLastError()); 170 #else 171 return (errno); 172 #endif 173 } 174 175 176 177 void BSSocket::initConnect( const char* ip, unsigned short port ) 178 { 179 connectIp = new char[sizeof(ip)]; 180 strcpy(connectIp, ip); 181 connectPort = port; 182 pthread_t initThread; 183 } 184 185 186 187 BSSocket* BSSocket::getInstance() 188 { 189 if(bsSocket == NULL) { 190 bsSocket = new BSSocket; 191 } 192 return bsSocket; 193 } 194 195 196 197 bool BSSocket::ConnectSyn( const char* ip, unsigned short port ) 198 { 199 Init(); 200 Create(AF_INET, SOCK_STREAM, 0); 201 bool success = Connect(ip, port); 202 std::cout << "connect status is = " << success << std::endl; 203 return success; 204 }
通过ConnectSyn实现连接,若失败则返回false
其次在其基础上封装了一层HttpSocket通过这一层的封装实现对http连接的下载
主要由静态方法
1 bool CHttpSocket::downFile( string strServer, string strObject, int nPort, string saveFile, int from, int to ) 2 { 3 CHttpSocket httpSocket; 4 long nLength; 5 const char *pRequestHeader = NULL; 6 pRequestHeader = httpSocket.FormatRequestHeader((char *)strServer.c_str(),(char *)strObject.c_str(),nLength, NULL, NULL, from, to); 7 httpSocket.Connect((char *)strServer.c_str(), nPort); 8 httpSocket.SendRequest(); 9 httpSocket.SetTimeout(10000,0); 10 char szValue[100]; 11 httpSocket.GetField("Content-Length",szValue,30); 12 int nFileSize = atoi(szValue); 13 14 15 int downFrom = 0, downTo = 0, fileSize; 16 httpSocket.GetField("Content-Range",szValue,100); 17 if(getRange(string(szValue), downFrom, downTo, fileSize)) { 18 int nCompletedSize = 0; 19 int nDownloadSize = downTo - downFrom + 1; 20 ensureFile(saveFile, fileSize); 21 fstream outFile; 22 outFile.open(saveFile.c_str(), ios::out|ios::in|ios::binary); 23 outFile.seekp(downFrom); 24 char pData[8192]; 25 int nReceSize = 0; 26 long dwStartTime,dwEndTime; 27 while(nCompletedSize < nDownloadSize) 28 { 29 dwStartTime = GetTickCount(); 30 nReceSize = httpSocket.Receive(pData,8192); 31 if(nReceSize == 0) 32 { 33 printf("服务器已经关闭连接."); 34 break; 35 } 36 if(nReceSize == -1) 37 { 38 printf("接收数据超时."); 39 break; 40 } 41 dwEndTime = GetTickCount(); 42 outFile.write(pData, nReceSize); 43 nCompletedSize += nReceSize; 44 printf("write count is %d", outFile.gcount()); 45 printf("download size is %d, all size is %d", nCompletedSize, nFileSize); 46 } 47 outFile.close(); 48 } else { 49 int nCompletedSize = 0; 50 fstream outFile; 51 outFile.open(saveFile.c_str(), ios::out|ios::binary); 52 53 char pData[8192]; 54 int nReceSize = 0; 55 long dwStartTime,dwEndTime; 56 while(nCompletedSize < nFileSize) 57 { 58 dwStartTime = GetTickCount(); 59 nReceSize = httpSocket.Receive(pData,8192); 60 if(nReceSize == 0) 61 { 62 printf("服务器已经关闭连接."); 63 break; 64 } 65 if(nReceSize == -1) 66 { 67 printf("接收数据超时."); 68 break; 69 } 70 dwEndTime = GetTickCount(); 71 outFile.write(pData, nReceSize); 72 nCompletedSize += nReceSize; 73 printf("write count is %d", outFile.gcount()); 74 printf("download size is %d, all size is %d", nCompletedSize, nFileSize); 75 } 76 outFile.close(); 77 } 78 79 return true; 80 }
其中 getRange(string(szValue), downFrom, downTo, fileSize) 解析http头中是否是断点续传的连接信息。
其中断点续传用C++的fstream用普通的ios::out打开会清除文件内容,所以需要用outFile.open(saveFile.c_str(), ios::out|ios::binary);
而其中如果不设置 ios::binary 这种模式的话,在下载的资源文件中如果碰到 此时进行写入的话,如果用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,初版代码,如果有什么不对的话请指证。