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 这种模式的话,在下载的资源文件中如果碰到\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,初版代码,如果有什么不对的话请指证。


posted @ 2013-09-10 22:12  TickDream  阅读(2352)  评论(0编辑  收藏  举报