時間:2023-09-01 03:24:01 | 來源:網(wǎng)站運營
時間:2023-09-01 03:24:01 來源:網(wǎng)站運營
(計算機網(wǎng)絡(luò)實驗1)HTTP 代理服務(wù)器的設(shè)計與實現(xiàn):@toc#include <stdio.h>#include <Windows.h>#include <process.h>#include <string.h>#include <tchar.h> #pragma comment(lib,"Ws2_32.lib")#define MAXSIZE 65507 //發(fā)送數(shù)據(jù)報文的最大長度#define HTTP_PORT 80 //http 服務(wù)器端口//Http重要頭部數(shù)據(jù)struct HttpHeader { char method[4]; // POST 或者 GET,注意有些為 CONNECT,本實驗暫不考慮 char url[1024]; // 請求的 url char host[1024]; // 目標主機 char cookie[1024 * 10]; //cookie HttpHeader() { ZeroMemory(this, sizeof(HttpHeader)); };};//禁止訪問的網(wǎng)站和釣魚網(wǎng)站是否可以輸入選擇char Invilid_web[1024] = "http://today.hit.edu.cn/";//不允許訪問的網(wǎng)站//char Invilid_web[1024] = "https://today.edu.cn/";char Target_web[1024] = "http://ids-hit-edu-cn.ivpn.hit.edu.cn";//統(tǒng)一身份認證網(wǎng)站char Fish_web[1024] = "http://jwes.hit.edu.cn/";//釣魚網(wǎng)站char Fish_host[1024] = "jwes.hit.edu.cn"; //釣魚主機名BOOL InitSocket();void ParseHttpHead(char* buffer, HttpHeader* httpHeader);BOOL ConnectToServer(SOCKET* serverSocket, char* host);unsigned int __stdcall ProxyThread(LPVOID lpParameter);//代理相關(guān)參數(shù)SOCKET ProxyServer;//代理服務(wù)器sockaddr_in ProxyServerAddr;//代理服務(wù)器地址const int ProxyPort = 10240;//設(shè)置代理窗口//緩存相關(guān)參數(shù)boolean haveCache = false;boolean needCache = true;void getfileDate(FILE* in, char* tempDate);void sendnewHTTP(char* buffer, char* datestring);void makeFilename(char* url, char* filename);void storefileCache(char* buffer, char* url);void checkfileCache(char* buffer, char* filename);//由于新的連接都使用新線程進行處理,對線程的頻繁的創(chuàng)建和銷毀特別浪費資源//可以使用線程池技術(shù)提高服務(wù)器效率//const int ProxyThreadMaxNum = 20;//HANDLE ProxyThreadHandle[ProxyThreadMaxNum] = {0};//DWORD ProxyThreadDW[ProxyThreadMaxNum] = {0};;;struct ProxyParam { SOCKET clientSocket; SOCKET serverSocket;};//主程序int _tmain(int argc, _TCHAR* argv[]){ printf("代理服務(wù)器正在啟動/n"); printf("初始化.../n"); if (!InitSocket()) { printf("socket 初始化失敗/n"); return -1; } printf("代理服務(wù)器正在運行,監(jiān)聽端口 %d/n", ProxyPort); SOCKET acceptSocket = INVALID_SOCKET; //把socket設(shè)置成無效套接字 ProxyParam* lpProxyParam; HANDLE hThread; DWORD dwThreadID;//unsigned long,無符號32位整型 //代理服務(wù)器不斷監(jiān)聽 while (TRUE) { acceptSocket = accept(ProxyServer, NULL, NULL); lpProxyParam = new ProxyParam;//新的客戶端和服務(wù)器端 if (lpProxyParam == NULL) { continue; } lpProxyParam->clientSocket = acceptSocket; //線程開始 hThread = (HANDLE)_beginthreadex(NULL, 0, &ProxyThread, (LPVOID)lpProxyParam, 0, 0); CloseHandle(hThread); Sleep(2000); } closesocket(ProxyServer); WSACleanup(); return 0;}//************************************// Method: InitSocket// FullName: InitSocket// Access: public// Returns: BOOL// Qualifier: 初始化套接字//************************************BOOL InitSocket() { //加載套接字庫(必須) WORD wVersionRequested; WSADATA wsaData; //套接字加載時錯誤提示 int err; //版本 2.2 wVersionRequested = MAKEWORD(2, 2); //加載 dll 文件 Scoket 庫 err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { //找不到 winsock.dll printf("加載 winsock 失敗,錯誤代碼為: %d/n", WSAGetLastError()); return FALSE; } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { printf("不能找到正確的 winsock 版本/n"); WSACleanup(); return FALSE; } //創(chuàng)建套接字 ProxyServer = socket(AF_INET, SOCK_STREAM, 0); if (INVALID_SOCKET == ProxyServer) { printf("創(chuàng)建套接字失敗,錯誤代碼為: %d/n", WSAGetLastError()); return FALSE; } ProxyServerAddr.sin_family = AF_INET;//地址族 ProxyServerAddr.sin_port = htons(ProxyPort); // 設(shè)置代理端口 //ProxyServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;//設(shè)置IP地址 ProxyServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//設(shè)置IP地址 //bind綁定 if (bind(ProxyServer, (SOCKADDR*)& ProxyServerAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) { printf("綁定套接字失敗/n"); return FALSE; } //listen監(jiān)聽,SOMAXCONN由系統(tǒng)來決定請求隊列長度 if (listen(ProxyServer, SOMAXCONN) == SOCKET_ERROR) { printf("監(jiān)聽端口%d 失敗", ProxyPort); return FALSE; } return TRUE;}//************************************// Method: ProxyThread// FullName: ProxyThread// Access: public// Returns: unsigned int __stdcall// Qualifier: 線程執(zhí)行函數(shù)// Parameter: LPVOID lpParameter//************************************unsigned int __stdcall ProxyThread(LPVOID lpParameter) { char Buffer[MAXSIZE]; char* CacheBuffer; char* DateBuffer; char filename[100] = { 0 }; _Post_ _Notnull_ FILE* in; char date_str[30]; //保存字段Date的值 ZeroMemory(Buffer, MAXSIZE); SOCKADDR_IN clientAddr; int length = sizeof(SOCKADDR_IN); int recvSize; int ret; FILE* fp; //第一次接收客戶端請求,將該請求緩存下來,存到本地文件中 recvSize = recv(((ProxyParam*)lpParameter)->clientSocket, Buffer, MAXSIZE, 0); HttpHeader* httpHeader = new HttpHeader(); if (recvSize <= 0) { goto error; } CacheBuffer = new char[recvSize + 1]; ZeroMemory(CacheBuffer, recvSize + 1); memcpy(CacheBuffer, Buffer, recvSize); ParseHttpHead(CacheBuffer, httpHeader); //printf("HTTP請求報文如下:/n%s/n", Buffer); ZeroMemory(date_str, 30); printf("httpHeader->url : %s/n", httpHeader->url); makeFilename(httpHeader->url, filename); //printf("filename是 %s/n", filename); if ((fopen_s(&in, filename, "r")) == 0) { printf("/n有緩存/n"); //fread_s(fileBuffer, MAXSIZE, sizeof(char), MAXSIZE, in); getfileDate(in,date_str);//得到本地緩存文件中的日期date_str fclose(in); //printf("date_str:%s/n", date_str); sendnewHTTP(Buffer, date_str); //向服務(wù)器發(fā)送一個請求,該請求需要增加 “If-Modified-Since” 字段 //服務(wù)器通過對比時間來判斷緩存是否過期 haveCache = TRUE; } //printf("httpHeader的url是%s,不允許訪問的是%s/n", httpHeader->url, Invilid_web); //網(wǎng)站過濾功能 if (strcmp(httpHeader->url, Invilid_web) == 0) { printf("%s網(wǎng)站被拒絕訪問", Invilid_web); goto error; } //添加釣魚功能 if (strstr(httpHeader->url, Target_web) != NULL) { printf("%s網(wǎng)站釣魚成功,被轉(zhuǎn)移至%s/n", Target_web, Fish_web); memcpy(httpHeader->host, Fish_host, strlen(Fish_host) + 1);//替換主機名 memcpy(httpHeader->url, Fish_web, strlen(Fish_web) + 1);//替換url } //此時數(shù)據(jù)報存儲在了httpHeader中 delete CacheBuffer; //連接發(fā)送數(shù)據(jù)報所在的服務(wù)器 if (!ConnectToServer(&((ProxyParam*)lpParameter)->serverSocket, httpHeader->host)) { printf("連接目的服務(wù)器失敗/n"); goto error; } printf("代理連接主機 %s 成功/n", httpHeader->host); //將客戶端發(fā)送的 HTTP 數(shù)據(jù)報文直接轉(zhuǎn)發(fā)給目標服務(wù)器 ret = send(((ProxyParam*)lpParameter)->serverSocket, Buffer, strlen(Buffer) + 1, 0); //等待目標服務(wù)器返回數(shù)據(jù) recvSize = recv(((ProxyParam*)lpParameter)->serverSocket, Buffer, MAXSIZE, 0); if (recvSize <= 0) { goto error; } //printf("服務(wù)器響應報文如下:/n%s/n", Buffer); if (haveCache == true) { checkfileCache(Buffer, httpHeader->url); } if (needCache == true) { storefileCache(Buffer, httpHeader->url); } //將目標服務(wù)器返回的數(shù)據(jù)直接轉(zhuǎn)發(fā)給客戶端 ret = send(((ProxyParam*)lpParameter)->clientSocket, Buffer, sizeof(Buffer), 0); //錯誤處理error: printf("關(guān)閉套接字/n"); Sleep(200); closesocket(((ProxyParam*)lpParameter)->clientSocket); closesocket(((ProxyParam*)lpParameter)->serverSocket); delete lpParameter; _endthreadex(0); //終止線程 return 0;}//************************************// Method: ParseHttpHead// FullName: ParseHttpHead// Access: public// Returns: void// Qualifier: 解析 TCP 報文中的 HTTP 頭部// Parameter: char * buffer// Parameter: HttpHeader * httpHeader//************************************void ParseHttpHead(char* buffer, HttpHeader* httpHeader) { char* p; char* ptr; const char* delim = "/r/n"; p = strtok_s(buffer, delim, &ptr);//提取第一行 printf("%s/n", p); if (p[0] == 'G') {//GET 方式 memcpy(httpHeader->method, "GET", 3); memcpy(httpHeader->url, &p[4], strlen(p) - 13); } else if (p[0] == 'P') {//POST 方式 memcpy(httpHeader->method, "POST", 4); memcpy(httpHeader->url, &p[5], strlen(p) - 14); } printf("%s/n", httpHeader->url); p = strtok_s(NULL, delim, &ptr); while (p) { switch (p[0]) { case 'H'://Host memcpy(httpHeader->host, &p[6], strlen(p) - 6); break; case 'C'://Cookie if (strlen(p) > 8) { char header[8]; ZeroMemory(header, sizeof(header)); memcpy(header, p, 6); if (!strcmp(header, "Cookie")) { memcpy(httpHeader->cookie, &p[8], strlen(p) - 8); } } break; default: break; } p = strtok_s(NULL, delim, &ptr); }}//************************************// Method: ConnectToServer// FullName: ConnectToServer// Access: public// Returns: BOOL// Qualifier: 根據(jù)主機創(chuàng)建目標服務(wù)器套接字,并連接// Parameter: SOCKET * serverSocket// Parameter: char * host//************************************BOOL ConnectToServer(SOCKET* serverSocket, char* host) { sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(HTTP_PORT); HOSTENT* hostent = gethostbyname(host); if (!hostent) { return FALSE; } in_addr Inaddr = *((in_addr*)* hostent->h_addr_list); serverAddr.sin_addr.s_addr = inet_addr(inet_ntoa(Inaddr)); *serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (*serverSocket == INVALID_SOCKET) { return FALSE; } if (connect(*serverSocket, (SOCKADDR*)& serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { closesocket(*serverSocket); return FALSE; } return TRUE;}//訪問本地文件,獲取本地緩存中的日期void getfileDate(FILE* in, char* tempDate) { char field[5] = "Date"; char* p, * ptr, temp[5]; char buffer[MAXSIZE]; ZeroMemory(buffer, MAXSIZE); fwrite(buffer, sizeof(char), MAXSIZE, in); const char* delim = "/r/n";//換行符 ZeroMemory(temp, 5); p = strtok_s(buffer, delim, &ptr); int len = strlen(field) + 2; while (p) { if (strstr(p, field) != NULL) {//調(diào)用strstr后指針會指向匹配剩余的第一個字符 memcpy(tempDate, &p[len], strlen(p) - len); return; } p = strtok_s(NULL, delim, &ptr); }}//改造HTTP請求報文void sendnewHTTP(char* buffer, char* datestring) { const char* field = "Host"; const char* newfield = "If-Modified-Since: "; //const char *delim = "/r/n"; char temp[MAXSIZE]; ZeroMemory(temp, MAXSIZE); char* pos = strstr(buffer, field);//獲取請求報文段中Host后的部分信息 int i = 0; for (i = 0; i < strlen(pos); i++) { temp[i] = pos[i];//將pos復制給temp } *pos = '/0'; while (*newfield != '/0') { //插入If-Modified-Since字段 *pos++ = *newfield++; } while (*datestring != '/0') {//插入對象文件的最新被修改時間 *pos++ = *datestring++; } *pos++ = '/r'; *pos++ = '/n'; for (i = 0; i < strlen(temp); i++) { *pos++ = temp[i]; }}//根據(jù)url構(gòu)造文件名void makeFilename(char* url, char* filename) { while (*url != '/0') { if ('a' <= *url && *url <= 'z') { *filename++ = *url; } url++; } strcat_s(filename, strlen(filename) + 9, "110.txt");}//檢測主機返回的狀態(tài)碼,如果是200則本地獲取緩存void storefileCache(char* buffer, char* url) { char* p, * ptr, tempBuffer[MAXSIZE + 1]; //num中是狀態(tài)碼 const char* delim = "/r/n"; ZeroMemory(tempBuffer, MAXSIZE + 1); memcpy(tempBuffer, buffer, strlen(buffer)); p = strtok_s(tempBuffer, delim, &ptr);//提取第一行 //printf("tempbuffer = %s/n", p); if (strstr(tempBuffer, "200") != NULL) { //狀態(tài)碼是200時緩存 char filename[100] = { 0 }; makeFilename(url, filename); printf("filename : %s/n", filename); FILE* out; fopen_s(&out, filename, "w+"); fwrite(buffer, sizeof(char), strlen(buffer), out); fclose(out); printf("/n===================網(wǎng)頁已經(jīng)被緩存==================/n/n"); }}//檢測主機返回的狀態(tài)碼,如果是304則從本地獲取緩存進行轉(zhuǎn)發(fā),否則需要更新緩存void checkfileCache(char* buffer, char* filename) { char* p, * ptr, tempBuffer[MAXSIZE + 1]; const char* delim = "/r/n"; ZeroMemory(tempBuffer, MAXSIZE + 1); memcpy(tempBuffer, buffer, strlen(buffer)); p = strtok_s(tempBuffer, delim, &ptr);//提取狀態(tài)碼所在行 //主機返回的報文中的狀態(tài)碼為304時返回已緩存的內(nèi)容 if (strstr(p, "304") != NULL) { printf("/n=================從本機獲得緩存====================/n/n"); ZeroMemory(buffer, strlen(buffer)); FILE* in = NULL; if ((fopen_s(&in, filename, "r")) == 0) { fread(buffer, sizeof(char), MAXSIZE, in); fclose(in); } needCache = FALSE; }}
關(guān)鍵詞:設(shè)計,實現(xiàn),服務(wù),網(wǎng)絡(luò),實驗,代理,計算機
微信公眾號
版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。