DELPHI下的Winsock编程(三)--一模式和I/O控制

本文详细介绍了在DELPHI中使用Winsock编程时的I/O控制指令,包括FIONBIO、FIONREAD和SIOCATMARK,以及套接字的锁定和非锁定模式。锁定模式下,套接字在I/O操作完成前会保持阻塞,而非锁定模式下,Winsock函数会立即返回。文章还讨论了这两种模式的优缺点和适用场景,以及如何通过select和WSAAsyncSelect函数来管理和控制套接字的I/O行为。
摘要由CSDN通过智能技术生成

I/O控制指令
        一系列套接字I/O控制函数用于在套接字之上,控制I/O的行为,同时获取与那个套接字上进行的I/O操作有关的信息。其中,第一个函数是ioctlsocket,起源于Winsock 1规范,声明如下:
int ioctlsocket (
    SOCKET s,
    long cmd,
    u_long FAR* argp
   );

 
        其中,参数s指定的是要在上面采取I/O操作的套接字描述符,而cmd是一个预定义的标志,用于打算执行的I/O控制命令。最后一个参数argp对应的是一个指针,指向与命令密切相关的一个变量。描述好每个命令之后,再给出要求变量的类型。

标准I/O控制命令
1. FIONBIO
       该命令可在套接字s上允许或禁止“非锁定”(Nonblocking)模式。默认情况下,所有套接字在创建好后,都会自动进入“锁定”套接字。若随FIONBIO这个I/O控制命令来调用ioctlsocket,那么应设置argp,令其传递指向一个“无符号”(无正负号)长整数的指针;若打算启用非锁定模式,应将那个长整数的值设为一个非零值。而若设为0值,意味着套接字进入锁定模式。
       调用WSAAsyncSelect或WSAEventSelect函数的时候,会将套接字自动设为非锁定模式。调用了其中任何一个函数之后,再有任何将套接字设回锁定模式的企图,都会以失败告终,并返回WSAEINVAL 错误。要想将套接字改回锁定模式,应用程序首先必须禁止WSAAsyncSelect。具体的做法是调用WSAAsyncSelect,同时令其lEvent参数等于0。或者调用WSAEventSelect,令lNetworkEvents参数等于0,从而禁止WSAEventSelect
2. FIONREAD
       该命令用于决定可从套接字上自动读入的数据量。对ioctlsocket 来说,argp值会返回一个无符号的整数,其中包含了打算读入的字节数。若套接字s是一个“面向数据流”的套接字(类型为SOCK_STREAM),那么FIONREAD会返回在单独一次接收调用中,可返回的数据总量。要注意的是,若使用这种或其他形式的消息预先“窥视”机制,并一定保证能够返回正确的数据量。若在一个数据报套接字(类型为SOCK_DGRAM)上使用I/O控制命令,返回值就是在套接字上排队的第一条消息的大小。
3. SIOCAT M A R K
       若一个套接字配置成接收带外(OOB)数据,而且已设置成以内嵌方式读取这种OOB数据(通过设置SO_OOBINLINE套接字选项),那么本I/O控制命令就会返回一个布尔值,指出接下来是否准备接收OOB数据。如答案是肯定的,则返回TRUE;否则,便返回FALSE,而且下一次接收操作会返回OOB数据之前的所有或部分数据。对ioctlsocket来说,argp会返回一个指向布尔变量的指针。

套接字模式
      Windows套接字在两种模式下执行I/O操作:锁定和非锁定(阻塞和非阻塞)。
     在锁定模式下,在I/O操作完成前,执行操作的Winsock函数(比如send和recv)会一直等候下去,不会立即返回程序(将控制权交还给程序)。而在非锁定模式下, Winsock函数无论如何都会立即返回。
      对于处在锁定模式的套接字,我们必须多加留意,因为在一个锁定套接字上调用任何一个Winsock API函数,都会产生相同的后果—耗费或长或短的时间“等待”。大多数Winsock应用都是遵照一种“生产者-消费者”模型来编制的。在这种模型中,应用程序需要读取(或写入)指定数量的字节,然后以它为基础执行一些计算。这种方式下的使用,一定要注意到阻塞作用产生的副作用,例如,我们编写了了一个“服务器端”的进程,创建一个套接字,然后在主线程中用一个循环接受客户端发起的连接请求,我们用到了ACCEPT函数,那么在阻塞模式下,当没有客户端请求发送时,调用accept函数的线程(这里是主线程)将一直阻塞下去,不会返回,这也就意味着你其他的并发操作无法执行,例如你的程序带有GUI界面,那么你将无法操作窗口上的其他按钮。
        为了解决上述问题,我们注意到阻塞的作用是针对调用它的线程,也就是说,如果我们在主线程中创建一个辅助线程来进行轮循操作,那么虽然此辅助线程可能被阻塞,但不会影响到主线程的工作。
       对锁定套接字来说,它的一个缺点在于:应用程序很难同时通过多个建好连接的套接字通信。使用前述的办法,我们可对应用程序进行修改,令其为连好的每个套接字都分配一个读线程,以及一个数据处理线程。尽管这仍然会增大一些开销,但的确是一种可行的方案。唯一的缺点便是扩展性极差,以后想同时处理大量套接字时,恐怕难以下手。

非锁定模式
        除了锁定模式,我们还可考虑采用非锁定模式的套接字。尽管这种套接字在使用上存在着些许难度,但只要排除了这项困难,它在功能上还是非常强大的。除具备锁定套接字已有的各项优点之外,还进行了少许扩充,功能更强。将一个套接字置为非锁定模式之后, Winsock API调用会立即返回。大多数情况下,这些调用都会“失败”,并返回一个WSAEWOULDBLOCK错误。什么意思呢?它意味着请求的操作在调用期间没有时间完成。举个例子来说,假如在系统的输入缓冲区中,尚不存在“待决”的数据,那么recv(接收数据)调用就会返回WSAEWOULDBLOCK错误。通常,我们需要重复调用同一个函数,直至获得一个成功返回代码。
       由于非锁定调用会频繁返回WSAEWOULDBLOCK错误,所以在任何时候,都应仔细检查所有返回代码,并作好“失败”的准备。许多程序员易犯的一个错误便是连续不停地调用一个函数,直到它返回成功的消息为止。
       锁定和非锁定套接字模式都存在着优点和缺点。其中,从概念的角度说,锁定套接字更易使用。但在应付建立连接的多个套接字时,或在数据的收发量不均,时间不定时,却显得极难管理。而另一方面,假如需要编写更多的代码,以便在每个Winsock调用中,对收到一个WSAEWOULDBLOCK错误的可能性加以应付,那么非锁定套接字便显得有些难于操作。在这些情况下,可考虑使用“套接字I / O模型”,它有助于应用程序通过一种异步方式,同时对一个或多个套接字上进行的通信加以管理。

一个例子:
   为了阐述锁定模式和非锁定模式的区别,可以用下面这个例子来演示:

{
    作者:wudi_1982
    联系方式:wudi_1982@hotmail.com
    转载请著名出处
}

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs,winsock, StdCtrls;

type
  TForm1 
=   class (TForm)
    Button1: TButton;
    Button2: TButton;
    ckbxB: TCheckBox;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  
private
    
{ Private declarations }
  
public
    
{ Public declarations }
  end;
  TSockReadThread
=   class (TThread)
    
private
      FSocket : TSocket;
      FBuf : array[
0 .. 255 ] of Char;
      FMemo : TMemo;
      procedure GetResult;
    
protected
      procedure Execute;
override ;
    
public
      constructor Create(pSocket : TSocket;mm : TMemo);
  end;

var
  Form1: TForm1;
  WSAData : TWSAData;

implementation

{$R *.dfm}

procedure StartUp;
var
  ErrorCode : integer;
begin
  
// 加载winSock dll
  ErrorCode : =  WSAStartup($ 0101 , WSAData);
  
if  ErrorCode  <>   0  then
  begin
    ShowMessage(
' 加载失败 ' );
    exit;
  end;
end;
// 创建一个服务器
procedure TForm1.Button1Click(Sender: TObject);
var
  ErrorCode,AddSize  : integer;
  SockAdd_In,Add: TSockAddrIn;
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值