www.pudn.com > MyHttpClient.rar > Client.cpp
// // TCP/IP Demo: 一个 HTTP 客户端的请求服务的简单实现. // // // Client.cpp // // 实现向某个 HTTP 服务器发出请求,并将返回内容写入文件。基于 TCP 连接。 // // //#include #include #include #include #include #include using namespace std; //--------------------------------------------------------------------------- // 预编译指令 //--------------------------------------------------------------------------- // WinSock 程序需要的静态链接库 #pragma comment(lib, "ws2_32.lib") #define PORT_HTTP_DEFAULT 80 // 缺省的 HTTP 服务端口 #define ERR_WRONG_CMD_SYNTAX -1 // 错误代码:命令行参数不正确 #define ERR_NOT_START_UP -2 // 错误代码:WSAStartup() #define ERR_WINSOCK_VER_NOT_SUPPORTED -3 // 错误代码:WinSock 版本不支持 //--------------------------------------------------------------------------- // 函数声明 //--------------------------------------------------------------------------- void GetHttpContent(string strURL, string strSaveFileName); bool ParseURL(string strURL, string& strServerName, u_short& uPortNumber, string& strFileName); //--------------------------------------------------------------------------- // 主程序 //--------------------------------------------------------------------------- int main(int argc, char **argv) { WORD wVersionRequested = MAKEWORD(1,1); WSADATA wsaData; int nRet; string strURL; string strSaveFileName; // 检查输入的命令行参数 if (argc == 1) // 如果不带任何参数启动程序,则缺省补充网址和保存文件名 { // strURL = "http://www.gdufs.edu.cn/index.htm"; strURL = "http://jsjx.gdufs.edu.cn/uploadfile/20061118140101.jpg"; strSaveFileName = "pic.jpg"; } else if (argc == 3) { strURL.assign(argv[1]); // 传入的第二个参数为需要访问的网址 strSaveFileName.assign(argv[2]); // 传入的第三个参数为保存内容的文件名 } else { fprintf(stderr, "\n启动命令: MyHttpClient \n"); return ERR_WRONG_CMD_SYNTAX; } printf("\n==============================================================================="); printf("\n MyHttpClient.exe "); printf("\n==============================================================================="); // 初始化 WinSock nRet = WSAStartup(wVersionRequested, &wsaData); if (nRet) { fprintf(stderr, "\nWSAStartup(): %d\n", nRet); return ERR_NOT_START_UP; } // 检查 WinSock 的版本 if (wsaData.wVersion != wVersionRequested) { fprintf(stderr, "\nWinSock version not supported\n"); WSACleanup(); return ERR_WINSOCK_VER_NOT_SUPPORTED; } // 调用 GetHttpContent() 完成请求任务。 GetHttpContent(strURL, strSaveFileName); // 清理并退出 WinSock WSACleanup(); printf("\n\n程序执行完毕。\n\n"); return 0; } //--------------------------------------------------------------------------- // 功能实现 //--------------------------------------------------------------------------- void GetHttpContent(string strURL, string strSaveFileName) { string strServerName; string strFileName; u_short uPortNumber; // 解析 URL 格式:http://主机名:端口/文件路径 if (! ParseURL(strURL, strServerName, uPortNumber, strFileName)) { fprintf(stderr, "\nError: ParseURL(): 输入的 URL 不正确。URL 格式:http://主机名:端口/文件路径"); return; } // 通过 inet_addr() 判断 strServerName 是 IP 地址,还是一个主机名 IN_ADDR iaHost; LPHOSTENT lpHostEntry; iaHost.s_addr = inet_addr(strServerName.c_str()); if (iaHost.s_addr == INADDR_NONE) { // 如果不是 IP 地址, 则假设它是一个主机名 lpHostEntry = gethostbyname(strServerName.c_str()); } else { // 由 IP 地址获得主机 lpHostEntry = gethostbyaddr((const char *)&iaHost, sizeof(struct in_addr), AF_INET); } if (lpHostEntry == NULL) { fprintf(stderr, "\nWinSock error: %s : %d\n", "gethostbyname()", WSAGetLastError()); return; } // 创建 TCP/IP socket SOCKET Socket; Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (Socket == INVALID_SOCKET) { fprintf(stderr, "\nWinSock error: %s : %d\n", "socket()", WSAGetLastError()); return; } // 查找基于 TCP 连接的 HTTP 服务的端口号 SOCKADDR_IN saServer; /* LPSERVENT lpServEnt; lpServEnt = getservbyname("http", "tcp"); if (lpServEnt == NULL) saServer.sin_port = htons(uPortNumber); else saServer.sin_port = lpServEnt->s_port; //*/ saServer.sin_port = htons(uPortNumber); // 其它地址信息 saServer.sin_family = AF_INET; saServer.sin_addr = *((LPIN_ADDR)*lpHostEntry->h_addr_list); // 开始建立连接 int nRet; nRet = connect(Socket, (LPSOCKADDR)&saServer, sizeof(SOCKADDR_IN)); if (nRet == SOCKET_ERROR) { fprintf(stderr, "\nWinSock error: %s : %d\n", "connect()", WSAGetLastError()); closesocket(Socket); return; } printf("\n\n已经建立 Socket 连接。"); // 构造 HTTP 协议的请求报文头 string strSendBuffer; strSendBuffer.reserve(1024); char szTmpBuf[1024]; sprintf(szTmpBuf, "GET %s\n", strFileName.c_str()); // sprintf(szTmpBuf, "GET %s HTTP/1.0\n", strFileName.c_str()); strSendBuffer.append(szTmpBuf); // strSendBuffer.append("\nAccept: image/gif, image/x-xbitmap, image/jpeg, application/x-shockwave-flash, */*"); // strSendBuffer.append("\nAccept-Language: zh-cn"); // strSendBuffer.append("\nHost: "); // strSendBuffer.append(strServerName); // strSendBuffer.append("\nReferer: "); // strSendBuffer.append(strFileName); // strSendBuffer.append("\nUser-Agent: MyHttpClient.exe"); nRet = send(Socket, strSendBuffer.c_str(), strSendBuffer.length(), 0); if (nRet == SOCKET_ERROR) { fprintf(stderr, "\nWinSock error: %s : %d\n", "send()", WSAGetLastError()); closesocket(Socket); return; } printf("\n\n已经发送 HTTP 请求报文:\n%s \n", strSendBuffer.c_str()); // 打开保存内容的文件 ofstream saveFile; saveFile.open(strSaveFileName.c_str(), ios::out | ios::trunc | ios::binary); if (! saveFile) { fprintf(stderr, "\nError: 创建输出文件失败。"); return; } // 接收服务器返回的应答报文内容,并将其保存到文件中。 printf("\n\n正在接收 HTTP 应答报文:"); char szRecvBuf[1024]; bool isProcessingHead = true; for (;;) { // 阻塞式接收, nRet = NumberOfBytesReceived nRet = recv(Socket, szRecvBuf, sizeof(szRecvBuf), 0); if (nRet == SOCKET_ERROR) { fprintf(stderr, "\nWinSock error: %s : %d\n", "recv()", WSAGetLastError()); break; } fprintf(stderr,"\nrecv() returned %d bytes", nRet); // 是否已经断开连接? if (nRet == 0) break; // 处理 HTTP 响应报文头,注意此处只是非常简单的处理 if (isProcessingHead) { if (strncmp(szRecvBuf, "HTTP", 4) == 0) { int iCount = 0; char * p = szRecvBuf; for (; ; p++, iCount++) { if ((*p != '\0') && (iCount>sizeof(szRecvBuf))) { //szRecvBuf[0] = '\0'; //nRet = 0; break; } else if ((iCount<(sizeof(szRecvBuf)-3)) && *p == '\r' && *(p+1)=='\n' && *(p+2)=='\r' && *(p+3)=='\n') { memcpy(szRecvBuf, p+4, sizeof(szRecvBuf) - iCount+1); break; } } } isProcessingHead = false; } // 将内容写入文件 saveFile.write(szRecvBuf, nRet); } saveFile.close(); printf("\n\n文件下载成功,已保存至:%s", strSaveFileName.c_str()); // 关闭连接 closesocket(Socket); } bool ParseURL(string strURL, string& strServerName, u_short& uPortNumber, string& strFileName) { strServerName = ""; uPortNumber = PORT_HTTP_DEFAULT; strFileName = ""; string strPrefix = strURL.substr(0, 7); // printf("\n strPrefix = %s", strPrefix.c_str()); if (strPrefix.compare("http://")) // 必须以 http:// 开头 { return false; } int idxFisrtSlash = strURL.find('/', 7); string strNameOrPort = strURL.substr(7, idxFisrtSlash-7); strFileName = strURL.substr(idxFisrtSlash); // 检查 URL 中是否有端口号 int idxColon = strNameOrPort.find(':'); if (idxColon != -1) { strServerName = strNameOrPort.substr(0, idxColon); string strPortNumber = strNameOrPort.substr(idxColon + 1); uPortNumber = atoi(strPortNumber.c_str()); } else { strServerName = strNameOrPort; } printf("\nURL 解析成功:\n\n\t服务器 = %s \n\t端口 = %d \n\t文件路径 = %s", strServerName.c_str(), uPortNumber, strFileName.c_str()); return true; }