网络程序设计 socket 复习题 考点 知识点Word格式文档下载.docx
《网络程序设计 socket 复习题 考点 知识点Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《网络程序设计 socket 复习题 考点 知识点Word格式文档下载.docx(21页珍藏版)》请在冰豆网上搜索。
•该接口允许对较低层协议,如IP、ICMP直接访问。
•常用于检验新的协议实现或访问现有服务中配置的新设备。
•服务方式
✧面向连接(虚电路)
•面向连接服务是电话系统服务模式的抽象,每一次完整的数据传输都要经过建立连接、使用连接、终止连接的过程。
•在数据传输过程中,不携带目的地址,而使用连接号(connectID)。
•本质上,连接是一个管道,收发数据不但顺序一致,而且内容相同。
TCP协议提供面向连接的虚电路。
✧无连接
•无连接服务是邮政系统服务的抽象,每个分组都携带完整的目的地址,各分组在系统中独立传送。
•无连接服务不能保证分组的先后顺序,不进行分组出错的恢复和重传,不保证传输的可靠性。
•UDP协议提供无连接的数据报服务。
Winsock寻址
•计算机都分配有一个IP地址,用一个32位数来表示。
•客户机需要通过TCP或UDP和服务器通信时,必须指定服务器的IP地址和服务端口号。
•服务器打算监听接入客户机请求时,也必须指定一个IP地址和一个端口号。
编写Winsock通信程序需要那些头文件和动态连接库?
•winsock2.h,ws2_32.lib
TCP和UDP通信使用的API函数
•socket()、bind()、listen()、accept()、send()、recv()、sendto()、recvfrom()、closesocket()
Connect()函数的说明
•在客户端使用该函数请求建立连接时,将激活建立连接的三次握手,用来建立到服务器TCP的连接。
如果调用该函数前没有调用bind()来绑定本地地址,则由系统隐式绑定一个地址到该套接字
•该函数用在UDP的客户端时,connect()函数并不是真正地发出建立请求连接的请求,调用将从本地操作系统直接返回。
这样可以将服务器的地址信息保存下来,在后续UDP端口发送数据时,由套接字自动在发送函数中填入服务器地址,而不需要由应用程序在调用发送函数时填入
面向连接的C/S程序工作流程(TCP)
•服务器端工作流程
Ø
使用WSAStartup()加载Winsock库
使用socket()函数创建服务器端通信套接字
使用bind()函数将创建的套接字与服务器地址绑定
使用listen()函数使服务器套接字做好接收连接请求准备
使用accept()接收来自客户端由connect()函数发出的连接请求
根据连接请求建立连接后,使用send()函数发送数据,或者使用recv()函数接收数据
使用closesocket()函数关闭套接字(可以先用shutdown()函数先关闭读写通道)
最后调用WSACleanup()函数释放Winsock库
•客户端程序工作流程
使用WSAStartup()函数加载Winsock库
使用socket()函数创建客户端套接字
使用connect()函数发出向服务器建立连接的请求(调用前可以不用bind()端口号,由系统自动完成)
连接建立后使用send()函数发送数据,或使用recv()函数接收数据
使用closesocet()函数关闭套接字
无连接的C/S程序工作流程(UDP)
无连接的数据报传输服务通信时,客户端与服务器端所使用的函数是类似的,其工作流程如下:
•使用WSAStartup()函数加载Winsock库
•使用socket()函数创建套接字,以确定协议类型
•调用bind()函数将创建的套接字与本地地址绑定,确定本地地址和本地端口号
•使用sendto()函数发送数据,或者使用recvfrom()函数接收数据
•使用closesocket()函数关闭套接字
•调用WSACleanup()函数释放Winsock库
UDP通信程序如何获取对方的地址信息?
•在发送的时候若是广播的话是不会有对方IP地址的,一般用send进行发送,若是定点发送的话是可以将对方IP地址确定的
UDP通信程序是不是一定要绑定套接字到本地地址?
不一定
UDP通信程序是否可以调用connect()?
•如果没有预先绑定地址和端口,要先发送数据之后,才能确定己方UDP采用的地址和端口,因为地址和端口是第一次发送数据时分配的。
如果不想通过发送数据来确定地址和端口,可以调用connect函数,不过这样以后该SOCKET只能跟connect函数中指定的端口和地址通信了。
套接字模式
•Winsock提供了两种套接字模式:
阻塞和非阻塞模。
•Windows平台支持套接字以两种工作。
•在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等候下去,不会立即返回。
•非阻塞模式下,Winsock函数会立即返回。
阻塞模式
•在一个阻塞套接字上调用任何一个WinsockAPI函数,都会产生相同的后果——“等待”。
•大多数Winsock应用都是遵照一种“生产者-消费者”模型来编制的。
在这种模型中,应用程序需要读取(或写入)指定数量的数据,然后以它为基础执行一些计算。
为了防止由于数据的缺乏造成应用程序完全陷于“凝固”状态,同时不必连续性地检查系统网络缓冲。
可采用如下方法
•将应用程序划分为一个读线程,以及一个计算线程,两个线程都共享同一个数据缓冲区。
对这个缓冲区的访问需要受到一定的限制,用同步对象来控制访问,比如Mutex。
•读线程从网络连续地读入数据,并将其置入共享缓冲区内。
读线程取得计算线程开始工作至少需要的数据量后,便触发一个事件,通知计算线程。
•随后,计算线程从缓冲区取走(删除)一个数据块,然后进行计算。
非阻塞模式
•非阻塞模式套接字除具备阻塞套接字已有的各项优点之外,还进行了少许扩充,功能更强。
•套接字工作在非阻塞模式时,WinsockAPI调用会立即返回。
大多数情况下,这些调用都会“失败”,并返回一个WSAEWOULDBLOCK错误。
•设置非阻塞套接字
阻塞与非阻塞通信小结
•通信包括阻塞和非阻塞两种模式。
对于不同的协议,阻塞通信和非阻塞通信有不同的表现。
•对于UDP协议,由于UDP没有发送缓存,因此所有UDP协议即使在阻塞模式下也不会发生阻塞。
•对于面向连接的协议,连接建立阶段,阻塞与非阻塞也表现不一。
在阻塞模式下,如果没有连接请求到达,则等待连接调用将阻塞直到有连接请求到达;
但在非阻塞模式下,如果没有连接请求到达,等待连接调用将直接返回。
•在连接建立阶段,不管是阻塞模式还是非阻塞模式,发起连接请求的一方总是会使调用它的进程阻塞,阻塞间隔最少等于到达服务器的一次往返时间。
•通信模式对应用程序的设计方法也有直接的影响。
在非阻塞模式下,应用程序必须不断地轮询是否有数据到达或有连接请求到达。
•这种轮询的方式耗费的CPU资源较大,要尽可能避免使用,而在阻塞模式下则不存在这一问题,但其缺点是进程或线程在执行I/O操作时将被阻塞而不能执行其他的工作,因此在单进程或单线程应用中不能使用这种模式。
•在多线程应用中比较适合采用阻塞模式,一个线程被阻塞不影响其他线程的工作。
套接字I/O模型
•Select模型,WSAAsynSelect模型,WSAEventSelect模型,重叠模型,完成端口模型。
Select模型
•Select模型是WinSock中最常见的I/O模型。
•通过调用Select函数可以确定一个或多个套接字的状态,判断套接字上是否存在数据,或者能否向一个套接字写入数据。
•既能防止应用程序在套接字处于阻塞模式时,在一次I/O操作后被阻塞,同时也能防止在套接字处于非阻塞模式时,产生WSAEWOULDBLOCK错误。
使用select模型的编程步骤:
•建立fd_set集合s,用来存放欲使用的套接字。
•将套接字添加到集合s中。
•确定要检查的套接字集合Xi(1=<
i<
=3)。
•使用FD_ZERO宏,初始化Xi。
•使用FD_SET宏,根据需要将套接字句柄添加到Xi中
•调用select函数
•根据select函数的返回值进行处理,当成功返回时,判断s中套接字是否在Xi中,并进行相应处理(处理时可能要添加新的套接字到s中)
•回到6
select模型的优点:
可以在单线程内管理多个套接字,最大套接字数量取决于FD_SETSIZE的大小,Winsock2.h中定义为64,用户也可自行定义,但不能超过1024。
缺点:
调用select前后对所有套接字都要进行遍历操作,以便设置和检查。
当FD_SETSIZE太大时,服务器性能明显下降。
WSAAsyncSelect模型
•WSAAsyncSelect模型是WinSock中另一个常用的异步I/O模型。
•该模型可在套接字上接收以Windows消息为基础的网络事件通知。
•调用WSAAsyncSelect函数自动将套接字设置为非阻塞模式,并向WinSockDLL注册一个或多个感兴趣的网络事件,同时提供接收通知时使用的窗口句柄,当注册的网络事件发生时,对应的窗口将收到一个基于消息的通知。
问题1:
对监听套接字和连接套接字应如何设置通知码?
•监听套接字:
WSAAsyncSelect(s,hWnd,WM_SOCKET,FD_ACCEPT|FD_CLOSE);
•连接套接字:
WSAAsyncSelect(s,hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);
使用WSAAsyncSelect模型编程步骤:
1.winsock初始化
2.自定义WM_SOCKET消息
3.创建窗口
4.创建套接字
5.调用WSAAsyncSelect()
6.编写WindowProc()
MFC中的应用方法
•WSAAsynSelect在MFC中使用流程
•
(1)、使用#define语句定义套接字网络事件设置用户消息值,一般为WM_USER+N形式。
#defineWM_USER_SERVERWM_USER+1
•
(2)、调用WSAAsynsSelect函数,为套接字设定"
网络事件-用户消息-消息接收窗体"
的对应关系。
•(3)、在消息接收窗体的代码的消息映射模块中,加入ON_MESSAGE宏,设定用户消息的处理函数。
•ON_MESSAGE(WM_USER_SERVER,OnServerMsg)
•(4)、编写用户处理函数,该函数应该首先使用WSAGETSELECTERROR宏判断是否有错误发生:
•然后根据wParam值了解是哪一个套接口上发生了网络事件从而引起用户消息被发送:
最后使用
•WSAGETSELECTEVENT宏来了解所发生的网络事件,从而进行相应处理。
WSAEventSelect模型
•WSAEventSelect模型是WinSock提供的另一个异步I/O模型,与WSAAsyncSelect模型类似,也允许应用程序在一个或多个套接字上接收以事件为基础的网络事件通知,并且支持的网络事件与WSAAsyncSelect模型一样。
•与WSAAsyncSelect模型的主要区别在于网络事件会被发送到一个事件对象句柄,而不是发送到一个窗口。
WSAEventSelect优缺点
•优点:
简单、不需要窗口环境。
•缺点:
事件对象数目受限制,最多64。
由于一个套接字对应一个事件对象,故支持的套接字数量也为64。
基于WSAEventSelect模型的多线程服务器
问题:
单线程的WSAEventSelect服务器容纳的事件对象受限,亦即可以管理的最大套接字数量为64。
思路:
采用多线程,当有新的连接请求到来时,如果一个线程可容纳的连接达到上限(64),创建新的线程处理连接。
程序构成:
主线程负责监听,创建连接套接字,将套接字分配给连接数未达到上限的线程。
为此先介绍两个重要的结构:
SOCKET_OBJ
THREAD_OBJ
重叠模型的基本原理
•重叠模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个WinsockI/O操作,在这些提交的I/O操作完成之后,应用程序可以接收到完成通知,从而进行相应的处理。
•两种方法可以接收到重叠IO操作的完成的通知:
1.
在事件对象上等待通知(eventobjectnotification)
2.
完成例程(completionroutines),注意:
并不是完成端口
关于缓冲区和重叠操作
•对接收方,应用程序使用WSARecv函数或WSARecvFrom函数来提供存放接收数据的缓冲区。
•如果数据在网络接收以前,应用程序已经提供了一个或多个缓冲区,那么接收的数据就可以立即存放进用户缓冲区。
•如果在应用程序提供数据缓冲区时已经有数据到来,那么接收的数据将被立即拷贝进用户缓冲区。
•如果数据到来时,应用程序没有提供接收缓冲区,那么网络将回到同步操作方式,传送过来的数据将被存放进内部缓冲区,直到应用程序发出了接收调用并且提供了接收缓冲区,这时接收的数据才被拷贝进接收缓冲区。
•特例:
当应用程序使用setsockopt函数把内部接收缓冲区长度设置为0时,对可靠传输协议,数据直到应用程序提供了接收缓冲区后才被接收,而对不可靠传输协议,数据将会丢失。
•对发送方,应用程序使用WSASend函数或WSASendTo函数提供一个指向已填充的数据缓冲区的指针。
•应用程序不应在网络使用完该缓冲区的数据以前以任何方式破坏该缓冲区的数据。
•重叠发送和接收调用会立即返回。
如返回值是0,那么表明I/O操作已经完成,对应的完成指示也可得到。
如果返回值是SOCKET_ERROR,并且错误代码是WSA_IO_PENDING,那么表明重叠操作已经被成功初始化,今后发送缓冲区被用完或者接收缓冲区有数据时,将会有完成指示。
其他错误代码表明初始化没有成功,今后也不会有完成指示。
•发送操作和接收操作都可以被重叠使用。
•接收函数可以被多次调用,指定接收缓冲区,准备接收到来的数据。
•发送函数也可被多次调用,组成一个发送缓冲区队列。
•注意:
应用程序可以通过按顺序提供发送缓冲区来确保一系列重叠发送操作的顺序,而对应的完成指示有可能是按照另外的顺序排列的。
同样,在接收数据的一方,缓冲区是按照被提供的顺序填充的,但是完成指示也可能按照另外的顺序排列。
•关联:
SOCKET–WSAOVERLAPPED–WSAEVENT
使用重叠模型的步骤
以接收操作为例:
1、定义相关变量…………
2、创建监听套接字(使用socket()或WSASocket()),并进入监听状态。
3、接受连接请求(使用accept()或AcceptEx())。
4、为新的连接套接字创建WSAOVERLAPPED结构,并分配事件对象句柄。
5、以WSAOVERLAPPED结构为参数,在套接字上投递WSARecv()请求。
6、将所有接收套接字使用的重叠结构上的事件组建成事件数组,并调用WSAWaitForMultipleEvents函数,等待事件受信。
7、使用WSAGetOverlappedResult(),判断重叠调用的返回状态。
8、设置事件对象状态为无信号状态,必要时重新组建事件数组。
9、在套接字上继续投递WSARecv()请求。
10、重复6~9。
几种winsockI/O模型的分析
①select模型:
•核心是select()函数,它可用于判断套接字上是否存在数据,或者能否向一个套接字写入数据。
•该函数可以有效地防止应用程序在套接字处于阻塞模式时,send或recv进入阻塞状态;
同时也可以防止在非阻塞模式产生大量的WSAEWOULDBLOCK错误。
单线程管理多个套接字
调用select()前后都需要对所有套接字进行遍历操作。
②WSAAsyncSelect模型:
•以事件为基础使用消息机制,关键在于WSAAsyncSelect()函数,将socket消息发送到窗口上,然后在窗口过程函数中处理相应的FD_READ、FD_WRITE等网络事件。
•1、WSAAsyncSelect和WSAEventSelect模型提供了读写数据能力的异步通知,但不提供数据的异步传输,而重叠及完成端口提供数据的异步传输。
•2、可以在系统开销不大的情况下同时处理很多连接,而select模型还需要建立fd_set结构。
必须要使用一个窗口接收消息,如果处理成千上万的套接字显得力不从心。
③WSAEventSelect模型:
•也是以事件为基础的网络事件通知,但是与WSAAsyncSelect不同的是,主要是由事件对象接收网络事件通知,而不是通过窗口。
不需要窗口。
每次只能等待64个事件,所以处理多个套接字时有必要组织一个线程池。
④重叠模型:
•可以使程序能达到更佳的系统性能。
基本设计原理就是让应用程序使用重叠的数据结构,一次投递一个或多个I/O请求。
针对这些提交的请求,在这些请求完成之后,应用程序可作相应的处理。
•使用重叠操作,提供了数据的异步传输能力
⑤完成端口:
•完成端口提供了最好的伸缩性,往往可以使系统达到最好的性能,是处理成千上万的套接字的首选。
•从本质上说,完成端口模型要求创建一个windows完成端口对象,该对象通过指定数量的线程,对重叠I/O请求进行管理,以便为已经完成的重叠I/O请求提供服务。
完成端口I/O模型
•I/O完成端口是应用程序使用线程池处理异步I/O请求的一种机制。
•处理多个并发异步I/O请求时,使用I/O完成端口比在I/O请求时创建线程更快更有效。
•完成端口可接受多种对象句柄。
•从本质上说,完成端口模型要求创建一个Win32完成端口对象,通过指定数量的线程,对重叠I/O请求完成情况进行管理,以便为已经完成的重叠I/O请求提供服务。
关闭IOCP
•要避免在进行重叠I/O操作的同时,强行释放一个OVERLAPPED结构,最好的办法是针对每个套接字句柄,调用closesocket函数,任何尚未进行的重叠I/O操作都会完成,只不过是失败方式完成。
•一旦所有套接字句柄都已关闭,便需在完成端口上终止所有工作线程的运行,则需要使用PostQueuedCompletionStatus()函数,向每个工作线程都发送一个指示每个线程都“立即结束并退出”完成通知包。
完成端口大概的处理流程
•创建一个完成端口。
•创建一个线程A。
•A线程循环调用GetQueuedCompletionStatus()函数来得到IO操作结果。
•主线程循环里调用accept等待客户端连接。
•主线程里accept返回新连接后,把这个新的套接字句柄用CreateIoCompletionPort()关联到完成端口,然后发出一个异步的WSASend或WSARecv调用,因为是异步函数,WSASend/WSARecv会马上返回,实际的发送或者接收操作由WINDOWS系统去做。
•主线程继续下一次循环,阻塞在accept这里等待客户端连接。
•WINDOWS系统完成WSASend或者WSArecv的操作,把结果发到完成端口。
•A线程里的GetQueuedCompletionStatus()马上返回,并从完成端口取得刚完成的WSASend/WSARecv的结果。
•在A线程里对这些数据进行处理(如果处理过程很耗时,需要新开线程处理),然后接着发出WSASend/WSARecv,并继续下一次循环(挂起)在GetQueuedCompletionStatus()这里。
主线程编程步骤
1、创建一个完成端口。
2、判断系统处理器数目。
3、根据步骤2得到的处理器数量,创建工作线程,在完成端口上为已完成的I/O请求提供服务。
4、准备好监听套接字,在端口****上监听进入的连接请求。
5、使用accept函数,接受进入的连接请求。
6、创建一个数据结构,用于容纳“句柄唯一数据”,同时在结构中存入接受的套接字句柄。
7、调用CreateIoCompletionPort,将自accept返回的新套接字句柄同完成端口关联到一起。
通过完成键(CompletionKey)参数,将句柄唯一数据结构传递给CreateIoCompletionPort。
8、开始在已接受的连接上投递I/O操作。
9、重复步骤5)~8),直至服务器中止。
归根到底概括完成端口模型一句话:
我们不停地向套接字发出异步的WSASend/WSARecv
IO操作,具体的IO处理过程由WINDOWS系统完成,WINDOWS系统完成实际的IO处理后,把结果送到完成端口上(如果有多个IO都完成了,那么就在完成端口那里排成一个队列)。
我们在另外一个线程里从完成端口不断地取出IO操作结果,然后根据需要再发出WSASend/WSARecv
IO操作。
可伸缩服务器设计注意事项
1、内存资源管理
•应用程序通过Winsock来和传输协议驱动程序交互,AFD.SYS负责为应用程序进行缓冲区管理。
•也就是说,当应用程序调用send()或WSASend()函数来发送数据时,AFD.SYS将把数据拷贝进它自己的内部缓冲区(取决于SO_SNDBUF设定值),然后send()或WSASend()函数立即返回。
•也可以这么说,AFD.SYS在后台负责把数据发送出去。