Windows网络编程 实验五 IO模型实验报告.docx

上传人:b****5 文档编号:7568368 上传时间:2023-05-11 格式:DOCX 页数:21 大小:218.70KB
下载 相关 举报
Windows网络编程 实验五 IO模型实验报告.docx_第1页
第1页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第2页
第2页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第3页
第3页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第4页
第4页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第5页
第5页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第6页
第6页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第7页
第7页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第8页
第8页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第9页
第9页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第10页
第10页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第11页
第11页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第12页
第12页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第13页
第13页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第14页
第14页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第15页
第15页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第16页
第16页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第17页
第17页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第18页
第18页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第19页
第19页 / 共21页
Windows网络编程 实验五 IO模型实验报告.docx_第20页
第20页 / 共21页
亲,该文档总共21页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

Windows网络编程 实验五 IO模型实验报告.docx

《Windows网络编程 实验五 IO模型实验报告.docx》由会员分享,可在线阅读,更多相关《Windows网络编程 实验五 IO模型实验报告.docx(21页珍藏版)》请在冰点文库上搜索。

Windows网络编程 实验五 IO模型实验报告.docx

Windows网络编程实验五IO模型实验报告

网络程序设计

实验报告

实验名称:

_______IO模型实验__________

实验类型:

_______验证型实验______

指导教师:

_______

专业班级:

____________________

姓名:

_____________________

学号:

___________________

电子邮件:

_________

实验地点:

__________________

实验日期:

  年 月日

 

实验成绩:

__________________________

一、实验目的

掌握WinsockI/O模型工作原理;

熟悉I/O模型中使用的Winsock接口函数;

掌握使用I/O模型进行网络程序设计的编程步骤;

二、实验设计

1.背景知识

Windows套接字工作模式

Windows套接字工作模式分为两类:

阻塞(Blocking)模式和非阻塞(NonBlocking)模式。

在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回(将控制权交还给程序),这就意味着任一个线程在某一时刻只能执行一个输入/输出(I/O)操作,而且应用程序很难同时通过多个建好连接的套接字进行通信。

正如我们在以前的实验中看到的,服务端或客户端在运行到recv()函数时会进入阻塞状态,直到对方响应时(即运行了send()函数后)才能继续执行下去。

在默认的情况下,套接字工作在阻塞模式。

在非阻塞模式下,Winsock函数会立即返回,并交出程序的控制权,这就为我们实际需要中同时管理多个连接、并维持与每个连接的及时通信提供了基础。

在实际问题中,Winsock编程经常需要使用多线程的方法使程序对用户的动作进行及时响应,但会增加一些开销,并且扩展性比较差。

尽管非阻塞模式套接字在使用上不如阻塞套接字简单,但它在功能上还是非常强大的,同时简化了我们针对实际问题的编程过程。

WinsockI/O模型

1)选择模型(SelectModel)

2)异步选择模型(WSAAsyncSelectModel)

3)事件选择模型(WSAEventSelectModel)

4)重叠模型(OverlappedModel)

5)完成端口模型(CompletionPortModel)

选择模型

Select模型是WinSock中最常见的I/O模型,通过调用Select函数可以确定一个或多个套接字的状态,判断套接字上是否存在数据,或者能否向一个套接字写入数据。

该模型关键在于select函数,其函数原型如下:

intselect(

intnfds,//忽略

fd_set*readfds,//检查可读性集合

fd_set*writefds,//检查可写性集合

fd_set*exceptfds,//检查错误集合

conststructtimeval*timeout//等待时间);

函数返回时指定的集合中保留了发生网络事件的套接字,应用程序可以对此进行判断从而确定在哪些套接字上有网络事件发生,进而进行数据或者错误处理。

使用该模型的编程一般步骤如下:

1.建立fd_set集合s,用来存放欲使用的套接字。

2.将套接字添加到集合s中。

3.确定要检查的套接字集合Xi(1=

4.使用FD_ZERO宏,初始化Xi。

5.使用FD_SET宏,根据需要将套接字句柄添加到Xi中

6.调用select函数

7.根据select函数的返回值进行处理,当成功返回时,判断s中套接字是否在Xi中,并进行相应处理(处理时可能要添加新的套接字到s中)

8.回到4

●异步选择模型

WSAAsyncSelect模型是WinSock中另一个常用的异步I/O模型,该模型可在套接字上接收以Windows消息为基础的网络事件通知,通过调用WSAAsyncSelect函数自动将套接字设置为非阻塞模式,并向WinSockDLL注册一个或多个感兴趣的网络事件,同时提供接收通知时使用的窗口句柄,当注册的网络事件发生时,对应的窗口将收到一个基于消息的通知。

WSAAsyncSelect函数原型如下:

intWSAAsyncSelect(SOCKETs,HWNDhWnd,unsignedintuMsg,longlEvent);

其中s就是我们想要的那个套接字;hWnd是接收消息通知那个窗口句柄;wMsg参数指定在发生网络事件时要接受的消息,通常设成比WM_USER大的一个值,以避免消息冲突;lEvent指定了一个位掩码,对应一系列网络事件的组合,见表3-1所示。

事件含义对照表

lEvent

含义

FD_READ

程序想要接收有关是否可读的通知,以便读入数据

FD_WRITE

程序想要接收有关是否可写的通知,以便写入数据

FD_OOB

程序想要接收是否有OOB数据到达的通知

FD_ACCEPT

程序想要接收与进入连接有关的通知

FD_CONNECT

程序想要接收与一次连接或多点接入有关的通知

FD_CLOSE

程序想要接收与套接字关闭有关的通知

FD_QOS

程序想要接收套接字"服务质量(QoS)"发生变化的通知

FD_GROUP_QOS

暂时没用,属于保留事件

FD_ROUTING_INTERFACE_CHANGE

程序想要接收有关到指定地址的路由接口发生变化的通知

FD_ADDRESS_LIST_CHANGE

程序想要接收本地地址变化的通知

当程序在一个套接字上调用WSAAsyncSelect成功后,这个程序就会在与hWnd窗口句柄对应的窗口例程中以Windows消息的形式接收网络事件通知。

窗口例程通常定义成这个样子:

LRESULTCALLBACKWndProc(HWNDhwnd,UINTmessage,WPARAMwParam,LPARAMlParam)

其中wParam参数指定在其上面发生了一个网络事件的套接字,如果定义了多个套接字,这个参数就显得很重要了。

lParam参数则包含了两方面的重要信息,它的低位字指定了已经发生的网络事件,而高位字包含了可能出现的错误代码。

简单的来说,这个模型的具体使用流程就是:

当网络消息抵达一个窗口例程后,程序要先检测lParam的高位字节,从而判断是否在套接字上面发生了网络错误。

现成的宏已经有在这里了-->WSAGETSELECTERROR,可以用它返回高字节包含的错误信息,如果没有发现任何的错误,接下来就是确定究竟是什么类型的网络事件触发了这条Windows消息,这个操作也有现成的宏-->WSAGETSELECTEVENT。

使用WSAAsyncSelect模型编程步骤:

1.winsock初始化

2.自定义WM_SOCKET消息

3.创建窗口

4.创建套接字

5.调用WSAAsyncSelect()

6.编写WindowProc()

●事件选择模型

WSAEventSelect模型是WinSock提供的另一个异步I/O模型,与WSAAsyncSelect模型类似,也允许应用程序在一个或多个套接字上接收以事件为基础的网络事件通知,并且支持的网络事件与WSAAsyncSelect模型一样。

与WSAAsyncSelect模型的主要区别在于网络事件会被发送到一个事件对象句柄,而不是发送到一个窗口。

WSAEventSelect函数原型如下:

intWSAEventSelect(SOCKETs,WSAEVENThEventObject,longlNetworkEvents);

使用WSAEventSelect模型编程的基本步骤:

1.创建一个事件句柄表和一个对应的套接字句柄表。

2.每创建一个套接字,就创建一个事件对象,把它们的句柄分别放入上面的两个表中,并调用WSAEventSelect将二者关联起来。

3.调用WSAWaitForMultipleEvents在所有事件对象上等待(bWaitAll=FALSE),函数返回后,从第一个有信号的事件对象开始检查事件对象表中的事件对象是否有信号(再次调用WSAWaitForMultipleEvents)。

4.调用WSAEnumNetworkEvents(),获取套接字上相应的网络事件并处理,然后继续在事件对象上等待。

●重叠模型

重叠模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个WinsockI/O操作,在这些提交的I/O操作完成之后,应用程序可以接收到完成通知,从而进行相应的处理。

有两种方法可以接收到重叠IO操作的完成的通知:

1. 在事件对象上等待通知(eventobjectnotification)

2. 完成例程(completionroutines),注意:

并不是完成端口

一般采用第一种方法,采用在投递重叠操作时使用的重叠结构上的事件对象等待以获得完成通知,当该事件对象受信时,再调用WSAGetOverlappedResult函数获得完成状态。

该函数原型如下:

BOOLWSAGetOverlappedResult(

SOCKETs,

 LPWSAOVERLAPPEDlpOverlapped,/*欲查询结果的重叠结构指针*/

LPDWORDlpcbTransfer,/*本次重叠操作的实际接收(或发送)的字节数*/

BOOLfWait,/*设置为TRUE,除非重叠操作完成,否则函数不会返回,设置FALSE,而且操作仍处于挂起状态,函数会返回FALSE,错误为WSA_IO_INCOMPLETE*/

LPDWORDlpdwFlags//取得完成标志

          );

使用重叠模型的步骤:

(以接收操作为例)

1、定义相关变量…………

2、创建监听套接字(使用socket()或WSASocket()),并进入监听状态。

3、接受连接请求(使用accept()或AcceptEx())。

4、为新的连接套接字创建WSAOVERLAPPED结构,并分配事件对象句柄。

5、以WSAOVERLAPPED结构为参数,在套接字上投递WSARecv()请求。

6、将所有接收套接字使用的重叠结构上的事件组建成事件数组,并调用WSAWaitForMultipleEvents函数,等待事件受信。

7、使用WSAGetOverlappedResult(),判断重叠调用的返回状态。

8、设置事件对象状态为无信号状态,必要时重新组建事件数组。

9、在套接字上继续投递WSARecv()请求。

10、重复6~9。

●完成端口

完成端口是应用程序和操作系统沟通的一个接口,将套接字与完成端口关联后就可投递WinsockIO操作;当IO操作完成后,完成端口会收到IO系统的通知包并将这些通知加入到一个队列中,然后应用程序可以查询完成端口取得通知包。

该过程中用到两个关键函数。

其中创建完成端口和关联完成端口,用到CreateIoCompletionPort函数,其原型如下:

HANDLE CreateIoCompletionPort ( 

HANDLE FileHandle,//handletofile 

HANDLE ExistingCompletionPort,//handletoI/Ocompletionport 

DWORD CompletionKey,//completionkey 

DWORD NumberOfConcurrentThreads//numberofthreadsto execute concurrently

); 

查询完成通知包,用到GetQueuedCompletionStatus函数,其原型如下:

BOOL GetQueuedCompletionStatus( 

HANDLE CompletionPort,//handle tocompletionport 

LPDWORD lpNumberOfBytes, // bytes transferred

LPDWORD lpCompletionKey, // file completion key

LPOVERLAPPED *lpOverlapped,//buffer 

DWORD dwMilliseconds    // optional timeout value

 ); 

在这两个函数中,CreateIoCompletionPort中的CompletionKey参数传递套接字信息,该信息在GetQueuedCompletionStatus函数的lpCompletionKey参数中返回,同时GetQueuedCompletionStatus函数中lpOverlapped参数带回投递IO操作时使用的重叠结构,可以自定义结构体包含IO信息,在该结构体中将重叠结构作为第一个字段,然后在将该结构体做强制类型转换后在投递IO操作时使用,以后通过lpOverlapped参数可以获取完整的IO信息。

使用完成端口的编程步骤:

1.创建一个完成端口。

2.创建一个线程A。

3.A线程循环调用GetQueuedCompletionStatus()函数来得到IO操作结果。

4.主线程循环里调用accept等待客户端连接。

5.主线程里accept返回新连接后,把这个新的套接字句柄用CreateIoCompletionPort()关联到完成端口,然后发出一个异步的WSASend或WSARecv调用,因为是异步函数,WSASend/WSARecv会马上返回,实际的发送或者接收操作由WINDOWS系统去做。

6.主线程继续下一次循环,阻塞在accept这里等待客户端连接。

7.WINDOWS系统完成WSASend或者WSArecv的操作,把结果发到完成端口。

A线程里的GetQueuedCompletionStatus()马上返回,并从完成端口取得刚完成的WSASend/WSARecv的结果。

8.在A线程里对这些数据进行处理(如果处理过程很耗时,需要新开线程处理),然后接着发出WSASend/WSARecv,并继续下一次循环阻塞在GetQueuedCompletionStatus()这里。

2.实验内容

1、在上述I/O模型中自选一个I/O模型,构建一个TCP服务器,该服务器能:

Ø接受客户端连接时显示客户端的IP,PORT信息

Ø接收客户端连接时显示其连接编号,客户端退出时显示关闭的连接编号

Ø能显示客户端发来的数据

Ø能从键盘输入数据并发到客户端

Ø其他数据传送功能(可选)

2、编写客户端程序,使之能:

Ø从键盘输入数据并发送到服务器

Ø能接收服务器发来的数据

Ø当输入“bye”时退出程序

2.实验设计

(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完成的套接字移除,然后遍历处理后的套接字集合,此时套接字集合中只剩下有网络事件的套接字。

五、实验者自评(从实验设计、实验过程、对实验知识点的理解上给出客观公正的自我评价)

通过本次实验,对Winsock I/O模型工作原理有了进一步了解和熟悉,并熟悉了I/O模型中使用的Winsock接口函数,掌握了使用I/O模型进行网络程序设计的编程步骤。

尤其是对选择模型有了深入的了解,掌握了其判断套接字上何时可以收发数据的或者数据收发已完成的原理,判断发生网络事件或者IO完成的套接字的原理,对其他模型的理解也更加深入。

 

六、附录:

关键代码(给出适当注释,可读性高)

客户端

//#include"stdafx.h"

#include

#include

#pragmacomment(lib,"ws2_32.lib")

#definePORT4567

#defineMSGSIZE1024

intmain()

{

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(SOCKE

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 幼儿教育 > 幼儿读物

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2