Select模型的简单实现

本文介绍了一个基于Delphi的TCP服务器实现案例,利用IO多路复用技术(select模型)来处理多个客户端连接。文章详细展示了如何创建监听套接字、接收客户端连接请求、并处理客户端发送的数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

interface

 

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

  Winsock2, StdCtrls;

 

type

  TfrmMain = class(TForm)

    Button1: TButton;

    Button2: TButton;

    procedure Button1Click(Sender: TObject);

    procedure Button2Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

    procedure IOSelectMain;

  end;

 

var

  frmMain: TfrmMain;

  WSData: TWSAData;

 

implementation

 

{$R *.DFM}

 

{ TfrmMain }

 

procedure CopyFDSet(Source: TFDSet; var Dest: TFDSet);

var

  i: Integer;

begin

  FD_ZERO(Dest);

  for i:=0 to Source.fd_count - 1 do begin

    FD_SET(Source.fd_array[i], Dest);

  end;

end;

 

procedure TfrmMain.IOSelectMain;

var

  sListen, sNew: TSocket;

  sin, addrRemote: TSockAddrIn;

  fdSocket, fdRead: TFDSet;

  nRet, nAddrLen, nRecv: Integer;

  i: Integer;

  sRecv: string;

begin

  //创建监听套接字

  sListen := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  sin.sin_family := AF_INET;

  sin.sin_port := htons(4567);

  sin.sin_addr.S_addr := INADDR_ANY;

 

  //绑定套接字到本机

  if bind(sListen, @sin, SizeOf(sin)) = SOCKET_ERROR then exit;

 

  //进入监听模式

  listen(sListen, 5);

 

  //select模型处理过程

  //1.初始化一个套接字集合fdSocket, 添加监听套接字句柄到这个集合

  FD_ZERO(fdSocket);

  FD_SET(sListen, fdSocket);

 

  while true do begin

    //2.fdSocket集合的一个拷贝fdRead传递给select函数

    //  当有事件发生时,select函数移除fdRead集合中没有

    //未决I/O操作的套接字句柄,然后返回

    CopyFDSet(fdSocket, fdRead);

    nRet := select(0, @fdRead, nil, nil, nil);

    if (nRet>0) then begin

      //3.通过将原来fdSocket集合与select处理过的fdRead集合比较

      //确定有哪些套接字有未决I/O

      for i:=0 to fdSocket.fd_count-1 do begin

        if (FD_ISSET(fdSocket.fd_array[i], fdRead)) then begin

          if (fdSocket.fd_array[i]=sListen) then begin

            //监听套接字接收到新连接

            if (fdSocket.fd_count < FD_SETSIZE) then begin

              nAddrLen := sizeOf(addrRemote);

              sNew := accept(sListen, addrRemote, nAddrLen);

              FD_SET(sNew, fdSocket);

              ShowMessage(inet_ntoa(addrRemote.sin_addr));

            end else begin

              Continue;

            end;

          end else begin

            SetLength(sRecv, 1024);

            nRecv := recv(fdSocket.fd_array[i], sRecv[1], 1024, 0);

            if (nRecv>0) then begin

              SetLength(sRecv, nRecv);

              ShowMessage(sRecv);

            end else begin

              closesocket(fdSocket.fd_array[i]);

              FD_CLR(fdSocket.fd_array[i], fdSocket);

              ShowMessage(' Client Quit');

            end;

          end;

        end else begin

 

        end;

      end;

    end else exit;

  end;

end;

 

procedure TfrmMain.Button1Click(Sender: TObject);

begin

  IOSelectMain;

end;

 

procedure TfrmMain.Button2Click(Sender: TObject);

var

  clientSocket: TSocket;

  servAddr: TSockAddrIn;

  sendstr: string;

begin

  clientSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  try

    if clientSocket = INVALID_SOCKET then exit;

 

    servAddr.sin_family := AF_INET;

    servAddr.sin_port := htons(4567);

    servAddr.sin_addr.S_addr := inet_addr('127.0.0.1');

    if connect(clientSocket, @servAddr, SizeOf(servAddr)) = -1 then exit;

 

    sendstr := 'test';

    send(clientSocket, Pointer(sendstr)^, Length(sendstr), 0);

  finally

    CloseSocket(clientSocket);

  end;

end;

 

initialization

  WSAStartup($0202, WSData);

 

finalization

  WSACleanup;

 

end.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值