ImageVerifierCode 换一换
格式:DOCX , 页数:12 ,大小:21.23KB ,
资源ID:6952534      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/6952534.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(简单的 Winsock 应用程式设计2.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

简单的 Winsock 应用程式设计2.docx

1、简单的 Winsock 应用程式设计2简单的 Winsock 应用程式设计(2) 在前一期的文章中,笔者为大家介绍了如何在 Winsock 环境下,建立主从架构(Client/Server)的 TCP socket 的连接建立与关闭;今天笔者将继续为大家介绍如何利用 TCP socket 来收送资料,并详细解说 WSAAsyncSelect 函式中的FD_READ 及 FD_WRITE 事件(笔者曾发现有相当多人对这两个事件甚不了解)。相信读者们已经知道 TCP socket 的连接是在 Client 端呼叫 connect 函式成功,且 Server 端呼叫 accept 函式後,才算完全建

2、立成功;当连接建立成功後,Client 及 Server 也就可以利用这个连接成功的 socket 来传送资料到对方,或是收取对方送过来的资料了。(图 1. TCP socket 的资料收送)在介绍资料的收送前,笔者先介绍一下 TCP socket 与 UDP socket 在传送资料时的特性:Stream (TCP) Socket 提供双向、可靠、有次序、不重覆之资料传送。Datagram (UDP) Socket 则提供双向之沟通,但没有可靠、有次序、不重覆等之保证; 所以使用者可能会收到无次序、重覆之资料,甚至资料在传输过程中也可能会遗漏。由於 UDP Socket 在传送资料时,并不保

3、证资料能完整地送达对方,所以我们常用的一些应用程式(如 telnet、mail、ftp、news.等)都是采用 TCPSocket,以保证资料的正确性。(TCP 及 UDP 封包的传送协定不在我们讨论围,想要了解的读者们,请自行参考相关书籍)TCP 及 UDP Socket 都是双向的,所以我们是利用同一个 Socket 来做传送及收取资料的动作;一般言 TCP Socket 的资料送、收是呼叫 send() 及 recv() 这两个函式来达成,而 UDP Socket 则是用 sendto() 及 recvfrom() 这两个函式。不过TCP Socket 也可用 sendto() 及 re

4、cvfrom() 函式,UDP Socket 同样可用 send() 及recv() 函式;这一点我们稍後再加以解释。现在我们先看一下 send() 及 recv() 的函式说明,并回到我们的前一期程式。 send():使用连接式(connected)的 Socket 传送资料。格 式: int PASCAL FAR send( SOCKET s, const char FAR *buf,int len, int flags );参 数: s Socket 的识别码buf 存放要传送的资料的暂存区len buf 的长度flags 此函式被呼叫的方式传回值: 成功 - 送出的资料长度失败 - S

5、OCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)说明: 此函式适用於连接式的 Datagram 或 Stream Socket 来传送资料。 对Datagram Socket 言,若是 datagram 的大小超过限制,则将不会送出任何资料,并会传回错误值。对 Stream Socket 言,Blocking 模式下,若是传送 (transport) 系统内之储存空间(output buffer)不够存放这些要传送的资料,send() 将会被 block住,直到资料送完为止;如果该 Socket 被设定为 Non-Blocking 模式,那麽将视目前的 outp

6、ut buffer 空间有多少,就送出多少资料,并不会被 block 住。使用者亦须注意 send()函式执行完成,并不表示资料已经成功地送抵对方了,而是已经放到系统的 output buffer 中,等待被送出。 flags 的值可设为 0 或 MSG_DONTROUTE及 MSG_OOB 的组合。(参见 WINSOCK第1.1版48页) recv():自 Socket 接收资料。格 式: int PASCAL FAR recv( SOCKET s, char FAR *buf, int len, int flags );参 数: s Socket 的识别码buf 存放接收到的资料的暂存区l

7、en buf 的长度flags 此函式被呼叫的方式传回值: 成功 - 接收到的资料长度 (若对方 Socket 已关闭,则为 0)失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)说明: 此函式用来自连接式的 Datagram Socket 或 Stream Socket 接收资料。对 Stream Socket 言,我们可以接收到目前 input buffer 内有效的资料,但其数量不超过 len 的大小。若是此 Socket 设定 SO_OOBINLINE,且有 out-of-band 的资料未被读取,那麽只有 out-of-band 的资料被取出

8、。对 Datagram Socket 言,只取出第一个 datagram;若是该 datagram 大 於使用者提供的储存空间,那麽只有该空间大小的资料被取出,多馀的资料将遗失,且回覆错误的讯息。另外如果 Socket为 Blocking 模式,且目前 input buffer 内没有任何资料,则 recv() 将 block 到有任何资料到达为止;如果为 Non-Blocking 模式,且 input buffer 无任何资料,则会马上回覆错误。参数 flags 的值可为 0 或 MSG_PEEK、MSG_OOB 的组合;MSG_PEEK 代表将资料拷贝到使用者提供的 buffer,但是资料

9、并不从系统的 inputbuffer 中移走;0 则表示拷贝并移走。(参考 WINSOCK 第1.1版41 页)【Server 端的资料收送及关闭 Socket?在前一期中,我们说建立的是一个 Asynchronous 模式的 Server;程式中,我们曾对 listen_sd 这个 Socket 呼叫 WSAAsyncSelect() 函式,并设定FD_ACCEPT 事件,所以当 Client 与我们连接时,系统会传给我们一个ASYNC_EVENT 讯息(请参见前一期文章内容);我们在收到讯息并判断是FD_ACCEPT 事件,於是呼叫 accept() 来建立连接。my_sd = accep

10、t(listen_sd, (struct sockaddr far *)&sa, &sa_len)我们在呼叫完 accept() 函式,成功地建立了 Server 端与 Client 端的连接後,此时便可利用新建的 Socket(my_sd)来收送资料了。由於我们同样希望用Asynchronous 的方式,因此要再利用 WSAAsyncSelect() 函式来帮新建的Socket 设定一些事件,以便事件发生时 Winsock Stack 能主动通知我们。由於我们的 Server 是被动的接受 Client 的要求,然後再做答覆,所以我们设定FD_READ 事件;我们也希望 Winsock St

11、ack 在知道 Client 关闭 Socket 时,能主动通知我们,所以同时也设定 FD_CLOSE 事件。(读者须注意,我们设定事件的 Socket 号码是呼叫 accept 後传回的新 Socket 号码,而不是原先监听状态的Socket 号码)WSAAsyncSelect(my_sd, hwnd, ASYNC_EVENT, FD_READ|FD_CLOSE)在这里,我们同样是利用 hwnd 这个视窗及 ASYNC_EVENT 这个讯息;在前文中,笔者曾告诉各位,在收到 ASYNC_EVENT 讯息时,我们可以利用WSAGETSELECTEVENT(lParam) 来判断究竟是哪一事件(

12、FD_READ 或FD_CLOSE)发生了;所以并不会混淆。那我们到底在什麽时候会收到FD_READ 或 FD_CLOSE 事件的讯息呢?【FD_READ 事件】我们会收到 FD_READ 事件通知我们去读取资料的情况有 :(1)呼叫 WSAAsyncSelect 函式来对此 Socket 设定 FD_READ 事件时,input buffer 中已有资料。(2)原先系统的 input buffer 是空的,当系统再收到资料时,会通知我们。(3)使用者呼叫 recv 或 recvfrom 函式,从 input buffer 读取资料,但是并没有一次将资料读光,此时会再驱动一个 FD_READ

13、事件,表示仍有资料在input buffer 中。读者必须注意:如果我们收到 FD_READ 事件通知的讯息,但是我们故意不呼叫 recv 或 recvfrom 来读取资料的话,尔後系统又收到资料时,并不会再次通知我们,一定要等我们呼叫了 recv 或 recvfrom 後,才有可能再收到FD_READ 的事件通知。【FD_CLOSE 事件】当系统知道对方已经将 Socket 关闭了的情况下(收到 FIN 通知,并和对方做关闭动作的 hand-shaking),我们会收到 FD_CLOSE 的事件通知,以便我们也能将这个相对的 Socket 关闭。FD_CLOSE 事件只会发生於 TCP So

14、cket,因为它是 connection-oriented;对於 connectionless 的 UDP Socket,即使设了FD_CLOSE,也不会有作用的。程式中,当 Client 端送一个要求(request)来时,系统会以ASYNC_EVENT 讯息通知我们的 hwnd 视窗;我们在利用WSAGETSELECTEVENT(lParam) 及 WSAGETSELECTERROR(lParam) 知道是FD_READ 事件及检查无误後,便呼叫 recv() 函式来收取 Client 端送来的资料。recv(wParam, &data, sizeof(data), 0)笔者在前一期文章中

15、也曾提到说,FD_XXXX 事件发生,收到讯息时,视窗 handle 被呼叫时的参数 wParam 代表的就是事件发生的 Socket 号码,所以此处 wParam 的值也就是前面提到的 my_sd 这个 Socket 号码。recv() 的第四个参数设为 0,表示我们要将资料从系统的 input buffer 中读取并移走。收到要求後,我们要答覆 Client 端,也就是要送资料给 Client;这时我们就要利用 send() 这个函式了。我们先将资料放到 data 这个资料暂存区,然後呼叫 send() 将它送出,我们利用的也是 wParam (my_sd) 这个同样的 Socket 来做

16、传送的动作,因为它是双向的。send(wParam, &data, strlen(data), 0)Server 与 Client 收送资料一段时间後(资料全部收送完毕),如果 Client 端先呼叫 closesocket() 将它那端的 Socket 关闭,那麽系统在知道後,会通知我们一个 FD_CLOSE 事件的讯息,此时我们也可以呼叫 closesocket() 将我们这端的Socket 关闭了;当然我们也可以呼叫 closesocket() 先主动关闭我们这端的Socket。【Client 端的资料收送及关闭 Socket】我们例子的 Client 是采 Blocking 模式,所以

17、在呼叫 connect() 函式与 Server连接时,可能会等一下子才成功;connect() 函式返回後,且无错误发生的话,Client 与 Server 端的 TCP socket 连接就算成功了。这时,我们便可利用这个连接成功的 Socket 来送收资料了。由於我们并没有要设定为 Asynchronous 模式,所以也不用呼叫 WSAAsyncSelect() 来设定事件。Client 端通常是会先主动发出要求到 Server 端,因此我们呼叫 send() 来传送此一资料。我们的资料量很小,所以并不会被 send() 函式 Block 住;不过如果您要送的资料量很大,那麽可能会等一段

18、时间才会自 send() 函式返回;也就是说必须等资料都放到系统的 output buffer 後才会返回;这是因为我们 Client 的Socket 是阻拦模式。如果我们用的是非阻拦模式的 Socket,那麽 send() 函式会视系统的 output buffer 的空间有多少,只拷贝那麽多的资料到 output buffer,然後就返回,并告知使用者送出了多少资料,并不须等所有资料都放到 outputbuffer 才返回。我们将要求放在 data 资料暂存区,然後呼叫 send() 将要求送出。资料送出後,我们呼叫 recv() 来等待 Server 端的答覆。send(mysd, da

19、ta, strlen(data), 0)recv(mysd, &data, sizeof(data), 0)由於我们 Client 端是 Blocking 模式,所以 recv() 会一直 Block 住,直到下列的情况之一发生,才会返回。(1)Server 端送来资料。(此时 return 值是读取的资料长度)(2)Server 端将相对的 Socket 关闭了。(此时的 return 值会是 0)(3)Client 端自己呼叫 WSACancelBlockingCall() 来取消 recv() 的呼叫。(此时 return 值是 SOCKET_ERROR 错误,错误码 10004 WSA

20、EINTR)同样地,资料全部送收完毕後,我们也呼叫 closesocket() 来将 Socket 关闭。 WSACancelBlockingCall():取消目前正在进行中的 blocking 动作。格 式: int PASCAL FAR WSACancelBlockingCall( void );参 数: 无传回值: 成功 - 0失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)说明: 此函式用来取消该应用程式正在进行中的 blocking 动作。通常的使用时机有:(a) Blocking 动作正在进行中,该应用程式又收到某一讯息(Mouse、Ke

21、yboard、Timer 等),则可在处理该讯息的段落中呼叫此函式。(b)Blocking 动作正在进行中,而 Windows Sockets 又呼叫回应用程式?blocking hook函式时,在该函式内可呼叫此函式来取消 blocking 动作。使用者必须注意,在某一 Winsock blocking 函式动作进行时,除了WSAIsBlocking() 及 WSACancelBlockingCall() 外,不可以再呼叫其它任何Windows Sockets DLL 提供的函式,否则会产生错误。另外若取消的blocking 动作不是 accept() 或 select() 的话,那麽该 S

22、ocket 可能会处於未定状态,使用者最好是呼叫 closesocket() 来关闭该 Socket,而不该再对它做任何动作。(图 2.)demoserv 与 democlnt 在资策会 WinKing 上收送资料的画面(图 3.)demoserv 与 democlnt 在资策会 WinKing 上关闭 Socket 後的画面介绍完了 TCP Socket 的资料收送,笔者接著为读者介绍 sendto() 及recvfrom() 这两个函式,以及许多人可能很容易搞错的 FD_WRITE 事件。【sendto 及 recvfrom 函式】一般言,TCP Socket 使用的是 send() 及

23、recv() 这两个函式;而 UDP Socket用的是 sendto() 及 recvfrom() 函式。这是因为 TCP 是 Connection-oriented,必须做完 Socket 真正的连接程序後,才可以开始收送资料,此时系统已经知道了连接的对方,所以我们不用再指定资料要送到哪里。而 UDP 是 Connectionless,收送资料的双方并没有建立真正的连接,所以我们要利用 sendto() 及 recvfrom()来指定收资料的对方及获知是谁送资料给我们?TCP Socket 也可以用 sendto() 及 recvfrom() 来送收资料,只是此时这两个函式的最後两个参数没

24、有作用,会被系统所忽略。而 UDP Socket 如果呼叫了connect() 函式来指定对方的位址(这个 connect 并不会真的和对方做连接的动作,而是告知我们本身的系统说我们只想收、送何方的资料),那麽也可以利用 send() 及 recv() 来送收资料。 sendto():将资料送到使用者指定的目的地。格 式: int PASCAL FAR sendto( SOCKET s, const char FAR *buf,int len, int flags, const struct sockaddr FAR *to, inttolen );参 数: s Socket 的识别码buf

25、存放要传送的资料的暂存区len buf 的长度flags 此函式被呼叫的方式to 资料要送达的位址tolen to 的大小传回值: 成功 - 送出的资料长度失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)说明: 此函式适用於 Datagram 或 Stream Socket 来传送资料到指定的位址。 对 Datagram Socket 言,若是 datagram 的大小超过限制,则将不会送出任何资料,并会传回错误值。对 Stream Socket 言,其作用与 send() 相同;参数 to 及 tolen 的值将被系统所忽略。 若是传送 (tran

26、sport) 系统内之储存空间不够存放这些要传送的资料,sendto() 将会被 block 住,直到资料都被送出;除非该 Socket 被设定为 non-blocking 模式。使用者亦须注意 sendto()函式执行完成,并不表示资料已经成功地送抵对方了,而可能仍在系统的 outputbuffer 中。 flags 的值可设为 0、MSG_DONTROUTE 及 MSG_OOB 的组合。(参见 WINSOCK第1.1版51页) recvfrom():读取资料,并储存资料来源的位址。格 式: int PASCAL FAR recvfrom( SOCKET s, char FAR *buf,

27、int len, int flags,struct socketaddr FAR *from, int FAR *fromlen );参 数: s Socket 的识别码buf 存放接收到的资料的暂存区len buf 的长度flags 此函式被呼叫的方式from 资料来源的位址fromlen from 的大小传回值: 成功 - 接收到的资料长度 (若对方 Socket 已关闭,则为 0)失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)说明: 此函式用来读取资料并记录资料来源的位址。对 Datagram Socket(UDP)言,一次读取一个 Data

28、gram;对 Stream Socket (TCP)言,其作用与recv() 相同,参数 from 及 fromlen 的值会被系统忽略。如果 Socket 为 Blocking 模式,且目前 input buffer 内没有任何资料,则 recvftom() 将 block 到有任何资料到达为止;如果为 Non-Blocking 模式,且 input buffer 无任何资料,则会马上回覆错误。【FD_WRITE 事件】笔者在前面介绍过 FD_READ 事件的发生时机,现在继续介绍 FD_WRITE这个较易使人混淆的事件,因为真的有相当多的人对此一事件的发生不明了。由字面上看,FD_WRIT

29、E 应该是要求系统通知我们某个 Socket 现在是否可以呼叫 send() 或 sendto() 来传送资料?答案可以说是,但是它和 FD_READ却又有不同的地方。在前面我们知道呼叫一次 recv() 後,如果 input buffer 中尚有资料未被取出的话,系统会再通知我们一次 FD_READ。那麽如果我们呼叫一次 send() 後,系统的 output buffer 仍有空间可写入的话,它是否会再通知我们一个FD_WRITE,叫我们继续传送资料呢?这个答案就是否定的了!系统并不会再通知我们了。系统会通知我们 FD_WRITE 事件的讯息,只有下列几种情况:(1)呼叫 WSAAsync

30、Select() 来设定 FD_WRITE 事件时,Socket 已经可以传送资料(TCP scoket 已经和对方连接成功了,或 UDP socket 已建立完成),且目前 output buffer 仍有空间可写入资料。(2)呼叫 WSAAsyncSelect() 来设定 FD_WRITE 事件时,Socket 尚不能传送资料,不过一旦 Socket 与对方连接成功,马上就会收到 FD_WRITE 的通知。(3)呼叫 send() 或 sendto() 传送资料时,系统告知错误,且错误码为10035 WSAEWOULDBLOCK (呼叫 WSAGetLastError() 得知这项错误),这时表示 output buffer 已经满了,无法再写入任何资料(此时即令呼叫再多次的send() 也都一定失败);一旦系统将部份资料成功送抵对方,空出 output buffer後,便会送一个 FD_WRITE 给使用者,告知可继续传送资料了。换句话说,读者在呼叫 send() 传送资料时,只要不是返回错误 10035 的话,便可一直继续呼叫 send() 来传送资料;一旦 send() 回返错误 10035,那麽便不要再呼叫 send()传送资料,而须等收到 FD_WRITE 後,再继续传送资料。【结语】在这一期的文章中,笔者介绍了各位有关 TCP Socket 的资料收、送方式及

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1