winsock简单使用(采用select轮询方式,从客户端获取数据)

最近查找有关winsock方面的资料,来源bmdh

客户端:窗体

unit ClientMain;

interface

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

type
  TClientForm = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    ed_Host: TEdit;
    ed_Port: TEdit;
    btn_Connect: TButton;
    btn_Stop: TButton;
    btn_Send: TButton;
    ed_Text: TEdit;
    Label3: TLabel;
    procedure btn_SendClick(Sender: TObject);
    procedure btn_StopClick(Sender: TObject);
    procedure btn_ConnectClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
     Client:TSocket;
     FServerClient:TSocketClient;
  public
    { Public declarations }
  end;

var
  ClientForm: TClientForm;

implementation

{$R *.dfm}

procedure TClientForm.btn_ConnectClick(Sender: TObject);
begin
  FServerClient:=TSocketClient.Create;
  FServerClient.Port:=StrToIntDef(ed_Port.Text,80);
  FServerClient.Host:=AnsiString(ed_Host.Text);
  if FServerClient.ConnectToServer then
     label3.Caption:='连接服务端成功!';
end;

procedure TClientForm.btn_SendClick(Sender: TObject);
begin
   FServerClient.SendData(AnsiString(ed_Text.Text));
end;

procedure TClientForm.btn_StopClick(Sender: TObject);
begin
  FServerClient.SendData('Exit');
  label3.Caption:='断开连接!';
  WinSock.closesocket(Client); 
end;

procedure TClientForm.FormCreate(Sender: TObject);
var
  WSA:TWSAData;
begin
  if WSAStartup($0101,WSA)<>0 then
  begin
     MessageDlg('创建套接字失败!',mtWarning,[mbYes],0);
     exit;
  end;
end;

end.

客户端掉用单元 clientsocket

unit SocketClient;

interface
 uses  Windows,Classes,SyncObjs,WinSock,SysUtils;

type
  TSocketClient=class
    private
     //客户端套接字描述
       FClientSocket:TSocket;
       //服务端地址
       FHost:AnsiString;
       // 端口
       FPort:Integer;
       //初始化套接字
       function InitSocket:Boolean;
     public
       constructor Create;overload;
       //连接服务器
       function ConnectToServer:Boolean;
       //发送字符串
       procedure SendData(s:AnsiString);

       property Host:AnsiString read FHost write FHost;
       property Port:Integer read FPort write FPort;
  end;
implementation
const
  BufLen=1024;
{ TSocketClient }

function TSocketClient.ConnectToServer: Boolean;
var
  addr:sockaddr_in;
  hostaddr:u_long;
begin
  Result:=false;
  FClientSocket:=socket(PF_INET,SOCK_STREAM,IPPROTO_IP);
  if FClientSocket<>INVALID_SOCKET then
  begin
     addr.sin_family:=PF_INET;
     addr.sin_port:=htons(FPort);
     hostaddr:=inet_addr(PAnsiChar(FHost));
     if hostaddr=1 then
     begin
        //showmessage('IP地址错误!');
        Exit;
     end;
     addr.sin_addr.S_addr:=hostaddr;
     if connect(FClientSocket,addr,SizeOf(addr))<>0 then
     begin
       //showmessage('连接服务器超时');
       Exit;
     end;
  end;
  Result:=True;
   // label.caption:='连接成功!'+Inttostr(Client);
end;

constructor TSocketClient.Create;
begin
    InitSocket;
end;

function TSocketClient.InitSocket: Boolean;
var
  WSA:TWSAData ;
begin
  Result:=true;
  if WSAStartup($0101,WSA)<>0 then
  begin
     Result:=False;
  end;
 
end;

procedure TSocketClient.SendData(s: AnsiString);
var
  buffer:array[0..1023] of Byte;
begin
  FillChar(buffer,Length(buffer),0);
  Move(s[1],buffer,Length(s));
  WinSock.send(FClientSocket,buffer,Length(s),0);
end;

end.

 

服务端:窗体

  unit ServerMain;

interface

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

type
  TServerForm = class(TForm)
    Label3: TLabel;
    ed_Port: TEdit;
    ListBox1: TListBox;
    Label1: TLabel;
    btn_Start: TButton;
    procedure btn_StartClick(Sender: TObject);
  private
    { Private declarations }
    Server:TSocket;
    FStop:Boolean;
    FServerThread:TWorkThread;
    FServerSocket:TSocketServer;
    procedure DoGetData(const stream:TMemoryStream);
    procedure DoClientConnected(const socket:TSocket);
    procedure DoClientDisConnected;
  public
    { Public declarations }
  end;

var
  ServerForm: TServerForm;

implementation

{$R *.dfm}

{ TServerForm }

procedure TServerForm.btn_StartClick(Sender: TObject);
begin
   FServerSocket:=TSocketServer.Create(Self.Handle,False);
   FServerSocket.Port:=StrToIntDef(ed_Port.Text,80);
   FServerSocket.OnGetData:=Self.DoGetData;
   FServerSocket.OnClientConnected:=Self.DoClientConnected;
   if FServerSocket.ListenStart then
      FServerSocket.ResumeThread;
end;

procedure TServerForm.DoClientConnected(const socket: TSocket);
begin
   ListBox1.Items.Add('用户:'+FServerSocket.IP[FServerSocket.ClientSocketList.count-1]+'已连接!');
   Label1.Caption:='客户端连接数:'+inttostr(FServerSocket.ClientCount);
end;

procedure TServerForm.DoClientDisConnected;
begin
  ListBox1.Items.Add('用户:'+FserverSocket.IP[FServerSocket.ClientSocketList.count-1]+'已断开!');
  Label1.Caption:='客户连接数:'+inttostr(FServerSocket.ClientCount);
end;

procedure TServerForm.DoGetData(const stream: TMemoryStream);
var
  i:Integer;
  buffer:array [0..1023] of AnsiChar;
  s:AnsiString;
begin
  FillChar(buffer,Length(buffer),0);
  stream.Position:=0;
  stream.Read(buffer,Length(buffer));
  SetString(s,buffer,Length(buffer)) ;
  //exit 退出标志
  if Trim(LowerCase(s))='exit' then 
  begin
     if FServerSocket.ClientIndex=-1 then
       Exit;
     DoClientDisConnected ;
     //将退出的套接字的活动状态设置为FALSE
     FServerSocket.ItemActived[FServerSocket.ClientIndex]:=false;
     exit;
  end;
  ListBox1.Items.Add(s);
end;

end.
服务端调用单元 Serversocket

  unit SocketServer;

interface
uses
   Windows,Classes,SyncObjs,WinSock,SysUtils;
type
  TGetDataEvent=procedure(const stream:TMemoryStream) of object;
  TClientConnected=procedure(const Socket:TSocket) of object;

  TWorkThread=class;
  TServerItem=class;
  TSocketServer=class
     FServerSocket:TSocket;
     //客户端连接列表
     FClientSocketList:TStringList;
     FServerItem:TServerItem;

     //允许最大连接数
     FMaxClientCout:Integer;
     //客户端连接数
     FClientCount:Integer;
     //当前客户端的索引
     FClientIndex:Integer;
     //调用方的Handle
     FCallHandle:HWND;
     //接收用的内存流
     FRecStream:TMemoryStream;
     //接收缓冲区
     FRecBuf:array of Byte;

     FHost:AnsiString;
     FPort:Integer;

     FTimeVal:TTimeVal;

     //工作线程
     FWorkThread:TWorkThread;

     //以下是自定义事件
     FOnGetData:TGetDataEvent;
     FOnClientConnected:TClientConnected;

     //初始化套接字
     function InitSocket:Boolean;
     //从某个客户端套接字中获取数据
     procedure GetDataFromClient(Socket:TSocket);
     //检测是否有新的客户连接
     procedure CheckNewSocket;

     procedure SetMaxClientCount(const value:Integer);
     function GetMaxClientCount:Integer;

     function GetClientCount:Integer;
     function GetMaxClient:Integer;
     procedure SetMaxClient(const Value:Integer);

  protected
      procedure DoRevData(stream:TMemoryStream);
      procedure DoClientConnected(Socket:TSocket);
      //取得某个套接字的IP地址
      function GetItemIP(index:Integer):PAnsiChar;

      function GetItemActived(index:Integer):Boolean;
      procedure SetItemActived(index:Integer;const value:Boolean);
  public
      //ACallHandel:调用者的句柄,CreageSuPended:线程是否挂起
      constructor Create(ACallHandle:HWND;CreateSuspended:Boolean);overload;
      //启用监听
      function ListenStart:Boolean;
      //执行线程
      procedure ResumeThread;

      property Host:AnsiString read FHost write FHost;
      property Port:Integer read FPort write FPort;
      property MaxClientCount:Integer read GetMaxClientCount write SetMaxClientCount;
      property OnClientConnected:TClientConnected read FOnClientConnected write FOnClientConnected;
      property OnGetData:TGetDataEvent read FOnGetData write FOnGetData;
      property ClientSocketList:TStringList read FClientSocketList write FClientSocketList;
      property IP[Index:Integer]:PAnsiChar read GetItemIP;
      property ItemActived[index:Integer]:Boolean read GetItemActived write SetItemActived;
      property ClientCount:Integer Read GetClientCount write FClientCount;
      property ClientIndex:Integer read FClientIndex;

  end;
  TWorkThread=class(TThread)
    private
      //临界
      FLock:TCriticalSection;
      FSocketServer:Tsocketserver;

      procedure Execute;override;
      procedure ReciveFromClient;
    public
      constructor Create(ASocketServer:TSocketServer;CreateSuspended:Boolean);overload;
  end;

  TServerItem=class
    private
      FSocket:Tsocket;
      FIP:PAnsiChar;
      FActived:boolean;
    public
      property socket:TSocket read FSocket write FSocket;
      property IP:PAnsiChar read FIP write FIP;
      property Actived:Boolean read FActived write FActived;
  end;
implementation
const
   BufLen=1024;
{ TSocketServer }

procedure TSocketServer.CheckNewSocket;
var
  fd:TFDSet;
  addr:sockaddr_in;
  addrlen:Integer;
  val:Integer;
  rec:TSocket;
  item:TServerItem;
begin
  WinSock.FD_ZERO(fd);
  WinSock.FD_SET(FServerSocket,fd);
  val:=WinSock.select(FServerSocket,@fd,nil,nil,@Ftimeval);//select 模式
  if val>0 then
  begin
     addrlen:=SizeOf(addr);
     //接收可用连接
     rec:=WinSock.accept(FServerSocket,@addr,@addrlen);
     if rec<>INVALID_SOCKET then
     begin
       item:=TServerItem.Create;
       item.socket:=rec;
       item.IP:=WinSock.inet_ntoa(addr.sin_addr);
       item.Actived:=true;
       //发现有新的连接添加到列表中
       FClientSocketList.AddObject(IntToStr(rec),TObject(item));
       FClientIndex:=FClientSocketList.Count-1;
       FClientCount:=FClientSocketList.Count;
       //触发连接事件
       DoClientConnected(rec);
     end;
  end;
end;

constructor TSocketServer.Create(ACallHandle: HWND; CreateSuspended: Boolean);
begin
  FCallHandle:=ACallHandle;
  FClientIndex:=-1;
  SetLength(FRecBuf,BufLen);
  FPort:=80;
  InitSocket;
  FRecStream:=TMemoryStream.Create;
  FClientSocketList:=TStringList.Create;
  FWorkThread:=TWorkThread.Create(Self,true);
  //设置等待时间
  FTimeVal.tv_sec:=0;{单位:毫秒}
  FTimeVal.tv_usec:=10;{单位:秒}
end;

procedure TSocketServer.DoClientConnected(Socket: TSocket);
begin
   if Assigned(FOnClientConnected) then
   begin
      OnClientConnected(Socket);
   end;
end;

procedure TSocketServer.DoRevData(stream: TMemoryStream);
begin
    if Assigned(OnGetData) then
    begin
       OnGetData(FRecStream);
    end;
end;

function TSocketServer.GetClientCount: Integer;
begin
   Result:=FClientSocketList.Count;
end;

procedure TSocketServer.GetDataFromClient(Socket: TSocket);
var
  fd:TFDSet;
  val,reclen,i:Integer;
begin
  WinSock.FD_ZERO(fd);
  WinSock.FD_SET(socket,fd);
  val:=WinSock.select(Socket,@fd,nil,nil,@FTimeval);
  FRecStream.Clear;
  //接收数据,reclen为接收到的数据长度
  reclen:=WinSock.recv(Socket,FRecBuf,BufLen,0);
  if reclen=0 then
     Exit;
  //写入到接收内存流中
  FRecStream.Write(FRecBuf,reclen);
  //触发调用者的接收数据事件
  DoRevData(FRecStream);
end;

function TSocketServer.GetItemActived(index: Integer): Boolean;
begin
   Result:=TServerItem(FClientSocketList.Objects[index]).Actived;
end;

function TSocketServer.GetItemIP(index: Integer): PAnsiChar;
begin
  Result:=TServerItem(FClientSocketList.Objects[index]).IP;
end;

function TSocketServer.GetMaxClient: Integer;
begin
   Result:=FMaxClientCout;
end;

function TSocketServer.GetMaxClientCount: Integer;
begin
   Result:=FClientSocketList.Count;
end;

function TSocketServer.InitSocket: Boolean;
var
  WSA:TWSAData;
begin
  Result:=false;
  if WSAStartup($0101,WSA)<>0 then
  begin
    Result:=false;
    Exit;
  end;
  Result:=True;
end;

function TSocketServer.ListenStart: Boolean;
var
  addr:sockaddr_in;
begin
  Result:=False;
  FServerSocket:=socket(PF_INET,SOCK_STREAM,IPPROTO_IP);
  if FServerSocket=INVALID_SOCKET then
  begin
    Result:=False;
    exit;
  end;
  addr.sin_family:=PF_INET;
  addr.sin_port:=htons(FPort);
  addr.sin_addr.S_addr:=INADDR_ANY;
  if bind(FServerSocket,addr,SizeOf(addr))=SOCKET_ERROR then
  begin
    Result:=false;
    closesocket(FServerSocket);
    exit;
  end;
  listen(FServerSocket,5);
  Result:=true;
end;

procedure TSocketServer.ResumeThread;
begin
  FWorkThread.Resume;
end;

procedure TSocketServer.SetItemActived(index: Integer; const value: Boolean);
begin
   TServerItem(FClientSocketList.Objects[index]).Actived:=value;
end;

procedure TSocketServer.SetMaxClient(const Value: Integer);
begin
   FMaxClientCout:=value;
end;

procedure TSocketServer.SetMaxClientCount(const value: Integer);
begin
   FMaxClientCout:=value;
end;

{ TWorkThread }

constructor TWorkThread.Create(ASocketServer: TSocketServer;
  CreateSuspended: Boolean);
begin
   FSocketServer:=ASocketServer;
   FLock:=TCriticalSection.Create;
   inherited Create(CreateSuspended);
   FreeOnTerminate:=True;
end;

procedure TWorkThread.Execute;
begin
  inherited;
  FLock.Enter;
  while not Terminated do
  begin
     ReciveFromClient;
     FSocketServer.CheckNewSocket;
  end;
  FLock.Leave;
end;
//采用select轮询方式,从客户端获取数据
procedure TWorkThread.ReciveFromClient;
var
  i:Integer;
begin
  for i := FSocketServer.ClientCount-1 downto 0 do
  begin
     //如果套接字actived:=FALSE;则从类表中删除
     if not TServerItem(FSocketServer.ClientSocketList.Objects[i]).Actived then
     begin
        FSocketServer.ClientSocketList.Delete(i);
        FSocketServer.ClientCount:=FSocketServer.ClientCount-1;
        Continue;
     end;
     FSocketServer.GetDataFromClient(TServerItem(FSocketServer.ClientSocketList.Objects[i]).socket);
  end;

end;

end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值