华中科技大学计算机学院《计算机网络》实验报告实验名称网络组建与实施及Socket编程团队成员:注:团队成员贡献百分比之和为1教师评语:实验一——网络组建与实施一.环境操作系统:WINDOWS7 (32位旗舰版)工具:Boson Netsim仿真软件(版本号5.31)二.实验目的1.了解IP协议,网络层协议和数据链路层协议的工作原理机以及工作机制2.掌握IP地址的规划方法3.掌握路由协议的配置方法4.掌握路由器以及二/三层交换机的配置方法5.了解VLAN的划分原理6.掌握访问控制的配制方法三.实验内容及步骤(包括主要流程和说明)实验所用拓扑图如下:图一:图二第一项试验——组网试验1. 设置PC1、PC2的ip分别为192.168.0.1、192.168.0.2,子网掩码为255.255.255.0,网关为192.168.0.254;设置PC3~PC8的ip分别为192.168.1.1~192.168.1.6,子网掩码为255.255.255.0,网关为192.168.1.254;设置Router A的端口地址为ether 0 192.168.0.254,ether 1 192.168.1.254。
测试结果为各PC机之间可以自由通信,部分结果如图1-1所示。
图1-1 (a)图1-1 (b)2.将PC4、PC6、PC8的ip地址改为192.168.2.2、192.168.2.4、192.168.2.6,网关改为192.168.2.254,其他设置不变。
PC4,PC6,PC8之间能互相通信,不能发送数据到其他PC;其他PC之间可以互相通信;PC1,PC2不能发送数据给PC4,PC6,PC8,而PC3,PC5,PC7可以发送数据给PC4,PC6,PC8。
部分结果如图1-2所示。
结果分析:路由器ip为PC3,PC5,PC7的默认网关,PC3,PC5,PC7可以通过路由器访问其他PC,也可以被PC1,PC2访问,而PC4,PC6,PC8无法通过路由器访问其他子网段PC图1-2 (a) PC3的ping结果图1-2 (b) PC1的ping结果图1-2(c) PC4的ping结果第二项试验——路由配置试验1.首先按如下要求配置各PC的IP地址2.路由器配置如下(Router A配置如下,其它路由器配置类似)3.RIP协议配置(Router A配置如下,其它路由器配置类似)4.思考题如果不设置时钟频率,各PC无法相互访问OSPF协议配置(Router A配置如下,其它路由器配置类似)结果是各PC能相互访问,部分结果见图2-1。
图3-1 (a) PC2的ping结果第三项试验——VLAN划分试验1.在switch2,switch3,switch4上设置一个名为class的VTP域,并创建一个名为Class1的VLAN2以及名为Class2的VLAN3.2.将switch2,switch3,switch4的各个端口分配到新创建的相应VLAN中。
(switch2的配置如下,switch3,switch4的配置与之类似,端口为fa0/3和fa0/4)3.配置switch2,switch3,switch4之间的trunk链路。
(switch2的配置如下,switch3,switch4的配置与之类似,端口为fa0/1和fa0/2)此时,PC3,PC5,PC7处于VLAN2,PC4,PC6,PC8处于VLAN3,VLAN2和VLAN3内部PC可以互相访问,但VLAN之间无法访问。
部分结果见图3-1。
图3-1 (a) PC3的ping结果图3-1 (b) PC4的ping结果第四项试验——访问控制配置试验1.对路由器Router A进行配置,使得PC1无法访问其它PC,也不能被其它PC访问部分结果见图4-1。
图4-1 (a) PC1的ping结果图4-1 (b) PC2的ping结果实验二——支持多线程处理的web服务服务端软件一.环境操作系统:WINDOWS 7 (32位旗舰版)运行工具:chrome浏览器编写程序语言:C++环境:Qt Creator 5.1.0二.实验目的1.了解应用层和运输层的作用以及相关协议的工作原理和机制2.掌握SOCKET编程的基本方法三.实验内容及步骤(包括主要流程和说明)内容:编写一个支持多线程处理的web服务服务端软件要求如下:第一级:1.可配置Web服务器的IP地址、监听端口和主目录2.制作一个主页放在主目录中,并配置Web服务器的IP地址、监听端口和主目录3.具备完成所需功能的基本图形用户界面(GUI),并具友好性第二级:1.选择合适的Socket编程模型,创建套接字,监听所配置的监听端口2.支持服务的启动和关闭3. 具备完成所需功能的基本图形用户界面(GUI),并具友好性第三级:1.支持多线程,能够针对每一个新的请求创建新的线程2.在服务端的屏幕上输出每一个请求的来源(IP地址、端口号和HTTP请求命令行)3.支持一定的异常情况处理能力4.具备完成所需功能的基本图形用户界面(GUI),并具友好性第四级:1.能够处理HTTP请求以及定位相应的HTML文件2.向客户端发送响应报文3.对于无法成功定位文件的请求,根据错误原因,做相应的错误提示4.在服务端的屏幕上能够输出对每一个请求处理的结果5.具备完成所需功能的基本图形用户界面(GUI),并具友好性第五级:1.支持多种类型文件(如TXT和JPG等)的发送2.具备简洁方便的GUI界面步骤:代码实现部分1.启动服务:BOOL QHttpServer::Start(string HomeDir, string DefIndex, int Port, int PersTO){m_HomeDir = HomeDir;m_DefIndex = DefIndex;if(m_HomeDir.substr(m_HomeDir.size() - 1, 1) != "\\")m_HomeDir += "\\";if(bRun){//LogMessage(LOGFILENAME, "_beginthreadex(...) failure, for Launch Thread", "Run", errno);return FALSE;}ServerPort = Port;PersistenceTO = PersTO;InitializeCriticalSection(&cs);InitializeCriticalSection(&_cs);Reset();ThreadLaunchedEvent = CreateEvent(NULL, FALSE, TRUE, NULL);// 启动接收线程ResetEvent(ThreadLaunchedEvent);ThreadA = (HANDLE)_beginthreadex(NULL, 0, AcceptThread, this, 0, &ThreadA_ID);if(!ThreadA){//LogMessage(LOGFILENAME, "_beginthreadex(...) failure, for Launch Thread", "Run", errno);return FALSE;}if(WaitForSingleObject(ThreadLaunchedEvent, THREADWAIT_TO) != WAIT_OBJECT_0) {//LogMessage(LOGFILENAME, "Unable to get response from Accept Thread withing specified Timeout ->", "Run", THREADWAIT_TO);CloseHandle(ThreadLaunchedEvent);return FALSE;}CloseHandle(ThreadLaunchedEvent);bRun = TRUE;return TRUE;// return Run(Port, PersTO);}2.关闭服务:BOOL QHttpServer::Shutdown(){if(!bRun)return FALSE;BOOL bResult = TRUE;HANDLE hArray[2];hArray[0] = ThreadA;hArray[1] = ThreadC;//// 关闭接收和helper线程//SetEvent(ShutdownEvent);DWORD n = WaitForMultipleObjects(2, hArray, TRUE, THREADKILL_TO);if(n == WAIT_TIMEOUT || n == WAIT_FAILED){//LogMessage(LOGFILENAME, "WaitForMultipleObjects(...) timed out", "Shutdown");//if(!TerminateThread(ThreadA, THREADEXIT_SUCCESS))//LogMessage(LOGFILENAME, "TerminateThread(.ThreadA.) failure, probably it is already terminated", "Shutdown", GetLastError());//if(!TerminateThread(ThreadC, THREADEXIT_SUCCESS))//LogMessage(LOGFILENAME, "TerminateThread(.ThreadC.) failure, probably it is already terminated", "Shutdown", GetLastError());bResult = FALSE;}CloseHandle(ThreadA);CloseHandle(ThreadC);//// 所有的客户线程都结束//THREADLIST::iterator it;while(ThreadList.size()){Sleep(100);}DeleteCriticalSection(&cs);DeleteCriticalSection(&_cs);bRun = FALSE;return bResult;}3.增加新链接:BOOL QHttpServer::AddClient(SOCKET s, char* clientAddress, int port){VISVECTOR::iterator it;it = find(Visitors.begin(), Visitors.end(), clientAddress);//查找重复IPif(it == Visitors.end()){Visitors.push_back(clientAddress);//增加新客户IPvisitsip.push_back(clientAddress);//输出用}InterlockedIncrement(&Stats.nTotalHits);//互斥ThreadTag t hreadTag;HANDLE hThread;unsigned int threadID;//进入临界区并创建连接对象EnterCriticalSection(&cs);NewConnectionTag *NewConn = new NewConnectionTag;NewConn->pHttpServer = this;NewConn->s = s;hThread = (HANDLE)_beginthreadex(NULL, 0, ClientThread, NewConn, 0, &threadID);if(hThread){threadTag.threadID = threadID;threadTag.hThread = hThread;ThreadList.push_back(threadTag);}elseserverlog.push_back("_beginthreadex(...) failure");//else//LogMessage(LOGFILENAME, "_beginthreadex(...) failure", "AddClient", errno);LeaveCriticalSection(&cs);return TRUE;}4.分析请求数据:BOOL QHttpServer::ParseRequest(string szRequest, string &szResponse, BOOL &bKeepAlive) {string szMethod;string szFileName;string szFileExt;string szStatusCode("200 OK");string szContentType("text/html");string szConnectionType("close");string szNotFoundMessage;string szDateTime;char pResponseHeader[2048];fpos_t lengthActual = 0, length = 0;char *pBuf = NULL;int n;//// 检查提交方法//n = szRequest.find(" ", 0);if(n != string::npos){szMethod = szRequest.substr(0, n);if(szMethod == "GET"){//// 获取文件名//int n1 = szRequest.find(" ", n + 1);if(n != string::npos){szFileName = szRequest.substr(n + 1, n1 - n - 1);if(szFileName == "/"){szFileName = m_DefIndex;}}else{return FALSE;}}else{szStatusCode = "501 Not Implemented";szFileName = ERROR501;}}else{return FALSE;}//// 分析链接类型//n = szRequest.find("\nConnection: Keep-Alive", 0);if(n != string::npos)bKeepAlive = TRUE;//// 分析内容类型//int nPointPos = szFileName.rfind(".");if(nPointPos != string::npos){szFileExt = szFileName.substr(nPointPos + 1, szFileName.size());strlwr((char*)szFileExt.c_str());MIMETYPES::iterator it;it = MimeTypes.find(szFileExt);if(it != MimeTypes.end())szContentType = (*it).second;}//得到目前的时间//char szDT[128];struct tm *newtime;time_t ltime;time(<ime);newtime = gmtime(<ime);strftime(szDT, 128,"%a, %d %b %Y %H:%M:%S GMT", newtime);//// 读取文件//FILE *f;f = fopen((m_HomeDir + szFileName).c_str(), "r+b");if(f != NULL){// 获得文件大小fseek(f, 0, SEEK_END);fgetpos(f, &lengthActual);fseek(f, 0, SEEK_SET);pBuf = new char[lengthActual + 1];length = fread(pBuf, 1, lengthActual, f);fclose(f);//// 返回响应//sprintf(pResponseHeader, "HTTP/1.0 %s\r\nDate: %s\r\nServer: %s\r\nAccept-Ranges: bytes\r\nContent-Length: %d\r\nConnection: %s\r\nContent-Type: %s\r\n\r\n",szStatusCode.c_str(), szDT, SERVERNAME, (int)length, bKeepAlive ? "Keep-Alive" : "close", szContentType.c_str());}else{//// 如果文件没有找到//f = fopen((m_HomeDir + ERROR404).c_str(), "r+b");if(f != NULL){// 获取文件大小fseek(f, 0, SEEK_END);fgetpos(f, &lengthActual);fseek(f, 0, SEEK_SET);pBuf = new char[lengthActual + 1];length = fread(pBuf, 1, lengthActual, f);fclose(f);szNotFoundMessage = string(pBuf, length);delete pBuf;pBuf = NULL;}szStatusCode = "404 Resource not found";sprintf(pResponseHeader, "HTTP/1.0 %s\r\nContent-Length: %d\r\nContent-Type:text/html\r\nDate: %s\r\nServer: %s\r\n\r\n%s",szStatusCode.c_str(), szNotFoundMessage.size(), szDT, SERVERNAME, szNotFoundMessage.c_str());bKeepAlive = FALSE;}szResponse = string(pResponseHeader);if(pBuf)szResponse += string(pBuf, length);delete pBuf;pBuf = NULL;return TRUE;}5.多线程的实现:UINT__stdcall QHttpServer::AcceptThread(LPVOID pParam){QHttpServer *pHttpServer = (QHttpServer*)pParam;SOCKET s; // 主线程WORD wVersionRequested;WSADATA wsaData;sockaddr_in saLocal;WSAEVENT Handles[2];WSANETWORKEVENTS NetworkEvents;sockaddr ClientAddr;INT addrlen = sizeof(ClientAddr);sockaddr_in sain;char cAddr[50];int result;saLocal.sin_family = AF_INET;saLocal.sin_port = htons(pHttpServer->ServerPort);saLocal.sin_addr.s_addr = INADDR_ANY;//(ULONG)(pHttpServer->ipcon);wVersionRequested = MAKEWORD(2, 2);result = WSAStartup(wVersionRequested, &wsaData);if(result != 0){//pHttpServer->LogMessage(LOGFILENAME, "WSAStartup(...) failure", "AcceptThread", result);serverlog.push_back("WSAStartup(...) failure");return THREADEXIT_SUCCESS;}if( LOBYTE(wsaData.wVersion) != 2 ||HIBYTE(wsaData.wVersion) != 2){//pHttpServer->LogMessage(LOGFILENAME, "Requested Socket version not exist", "AcceptThread");serverlog.push_back("Requested Socket version not exist");pHttpServer->CleanupThread(NULL, NULL, NULL);return THREADEXIT_SUCCESS;}s = WSASocket(AF_INET, SOCK_STREAM, 0, (LPWSAPROTOCOL_INFO)NULL, 0,WSA_FLAG_OVERLAPPED);if(s == INVALID_SOCKET){//pHttpServer->LogMessage(LOGFILENAME, "WSASocket(...) failure", "AcceptThread", WSAGetLastError());serverlog.push_back("WSASocket(...) failure");pHttpServer->CleanupThread(NULL, NULL, NULL);return THREADEXIT_SUCCESS;}//// 绑定//result = bind(s, (struct sockaddr *)&saLocal, sizeof(saLocal));if(result == SOCKET_ERROR){//pHttpServer->LogMessage(LOGFILENAME, "bind(...) failure", "AcceptThread", WSAGetLastError());serverlog.push_back("bind(...) failure");pHttpServer->CleanupThread(NULL, NULL, s);return THREADEXIT_SUCCESS;}//// 侦听//result = listen(s, SOMAXCONN);if(result == SOCKET_ERROR){//pHttpServer->LogMessage(LOGFILENAME, "listen(...) failure", "AcceptThread", WSAGetLastError());serverlog.push_back("listen(...) failure");pHttpServer->CleanupThread(NULL, NULL, s);return THREADEXIT_SUCCESS;}pHttpServer->ShutdownEvent = WSACreateEvent();if(pHttpServer->ShutdownEvent == WSA_INVALID_EVENT){//pHttpServer->LogMessage(LOGFILENAME, "WSACreateEvent(...) failure for ShutdownEvent", "AcceptThread", WSAGetLastError());serverlog.push_back("WSACreateEvent(...) failure for ShutdownEvent");pHttpServer->CleanupThread(NULL, NULL, NULL, s);return THREADEXIT_SUCCESS;}//创建事件WSAEVENT Event = WSACreateEvent();if(Event == WSA_INVALID_EVENT){//pHttpServer->LogMessage(LOGFILENAME, "WSACreateEvent(...) failure for Event", "AcceptThread", WSAGetLastError());serverlog.push_back("WSACreateEvent(...) failure for Event");pHttpServer->CleanupThread(NULL, pHttpServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;}Handles[0] = pHttpServer->ShutdownEvent;Handles[1] = Event;result = WSAEventSelect(s, Event, FD_ACCEPT);if(result == SOCKET_ERROR){//pHttpServer->LogMessage(LOGFILENAME, "WSAEventSelect(...) failure", "AcceptThread", WSAGetLastError());serverlog.push_back("WSAEventSelect(...) failure");pHttpServer->CleanupThread(Event, pHttpServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;}SetEvent(pHttpServer->ThreadLaunchedEvent);for(;;){DWORD EventCaused = WSAWaitForMultipleEvents(2,Handles,FALSE,WSA_INFINITE,FALSE);if(EventCaused == WAIT_FAILED || EventCaused == WAIT_OBJECT_0){if(EventCaused == WAIT_FAILED){serverlog.push_back("WaitForMultipleObjects(...) failure");}//pHttpServer->LogMessage(LOGFILENAME, "WaitForMultipleObjects(...) failure", "AcceptThread", GetLastError());pHttpServer->CleanupThread(Event, pHttpServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;}result = WSAEnumNetworkEvents(s,Event,&NetworkEvents);if(result == SOCKET_ERROR){//pHttpServer->LogMessage(LOGFILENAME, "WSAEnumNetworkEvents(...) failure", "AcceptThread", WSAGetLastError());serverlog.push_back("WSAEnumNetworkEvents(...) failure");pHttpServer->CleanupThread(Event, pHttpServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;}if(NetworkEvents.lNetworkEvents == FD_ACCEPT){SOCKET ClientSocket = WSAAccept(s, &ClientAddr, &addrlen, NULL, NULL);memcpy(&sain, &ClientAddr, addrlen);sprintf(cAddr, "%d.%d.%d.%d",sain.sin_addr.S_un.S_un_b.s_b1,sain.sin_addr.S_un.S_un_b.s_b2,sain.sin_addr.S_un.S_un_b.s_b3,sain.sin_addr.S_un.S_un_b.s_b4);if(INVALID_SOCKET == ClientSocket){//pHttpServer->LogMessage(LOGFILENAME, "WSAAccept(...) failure", "AcceptThread", WSAGetLastError());serverlog.push_back("WSAAccept(...) failure");// 有一个文件错误continue;}else{if(!pHttpServer->AddClient(ClientSocket, cAddr, sain.sin_port)){//pHttpServer->LogMessage(LOGFILENAME, "AddClient(...) failure", "AcceptThread");serverlog.push_back("AddClient(...) failure");continue; // I think there is no reason to shutdown whole server if just one connection failed}}}}pHttpServer->CleanupThread(Event, pHttpServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;四.实验结果1.配置界面:IP配置,监听端口配置,主目录配置2.服务器开启后主页(因未进行中文解码,导致中文显示乱码)3.开启与关闭4.多线程访问并输出请求来源以及请求处理的结果5.访问不同类型文件TXT文件:JPG文件:6.根据错误原因作相应提示7.服务器关闭五.实验中的问题及心得本次实验对于我们来说是一个考验,第一个实验本身的内容并不复杂,但是要求实验时足够严谨,稍有疏忽而产生小纰漏的话,最后就会前功尽弃。