socket 编程

利用windows api进行最原始的socket编程:

服务端:

  • WSAStartup (...)
  • ListenSock= socket (...)
  • bind (...)
  • listen (ListenSock,...)
  • acceptSock= accept (ListenSock,...)            //在异步非阻塞状态下将触发FD_WRITE
  • send (acceptSock,...)
  • recv (acceptSock,...)
  • closesocket (ListenSock)
  • WSACleanup ()

客户端:

  • WSAStartup (...)
  • sock= socket (...)
  • bind (...)
  • connect (...)                   //在异步非阻塞状态下将触发FD_READ    
  • recv ((sock,...)
  • send ((sock,...)
  • closesocket (ListenSock)
  • WSACleanup ()

使用非阻塞模式编程:

int PASCAL FAR WSAAsyncSelect ( SOCKET s, HWND hWnd, unsigned int wMsg,  long lEvent )

 

                s   标识一个需要事件通知的套接口的描述符.

          hWnd    标识一个在网络事件发生时需要接收消息的窗口句柄.
          wMsg    在网络事件发生时要接收的消息.
          lEvent  位屏蔽码,用于指明应用程序感兴趣的网络事件集合.
         
  注释:
          本函数用来请求Windows Sockets DLL为窗口句柄发一条消息-无论它何时检测到由lEvent参数指明的网络事件.要发送的消息由wMsg参数标明.被通知的套接口由s标识.
          本函数自动将套接口设置为非阻塞模式.
          lEvent参数由下表中列出的值组成.
          值      意义
          FD_READ 欲接收读准备好的通知.
          FD_WRITE    欲接收写准备好的通知.
          FD_OOB  欲接收带边数据到达的通知.
          FD_ACCEPT   欲接收将要连接的通知.
          FD_CONNECT  欲接收已连接好的通知.
          FD_CLOSE    欲接收套接口关闭的通知.
         
          启动一个WSAAsyncSelect()将使为同一个套接口启动的所有先前的WSAAsyncSelect()作废. 例如,要接收读写通知,应用程序必须同时用FD_READ和FD_WRITE调用WSAAsyncSelect(),如下:
          rc = WSAAsyncSelect(s, hWnd, wMsg, FD_READ|FD_WRITE);
          对不同的事件区分不同的消息是不可能的.下面的代码将不会工作;第二个调用将会使第一次调用的作用失效,只有FD_WRITE会通过wMsg2消息通知到.
                          rc = WSAAsyncSelect(s, hWnd, wMsg1, FD_READ);
                          rc = WSAAsyncSelect(s, hWnd, wMsg2, FD_WRITE);
          如果要取消所有的通知,也就是指出Windows Sockets的实现不再在套接口上发送任何和网络事件相关的消息,则lEvent应置为0.
                          rc = WSAAsyncSelect(s, hWnd, 0, 0);
         
          尽管在本例中,WSAAsyncSelect()立即使传给该套接口的事件消息无效, 仍有可能有消息等在应用程序的消息队列中.应用程序因此也必须仍准备好接收网络消息-即使消息作废.用closesocket() 关闭一个套接口也同样使WSAAsyncSelect()发送的消息作废,但在closesocke()之前队列中的消息仍然起作用.
          由于一个已调用accept() 的 套接口和用来接收它的侦听套接口有同样的属性, 任何为侦听套接口设置的的WSAAsyncSelect()事件也同样对已接收的套接口起作用.例如, 如果一个侦听的套接口有WSAAsyncSelect()事件FD_ACCEPT,FD_READ,FD_WRITE, 则任何在那个侦听的套接口上接收的套接口将也有FD_ACCEPT,FD_READ,FD_WRITE事件,以及同样的wMsg的值.若需要不同的 wMsg及事件,应用程序应调用WSAAsyncSelect(),将已接收的套接口和想要发送的新消息作为参数传递.
          当某一套接口s上发生了一个已命名的网络事件,应用程序窗口hWnd会接收到消息wMsg.wParam参数标识了网络事件发生的套接口.lParam的 低字指明了发生的网络事件.lParam的高字则含有一个错误代码.该错误代码可以是winsock.h中定义的任何错误.
          错误代码和事件可以通过WSAGETSELECTERRORH和WSAGETSELECTEVENT宏从lParam中取出.定义如下:
                    #define WSAGETSELECTERROR(lParam)            HIWORD(lParam)
                    #define WSAGETSELECTEVENT(lParam)            LOWORD(lParam)
  注意:在accept()调用和为改变事件或wMsg的WSAAsyncSelect()调用 中有一个计时窗口.应用程序如果需要给侦听的和调用过accept()的套接口以不同的wMsg,它就应该在侦听的套接口上请求FD_ACCEPT事件, 然后在accept()调用后设置相应的事件.由于FD_ACCEPT从不发送给已连接的套接口,而FD_READ,FD_WRITE,FD_OOB及 FD_CLOSE也从不发送给侦听套接口,所以不会产生困难.
          使用以上的宏将最大限度的提高应用程序的可移植性.
          返回的可能网络事件如下:
          值      意义
          FD_READ 套接口s准备读
          FD_WRITE    套接口s准备写
          FD_OOB  带外数据 准备好在套接口s上读.
          FD_ACCEPT   套接口s准备接收新的将要到来的连接.
          FD_CONNECT  套接口s上的连接完成.
          FD_CLOSE    由套接口s标识的连接已关闭.
  返回值:
          0           若应用程序感兴趣的网络事件的声明成功.
          SOCKET_ERROR    否则.可通过调用WSAGetLastError()返回特定的错误代码.
  评价:
          尽管WSAAsyncSelect()可以以多个事件的组合来调用,应用程序窗口还是会为每个网络事件接收一条消息.
          如同select() 函数,WSAAsyncSelect()会被频繁地调用来决定,何时一次数据转移操作(send()或recv() )可以启动,并且可以立刻成功.尽管如此,健壮的应用程序必须做好这样的准备, 即它可能接收到消息及启动了一个会立即返回WSAEWOULDBLOCK的Windows Sockets API调用.例如,下列的事件序列是可能的:
          (i) 数据到达套接口s;Windows Sockets传递WSAAsyncSelect消息.
          (ii)    应用程序处理其它一些消息.
          (iii)   在处理过程中,应用程序启动了ioctlsocket(s,FIONREAD...)并且注意到有数据准备好读.
          (iv)    应用程序启动recv(s,...)来读数据.
          (v) 应用程序循环处理下一条消息,最终到达WSAAsyncSelect消息,表示数据已准备好读.
          (vi)    应用程序启动recv(s,...),但失败并有错误WSAEWOULDBLOCK.
          其它的事件序列也是可能的.
          Windows Sockets DLL不会不断地为某一特定的网络事件向一个应用程序发送消息. 如果已成功地向应用程序窗口发送了一特定事件的通知,对该应用程序窗口将不再为该网络事件发消息,直到应用程序调用函数隐含地重新通知该网络事件.
          事件        重新通知函数
          FD_READ recv()或recvfrom()
          FD_WRITE    send()或sendto()
          FD_OOB  recv()
          FD_ACCEPT   accept()
          FD_CONNECT  无
          FD_CLOSE    无
          任何对重新通知函数的调用,即使失败,也会达到为相关事件发重新通知消息的效果.
          对FD_READ,FD_OOB和FD_ACCEPT事件,消息传递是"水平触发"(level-triggered)的.这意味着,若调用了重新通知函 数并且相关的事件对该调用仍有效,WSAAsyncSelect()消息就将传给应用程序.这为应用程序提供了事件驱动以及不必考虑在任一时刻到达的数据 量的能力.考虑下列序列:
          (i) Windows Sockets DLL在套接口s上接收100字节的数据并传递一个FD_READ消息.
          (ii)    应用程序启动recv(s,buffptr,50,0)接收50字节.
          (iii)   由于仍有数据未读,Windows Sockets DLL发送另一个FD_READ消息.
         
          根据以上语义,应用程序不必在收到FD_READ消息时读进所有可读的数据-对应于每一FD_READ消息进行一次recv()调用是恰当的.如果应用程 序为一个FD_READ消息而启动了多个recv()调用,它将接收到多个FD_READ消息.这样的应用程序可能希望在开始recv()调用( 通过不为FD_READ事件置位的WSAAsyncSelect()函数调用)之前关闭FD_READ消息.
          如果在应用程序初次调用WSAAsyncSelect()或当调用了重新通知函数时,有一个事件为真, 则会发送一个相应的消息.例如,若应用程序调用listen(),就会试图进行连接,然后应用程序调用WSAAsyncSelect()声明它需要为套接 口接收FD_ACCEPT消息,Windows Sockets的实现就会立即传递一个FD_ACCEPT消息.
          FD_WRITE事件处理起来稍有不同.FD_WRITE消息是在套接口第一次用connect() 连 接或由accept()接受,并且在send()或sendto()以WSAWOULDBLOCK错误失败后缓冲区空闲时发送的.因此,应用程序可以假设 发送可能在第一次FD_WRITE消息时开始,并持续到一次返回WSAEWOULDBLOCK的发送. 在这样的失败后,应用程序将被通知,FD_WRITE消息的发送又将可能.
          FD_OOB事件只用在当套接口配置成独立接收带外数据时.如果一个套接口被配置成接收感兴趣的带外数据状态,带外数据将和普通数据等同视之,并且应用程 序应该注册它感兴趣的方面,然后将接收FD_READ事件,而不是FD_OOB事件.应用程序可以设置或监控带外数据处理的方法(通过使用setsockopt()getsockopt() 函数,及SO_OOBINLINE选项).
          在FD_CLOSE消息中的错误代码指出套接口的关闭是正常的还是异常的.如果错误代码是0,则关闭是正常的;若错误代码是WSAECONNRESET,则套接口的虚套接口将被重置.这些只对SOCK_STREAM类型的套接口起作用.
          FD_CLOSE消息在相应套接口的虚电路关闭指令接收到时发送.在TCP术语中,这意味着FD_CLOSE在连接进入了FIN WAIT或CLOSE WAIT状态时发送.这是远端对发送方进行了shutdown()调用或closesocket()调用的结果.
          请注意你的应用程序将只会收到FD_CLOSE消息来指出虚电路的关闭.它不会收到FD_READ消息来表示该状况.
  错误代码:
          WSANOTINITIALISED       在使用本API前必须进行一次成功的WSAStartup()调用.
          WSAENETDOWN     WINDOWS SOCKETS实现已检测到网络子系统故障.  
          WSAEINVAL           指出指定的参数之一是非法的.
          WSAEINPROGRESS      一个阻塞的Windows Sockets操作正在进行.
          附加的错误代码可能在应用程序窗口接收到消息时被置.这些代码可以用WSAGETSELECTERROR宏从lParam中取出.对应于每个网络事件的可能错误代码为:
          事件:FD_CONNECT
          WSAEADDRINUSE       给定的地址已被使用.
          WSAEADDRNOTAVAIL        指定的地址在本地机器不能使用.
          WSAEAFNOSUPPORT     指定族的地址不能和本套接口同时使用.
          WSAECONNREFUSED     连接的尝试被拒绝.
          WSAEDESTADDRREQ     需要一个目的地址.
          WSAEFAULT           namelen参数不正确.
          WSAEINVAL           套接口已经约束到一个地址.
          WSAEISCONN          套接口已经连接.
          WSAEMFILE           没有可用的文件描述符.
          WSAENETUNREACH      此时网络不能从该主机访问.
          WSAENOBUFS          无可用的缓冲区空间.套接口不能连接.
          WSAENOTCONN     套接口没有连接.
          WSAENOTSOCK     该描述符是文件,不是套接口.
          WSAETIMEDOUT        试图连接超时,未建立连接.
          事件:FD_CLOSE
          WSAENETDOWN     WINDOWS SOCKETS实现已检测到网络子系统故障.  
          WSAECONNRESET       连接由远端重建.
          WSAECONNABORTED     由于超时或其它失败放弃连接.
          事件:FD_READ
          事件:FD_WRITE
          事件:FD_OOB
          事件:FD_ACCEPT
          WSAENETDOWN     WINDOWS SOCKETS实现已检测到网络子系统故障.  
  关于Windows Sockets提供者的说明:
          Windows Sockets的提供者应确保消息可以成功地传给应用程序.如果PostMessag()操作失败,Windows Sockets的实现必须重发该消息-只要窗口存在.
          Windows Sockets提供者应使用WSAMAKESELECTREPLY宏来构造消息中的lParam参数.
          当套接口关闭时,Windows Sockets提供者应清除所有保留下来要发送给应用程序窗口的消息.然而应用程序必须准备好接收,放弃任何在closesocket()之前可能已经发送的消息.


总之:

WSAAsyncSelect 中注册的消息中,FD_READ只要接收缓冲区有数据过来,系统就会发该消息(如果一次没recv完,则继续发),FD_ACCEPT只要有客户端连接,系统就会发,FD_CLOSE只要连接关闭,系统就会发,FD_WRITE自己根据需要自己发(如果一次send不完,则系统会再发)。

转载于:https://my.oschina.net/dake/blog/196641

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值