版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
网络程序设计实验报告实验名称:完成端口模型实验类型:验证型指导教师:贾浩专业班级:信安1204姓名:马灿学号:20233252电子邮件:实验地点:东6E307实验日期:实验成绩:__________________________一、实验目的掌握WinsockI/O模型工作原理;熟悉I/O模型中使用的Winsock接口函数;掌握使用I/O模型进行网络程序设计的编程步骤;二、实验设计Windows套接字工作模式Windows套接字工作模式分为两类:阻塞〔Blocking〕模式和非阻塞〔NonBlocking〕模式。在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回〔将控制权交还给程序〕,这就意味着任一个线程在某一时刻只能执行一个输入/输出〔I/O〕操作,而且应用程序很难同时通过多个建好连接的套接字进行通信。正如我们在以前的实验中看到的,效劳端或客户端在运行到recv()函数时会进入阻塞状态,直到对方响应时〔即运行了send〔〕函数后〕才能继续执行下去。在默认的情况下,套接字工作在阻塞模式。在非阻塞模式下,Winsock函数会立即返回,并交出程序的控制权,这就为我们实际需要中同时管理多个连接、并维持与每个连接的及时通信提供了根底。在实际问题中,Winsock编程经常需要使用多线程的方法使程序对用户的动作进行及时响应,但会增加一些开销,并且扩展性比拟差。尽管非阻塞模式套接字在使用上不如阻塞套接字简单,但它在功能上还是非常强大的,同时简化了我们针对实际问题的编程过程。WinsockI/O模型选择模型〔SelectModel〕异步选择模型〔WSAAsyncSelectModel〕事件选择模型〔WSAEventSelectModel〕重叠模型〔OverlappedModel〕完成端口模型〔CompletionPortModel〕选择模型Select模型是WinSock中最常见的I/O模型,通过调用Select函数可以确定一个或多个套接字的状态,判断套接字上是否存在数据,或者能否向一个套接字写入数据。该模型关键在于select函数,其函数原型如下:intselect( int nfds,//忽略fd_set*readfds,//检查可读性集合fd_set*writefds,//检查可写性集合fd_set*exceptfds,//检查错误集合conststructtimeval*timeout//等待时间);函数返回时指定的集合中保存了发生网络事件的套接字,应用程序可以对此进行判断从而确定在哪些套接字上有网络事件发生,进而进行数据或者错误处理。使用该模型的编程一般步骤如下:建立fd_set集合s,用来存放欲使用的套接字。将套接字添加到集合s中。确定要检查的套接字集合Xi〔1=<i<=3〕。使用FD_ZERO宏,初始化Xi。使用FD_SET宏,根据需要将套接字句柄添加到Xi中调用select函数根据select函数的返回值进行处理,当成功返回时,判断s中套接字是否在Xi中,并进行相应处理〔处理时可能要添加新的套接字到s中〕回到4实验内容1、在上述I/O模型中自选一个I/O模型,构建一个TCP效劳器,该效劳器能:接受客户端连接时显示客户端的IP,PORT信息接收客户端连接时显示其连接编号,客户端退出时显示关闭的连接编号能显示客户端发来的数据能从键盘输入数据并发到客户端其他数据传送功能〔可选〕2、编写客户端程序,使之能:从键盘输入数据并发送到效劳器能接收效劳器发来的数据当输入“bye〞时退出程序实验设计〔1〕.设计效劳端程序,效劳端程序首先需要创立套接字并监听,等待用户连接上后,获取客户端的IP、PORT信息,并为该连接编号,记录其编号,在客户端发来信息或退出程序时显示其连接编号,接受用户发来的信息并显示,获取用户在效劳器端的输入并发送到客户端。〔2〕.设计客户端程序,客户端首先需要创立套接字并连接到效劳器端,然后接受用户输入的数据并传输给效劳器端,等待效劳器端将发送的数据,在每次接受输入时需要判断其输入的字符串是否是“bye〞,如果是那么退出程序,否那么继续连接。三、实验过程〔包含实验结果〕.针对实验要求设计代码2.编写代码实现要求3.实验结果效劳器运行结果第一个连接效劳器的客户端运行效果第二个连接效劳器的客户端运行效果第三个连接效劳器的客户端运行效果四、讨论与分析1.你所选用的I/O模型是如何判断套接字上何时可以收发数据的或者数据收发已完成的?答:我选择的是选择模型,在使用选择模式时,会用到三个套接字集合,readfds用于检查套接字集合中套接字是否可读,writefds用于检查其可写性,最后一个excpetfds用于检查错误。当有数据可读,连接已经关闭、重启或是中断,有未决的连接请求时,readfds受信。当数据能够发送和连接成功调用connect时,writefds受信。当连接失败和OOB数据可读时,excpetfds受信。只需调用select函数将套接字集合遍历,当套接字上有数据可接受时,可得到该受信的套接字并执行相应的操作。当已经完成时,也会将该套接字从集合中删去。2.简述你所使用的I/O模型的编程步骤答:使用选择模型的编程步骤大致为:〔1〕初始化套接字结合fdSocket,向这个集合添加监听套接字句柄;〔2〕将fdSocket集合的拷贝fdRead传递给select函数,当有事件发生时,select函数移除fdRead中没有未决I/O操作的套接字句柄,然后返回;〔3〕比拟原来fdSocket集合与select处理过的fdRead集合,确定哪些套接字有未决的I/O,并进一步处理这些I/O';〔4〕回到第二步继续处理。3.在你所使用的I/O中如何判断发生网络事件或者IO完成的套接字?答:选择模型是通过select函数去处理套接字集合,并将其中没有未决I/O的套接字,即IO完成的套接字移除,然后遍历处理后的套接字集合,此时套接字集合中只剩下有网络事件的套接字。五、实验者自评〔从实验设计、实验过程、对实验知识点的理解上给出客观公正的自我评价〕通过本次实验,对WinsockI/O模型工作原理有了进一步了解和熟悉,并熟悉了I/O模型中使用的Winsock接口函数,掌握了使用I/O模型进行网络程序设计的编程步骤。尤其是对选择模型有了深入的了解,掌握了其判断套接字上何时可以收发数据的或者数据收发已完成的原理,判断发生网络事件或者IO完成的套接字的原理,对其他模型的理解也更加深入。六、附录:关键代码〔给出适当注释,可读性高〕客户端//#include"stdafx.h"#include<WINSOCK2.H>#include<stdio.h>#pragmacomment(lib,"ws2_32.lib")#definePORT4567#defineMSGSIZE1024intmain(){ WSADATAwsaData; SOCKETsClient; SOCKADDR_INserver; SOCKADDR_INclient; charszMessage[MSGSIZE]; //USHORTport1=5555; intret; WSAStartup(0x0202,&wsaData); sClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); memset(&server,0,sizeof(SOCKADDR_IN)); server.sin_family=AF_INET; server.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); server.sin_port=htons(PORT); client.sin_family=AF_INET; client.sin_addr.S_un.S_addr=INADDR_ANY;//inet_addr("127.1.1.2");//INADDR_ANY; //client.sin_port=htons(port1);/* if(::bind(sClient,(sockaddr*)&client,sizeof(client))==SOCKET_ERROR) { printf("Failedbind()\n"); return-1; }*/ if(::connect(sClient,(sockaddr*)&server,sizeof(SOCKADDR_IN))==SOCKET_ERROR) { printf("Connectfail."); } while(TRUE) { printf("Send:"); gets(szMessage); send(sClient,szMessage,strlen(szMessage),0); ret=recv(sClient,szMessage,MSGSIZE,0); szMessage[ret]='\0'; printf("Received[%dbytes]:'%s'\n",ret,szMessage); } closesocket(sClient); WSACleanup();return0;}效劳器//EventSelectServer.h文件DWORDWINAPIServerThread(LPVOIDlpParam);//套节字对象typedefstruct_SOCKET_OBJ{ SOCKETs; //套节字句柄 HANDLEevent; //与此套节字相关联的事件对象句柄 sockaddr_inaddrRemote; //客户端地址信息 _SOCKET_OBJ*pNext; //指向下一个SOCKET_OBJ对象,为的是连成一个表}SOCKET_OBJ,*PSOCKET_OBJ;//线程对象typedefstruct_THREAD_OBJ{ HANDLEevents[WSA_MAXIMUM_WAIT_EVENTS]; //记录当前线程要等待的事件对象的句柄 intnSocketCount; //记录当前线程处理的套节字的数量<=WSA_MAXIMUM_WAIT_EVENTS PSOCKET_OBJpSockHeader; //当前线程处理的套节字对象列表,pSockHeader指向表头 PSOCKET_OBJpSockTail; //pSockTail指向表尾 CRITICAL_SECTIONcs; //关键代码段变量,为的是同步对本结构的访问 _THREAD_OBJ*pNext; //指向下一个THREAD_OBJ对象,为的是连成一个表}THREAD_OBJ,*PTHREAD_OBJ;//线程列表PTHREAD_OBJg_pThreadList; //指向线程对象列表表头CRITICAL_SECTIONg_cs; //同步对此全局变量的访问//状态信息LONGg_nTatolConnections; //总共连接数量LONGg_nCurrentConnections; //当前连接数量//申请一个套节字对象,初始化它的成员PSOCKET_OBJGetSocketObj(SOCKETs) { PSOCKET_OBJpSocket=(PSOCKET_OBJ)::GlobalAlloc(GPTR,sizeof(SOCKET_OBJ));////调用GlobalAlloc函数分配一块内存,该函数会返回分配的内存句柄。 if(pSocket!=NULL) { pSocket->s=s; pSocket->event=::WSACreateEvent(); } returnpSocket;}//释放一个套节字对象voidFreeSocketObj(PSOCKET_OBJpSocket){ ::CloseHandle(pSocket->event); if(pSocket->s!=INVALID_SOCKET) { ::closesocket(pSocket->s); } ::GlobalFree(pSocket);}//申请一个线程对象,初始化它的成员,并将它添加到线程对象列表中PTHREAD_OBJGetThreadObj(){ PTHREAD_OBJpThread=(PTHREAD_OBJ)::GlobalAlloc(GPTR,sizeof(THREAD_OBJ)); if(pThread!=NULL) { ::InitializeCriticalSection(&pThread->cs); //创立一个事件对象,用于指示该线程的句柄数组需要重组 pThread->events[0]=::WSACreateEvent(); //将新申请的线程对象添加到列表中 ::EnterCriticalSection(&g_cs); pThread->pNext=g_pThreadList; g_pThreadList=pThread; ::LeaveCriticalSection(&g_cs); } returnpThread;}//释放一个线程对象,并将它从线程对象列表中移除voidFreeThreadObj(PTHREAD_OBJpThread){ //在线程对象列表中查找pThread所指的对象,如果找到就从中移除 ::EnterCriticalSection(&g_cs); PTHREAD_OBJp=g_pThreadList; if(p==pThread) //是第一个? { g_pThreadList=p->pNext; } else { while(p!=NULL&&p->pNext!=pThread) { p=p->pNext; } if(p!=NULL) { //此时,p是pThread的前一个,即“p->pNext==pThread〞 p->pNext=pThread->pNext; } } ::LeaveCriticalSection(&g_cs); //释放资源 ::CloseHandle(pThread->events[0]); ::DeleteCriticalSection(&pThread->cs); ::GlobalFree(pThread);}//重新建立线程对象的events数组voidRebuildArray(PTHREAD_OBJpThread) { ::EnterCriticalSection(&pThread->cs); PSOCKET_OBJpSocket=pThread->pSockHeader; intn=1; //从第1个开始写,第0个用于指示需要重建了 while(pSocket!=NULL) { pThread->events[n++]=pSocket->event; pSocket=pSocket->pNext; } ::LeaveCriticalSection(&pThread->cs);}//向一个线程的套节字列表中插入一个套节字BOOLInsertSocketObj(PTHREAD_OBJpThread,PSOCKET_OBJpSocket){ BOOLbRet=FALSE; ::EnterCriticalSection(&pThread->cs); if(pThread->nSocketCount<WSA_MAXIMUM_WAIT_EVENTS-1) { if(pThread->pSockHeader==NULL) { pThread->pSockHeader=pThread->pSockTail=pSocket; } else { pThread->pSockTail->pNext=pSocket; pThread->pSockTail=pSocket; } pThread->nSocketCount++; bRet=TRUE; } ::LeaveCriticalSection(&pThread->cs); //插入成功,说明成功处理了客户的连接请求 if(bRet) { ::InterlockedIncrement(&g_nTatolConnections); ::InterlockedIncrement(&g_nCurrentConnections); } returnbRet;}//将一个套节字对象安排给空闲的线程处理voidAssignToFreeThread(PSOCKET_OBJpSocket){ pSocket->pNext=NULL; ::EnterCriticalSection(&g_cs); PTHREAD_OBJpThread=g_pThreadList; //试图插入到现存线程 while(pThread!=NULL) { if(InsertSocketObj(pThread,pSocket)) break; pThread=pThread->pNext; } //没有空闲线程,为这个套节字创立新的线程 if(pThread==NULL) { pThread=GetThreadObj(); InsertSocketObj(pThread,pSocket); ::CreateThread(NULL,0,ServerThread,pThread,0,NULL); } ::LeaveCriticalSection(&g_cs); //指示线程重建句柄数组 ::WSASetEvent(pThread->events[0]);}//从给定线程的套节字对象列表中移除一个套节字对象voidRemoveSocketObj(PTHREAD_OBJpThread,PSOCKET_OBJpSocket){ ::EnterCriticalSection(&pThread->cs); //在套节字对象列表中查找指定的套节字对象,找到后将之移除 PSOCKET_OBJpTest=pThread->pSockHeader; if(pTest==pSocket) { if(pThread->pSockHeader==pThread->pSockTail) pThread->pSockTail=pThread->pSockHeader=pTest->pNext; else pThread->pSockHeader=pTest->pNext; } else { while(pTest!=NULL&&pTest->pNext!=pSocket) pTest=pTest->pNext; if(pTest!=NULL) { if(pThread->pSockTail==pSocket) pThread->pSockTail=pTest; pTest->pNext=pSocket->pNext; } } pThread->nSocketCount--; ::LeaveCriticalSection(&pThread->cs); //指示线程重建句柄数组 ::WSASetEvent(pThread->events[0]); //说明一个连接中断 ::InterlockedDecrement(&g_nCurrentConnections);}BOOLHandleIO(PTHREAD_OBJpThread,PSOCKET_OBJpSocket){ //获取具体发生的网络事件 WSANETWORKEVENTSevent; ::WSAEnumNetworkEvents(pSocket->s,pSocket->event,&event); do { if(event.lNetworkEvents&FD_READ) //套节字可读 { if(event.iErrorCode[FD_READ_BIT]==0) { charszText[256]; intnRecv=::recv(pSocket->s,szText,strlen(szText),0); if(nRecv>0) { szText[nRecv]='\0'; printf("接收到数据:%s\n",szText); //send(pSocket->s,szText,strlen(szText),0); } } else break; } elseif(event.lNetworkEvents&FD_CLOSE) //套节字关闭 { break; } elseif(event.lNetworkEvents&FD_WRITE) //套节字可写 { if(event.iErrorCode[FD_WRITE_BIT]==0) { send(pSocket->s,"接收到数据",strlen("接收到数据"),0); } else break; } returnTRUE; } while(FALSE); //套节字关闭,或者有错误发生,程序都会转到这里来执行 RemoveSocketObj(pThread,pSocket); FreeSocketObj(pSocket); returnFALSE;}PSOCKET_OBJFindSocketObj(PTHREAD_OBJpThread,intnIndex)//nIndex从1开始{ //在套节字列表中查找 PSOCKET_OBJpSocket=pThread->pSockHeader; while(--nIndex) { if(pSocket==NULL) returnNULL; pSocket=pSocket->pNext; } returnpSocket;}DWORDWINAPIServerT
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 个人购房合同法律文本模板
- 不锈钢板材购销合同模板
- 中小企业标准劳动合同样本
- 2025年充电桩运营合作协议指南
- 专业私教与会员间的健身合同协议
- 2025年标准版建筑工程施工专业分包协议
- 二手房买卖合同模板「权威」
- 上海市种子采购合同模板
- 个人房屋租赁合同格式范本
- DB6103T 77-2025 酿酒高粱宽窄行栽培技术规范
- 浙教版七年级数学下册单元测试题及参考答案
- 华为人才发展与运营管理
- 2024年广州金融控股集团有限公司招聘笔试冲刺题(带答案解析)
- 卓有成效的管理者读后感3000字
- 七年级下册-备战2024年中考历史总复习核心考点与重难点练习(统部编版)
- 岩土工程勘察服务投标方案(技术方案)
- 实验室仪器设备验收单
- 新修订药品GMP中药饮片附录解读课件
- 蒙特利尔认知评估量表北京版
- 领导干部个人有关事项报告表(模板)
- GB/T 7631.18-2017润滑剂、工业用油和有关产品(L类)的分类第18部分:Y组(其他应用)
评论
0/150
提交评论