Delphi WinSocket 通信

本示例中,涉及到的缓冲,均采取1024长度,采用的select轮询,现在select轮询已经被认为是比较低级的方法了,微软也不推荐使用,这里就以它开头吧,有时间,大家可以研究其他用法,比如WSAAsyncSelect模型WSAEventSelect模型Overlapped I/O 事件通知模型Overlapped I/O 完成例程模型IOCP模型 。下面开始贴代码了:

 

服务端单元:SocketServer

[delphi] view plain copy print ?
  1. unit SocketServer;  
  2.   
  3. interface  
  4.   
  5. uses  
  6.   Windows, Classes,SyncObjs ,WinSock ,SysUtils;  
  7.   
  8. type  
  9.   TGetDataEvent = procedure(const stream:TMemoryStream) of object;  
  10.   TClientConnected = procedure(const socket: TSocket) of object;  
  11.   
  12.   TWorkThread = class;  
  13.   TServerItem = class;  
  14.   TSocketServer = class  
  15.   private  
  16.     FServerSocket : TSocket;  
  17.     //客户端连接列表   
  18.     FClientSocketList : TStringList;  
  19.     FServerItem : TServerItem;  
  20.   
  21.     //允许最大连接数   
  22.     FMaxClientCount : Integer;  
  23.     //客户端连接数   
  24.     FClientCount : Integer;  
  25.     //当前客户端的索引   
  26.     FClientIndex : Integer;  
  27.     //调用方的Handle   
  28.     FCallHandle : HWND;  
  29.     //接收用的内存流   
  30.     FRecStream : TMemoryStream;  
  31.     //接收缓冲区   
  32.     FRecBuf : array of byte;  
  33.   
  34.     FHost : AnsiString;  
  35.     FPort : Integer;  
  36.   
  37.     FTimeVal: TTimeVal;  
  38.   
  39.     //工作线程   
  40.     FWorkThread : TWorkThread;  
  41.   
  42.     //以下是自定义事件   
  43.     FOnGetData : TGetDataEvent;  
  44.     FOnClientConnected : TClientConnected;  
  45.   
  46.     //初始化套接字   
  47.     function InitSocket:Boolean;  
  48.     //从某个客户端套接字中获取数据   
  49.     procedure GetDataFromClient(socket:TSocket);  
  50.     //检查是否有新的客户端连接   
  51.     procedure CheckNewSocket;  
  52.   
  53.     procedure SetMaxClientCount(const Value:Integer);  
  54.     function GetMaxClientCount:Integer;  
  55.   
  56.     function GetClientCount:Integer;  
  57.     function GetMaxClient: Integer;  
  58.     procedure SetMaxClient(const Value: Integer);  
  59.   
  60.   
  61.   protected  
  62.     procedure DoRevData(stream:TMemoryStream);  
  63.     procedure DoClientConnected(socket:TSocket);  
  64.     //取得某个套接字的IP地址   
  65.     function GetItemIP(index:Integer):PAnsiChar;  
  66.   
  67.     function GetItemActived(index:Integer):Boolean;  
  68.     procedure SetItemActived(index:Integer;const Value:Boolean);  
  69.   
  70.   public  
  71.     //ACallHandle:调用者的句柄, CreateSuspended:线程是否挂起   
  72.     constructor Create(ACallHandle:HWND;CreateSuspended: Boolean);overload;  
  73.     //启用监听   
  74.     function ListenStart:Boolean;  
  75.     //执行线程   
  76.     procedure ResumeThread;  
  77.   
  78.     property Host : AnsiString read FHost write FHost;  
  79.     property Port : Integer read FPort write FPort;  
  80.     property MaxClientCount : Integer read GetMaxClientCount write SetMaxClientCount;  
  81.   
  82.     property OnClientConnected : TClientConnected read FOnClientConnected write FOnClientConnected;  
  83.     property OnGetData : TGetDataEvent read FOnGetData write FOnGetData;  
  84.     property ClientSocketList : TStringList read FClientSocketList;  
  85.     property IP[index: Integer] : PAnsiChar read GetItemIP;  
  86.     property ItemActived[index: Integer] : Boolean read GetItemActived write SetItemActived;  
  87.   
  88.     property ClientCount : Integer read GetClientCount write FClientCount;  
  89.     property ClientIndex : Integer read FClientIndex;  
  90. end;  
  91.   
  92.   TWorkThread = class(TThread)  
  93.     private  
  94.       //临界   
  95.       FLock : TCriticalSection;  
  96.       FSocketServer : TSocketServer;  
  97.   
  98.       procedure Execute;override;  
  99.       procedure ReciveFromClient;  
  100.     public  
  101.       constructor Create(ASocketServer:TSocketServer;CreateSuspended: Boolean);overload;  
  102.   end;  
  103.   
  104.   TServerItem = class  
  105.     private  
  106.       FSocket : TSocket;  
  107.       FIP : PAnsiChar;  
  108.       FActived : Boolean;  
  109.     public  
  110.       property Socket : TSocket read FSocket write FSocket;  
  111.       property IP : PAnsiChar read FIP write FIP;  
  112.       property Actived : Boolean read FActived write FActived;  
  113.   end;  
  114.   
  115. implementation  
  116. const  
  117.   BufLen = 1024;  
  118.   
  119. { TSocketServer }  
  120.   
  121. procedure TSocketServer.CheckNewSocket;  
  122. var  
  123.   fd : TFDSet;  
  124.   addr: sockaddr_in;  
  125.   addrlen: Integer;  
  126.   val: Integer;  
  127.   rec : TSocket;  
  128.   item : TServerItem;  
  129. begin  
  130.   WinSock.FD_ZERO(fd);  
  131.   WinSock.FD_SET(FServerSocket,fd);  
  132.   val := WinSock.select(FServerSocket,@fd,nil,nil,@FTimeVal);  
  133.   if val > 0 then  
  134.   begin  
  135.     addrlen := SizeOf(addr);  
  136.     //接收可用连接   
  137.     rec := WinSock.accept(FServerSocket,@addr,@addrlen);  
  138.     if rec <> INVALID_SOCKET then  
  139.     begin  
  140.       item := TServerItem.Create;  
  141.       item.Socket := rec;  
  142.       item.IP := WinSock.inet_ntoa(addr.sin_addr);  
  143.       item.Actived := True;  
  144.       //发现有新连接,添加到列表   
  145.       FClientSocketList.AddObject(IntToStr(rec),TObject(item));  
  146.       FClientIndex := FClientSocketList.Count - 1;  
  147.       FClientCount := FClientSocketList.Count;  
  148.       //触发连接事件   
  149.       DoClientConnected(rec);  
  150.     end;  
  151.   end;  
  152. end;  
  153.   
  154. constructor TSocketServer.Create(ACallHandle: HWND; CreateSuspended: Boolean);  
  155. begin  
  156.   FCallHandle := ACallHandle;  
  157.   FClientIndex := -1;  
  158.   SetLength(FRecBuf,BufLen);  
  159.   FPort := 80;  
  160.   InitSocket;  
  161.   FRecStream := TMemoryStream.Create;  
  162.   FClientSocketList := TStringList.Create;  
  163.   FWorkThread := TWorkThread.Create(Self,True);  
  164.   //设置等待时间   
  165.   FTimeVal.tv_sec := 0;     {单位:毫秒}  
  166.   FTimeVal.tv_usec := 10;   {单位:秒}  
  167. end;  
  168.   
  169. procedure TSocketServer.DoClientConnected(socket: TSocket);  
  170. begin  
  171.   if Assigned(OnClientConnected) then  
  172.   begin  
  173.     OnClientConnected(socket);  
  174.   end;  
  175. end;  
  176.   
  177. procedure TSocketServer.DoRevData(stream: TMemoryStream);  
  178. begin  
  179.   if Assigned(OnGetData) then  
  180.   begin  
  181.     OnGetData(FRecStream);  
  182.   end;  
  183. end;  
  184.   
  185. function TSocketServer.GetClientCount: Integer;  
  186. begin  
  187.   Result := FClientSocketList.Count;  
  188. end;  
  189.   
  190. procedure TSocketServer.GetDataFromClient(socket: TSocket);  
  191. var  
  192.   fd : TFDSet;  
  193.   val,reclen,i : Integer;  
  194. begin  
  195.   WinSock.FD_ZERO(fd);  
  196.   WinSock.FD_SET(socket,fd);  
  197.   val := WinSock.select(socket,@fd,nil,nil,@FTimeVal);  
  198.   if val > 0 then  
  199.   begin  
  200.     FClientIndex := FClientSocketList.IndexOf(IntToStr(socket));  
  201.     FRecStream.Clear;  
  202.     //接收数据,reclen为接收到的数据长度   
  203.     reclen := WinSock.recv(socket,FRecBuf,BufLen,0);  
  204.     if reclen = 0 then  
  205.       Exit;  
  206.     //写入到接收内存流中   
  207.     FRecStream.Write(FRecBuf,reclen);  
  208.     //触发调用者的接收数据事件   
  209.     DoRevData(FRecStream);  
  210.   end;  
  211. end;  
  212.   
  213. function TSocketServer.GetItemActived(index: Integer): Boolean;  
  214. begin  
  215.   Result := TServerItem(FClientSocketList.Objects[index]).Actived;  
  216. end;  
  217.   
  218. function TSocketServer.GetItemIP(index: Integer): PAnsiChar;  
  219. begin  
  220.   Result := TServerItem(FClientSocketList.Objects[index]).IP;  
  221. end;  
  222.   
  223. function TSocketServer.GetMaxClient: Integer;  
  224. begin  
  225.   Result := FMaxClientCount;  
  226. end;  
  227.   
  228. function TSocketServer.GetMaxClientCount: Integer;  
  229. begin  
  230.   
  231. end;  
  232.   
  233. function TSocketServer.InitSocket: Boolean;  
  234. var  
  235.   WSA : TWSAData;  
  236. begin  
  237.   Result := False;  
  238.   if WSAStartup($0101,WSA) <> 0 then  
  239.   begin  
  240.     Result := False;  
  241.     Exit;  
  242.   end  
  243.   else  
  244.   begin  
  245.   end;  
  246.   Result := True;  
  247. end;  
  248.   
  249. function TSocketServer.ListenStart: Boolean;  
  250. var  
  251.   addr : sockaddr_in;  
  252. begin  
  253.   Result := False;  
  254.   FServerSocket := socket(PF_INET,Sock_Stream,IPPROTO_IP);  
  255.   if FServerSocket = INVALID_SOCKET then  
  256.   begin  
  257.     Result := False;  
  258.     Exit;  
  259.   end  
  260.   else  
  261.   begin  
  262.   end;  
  263.   addr.sin_family := PF_INET;  
  264.   addr.sin_port := htons(FPort);  
  265.   addr.sin_addr.S_addr := INADDR_ANY;  
  266.   if bind(FServerSocket,addr,SizeOf(addr)) = SOCKET_ERROR then  
  267.   begin  
  268.     Result := False;  
  269.     closesocket(FServerSocket);  
  270.     Exit;  
  271.   end  
  272.   else  
  273.   begin  
  274.   end;  
  275.   listen(FServerSocket,5);  
  276.   Result := True;  
  277. end;  
  278.   
  279. procedure TSocketServer.ResumeThread;  
  280. begin  
  281.   FWorkThread.Resume;  
  282. end;  
  283.   
  284. procedure TSocketServer.SetItemActived(index: Integer; const Value: Boolean);  
  285. begin  
  286.   TServerItem(FClientSocketList.Objects[index]).Actived := Value;  
  287. end;  
  288.   
  289. procedure TSocketServer.SetMaxClient(const Value: Integer);  
  290. begin  
  291.   FMaxClientCount := Value;  
  292. end;  
  293.   
  294. procedure TSocketServer.SetMaxClientCount(const Value: Integer);  
  295. begin  
  296.   
  297. end;  
  298.   
  299. { TWorkThread }  
  300.   
  301. constructor TWorkThread.Create(ASocketServer: TSocketServer;  
  302.   CreateSuspended: Boolean);  
  303. begin  
  304.   FSocketServer := ASocketServer;  
  305.   FLock := TCriticalSection.Create;  
  306.   inherited Create(CreateSuspended);  
  307.   FreeOnTerminate := True;  
  308. end;  
  309.   
  310. procedure TWorkThread.Execute;  
  311. begin  
  312.   inherited;  
  313.   FLock.Enter;  
  314.   while not Terminated do  
  315.   begin  
  316.     ReciveFromClient;  
  317.     FSocketServer.CheckNewSocket;  
  318.   end;  
  319.   FLock.Leave;  
  320. end;  
  321.   
  322. //采用select轮询方式,从客户端获取数据   
  323. procedure TWorkThread.ReciveFromClient;  
  324. var  
  325.   i : Integer;  
  326. begin  
  327.   for i := FSocketServer.ClientSocketList.Count - 1 downto 0 do  
  328.   begin  
  329.     //如果套接字 Actived := False,则从列表中删除   
  330.     if not TServerItem(FSocketServer.ClientSocketList.Objects[i]).Actived then  
  331.     begin  
  332.       FSocketServer.ClientSocketList.Delete(i);  
  333.       FSocketServer.ClientCount := FSocketServer.ClientCount - 1;  
  334.       continue;  
  335.     end;  
  336.     FSocketServer.GetDataFromClient(TServerItem(FSocketServer.ClientSocketList.Objects[i]).Socket);  
  337.   end;  
  338. end;  
  339.   
  340. end.  

服务端窗体:ServerMain

[delphi] view plain copy print ?
  1. unit ServerMain;  
  2.   
  3. interface  
  4.   
  5. uses  
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  
  7.   Dialogs, StdCtrls,WinSock,SocketServer;  
  8.   
  9. type  
  10.   TServerForm = class(TForm)  
  11.     Label2: TLabel;  
  12.     ed_Port: TEdit;  
  13.     ListBox1: TListBox;  
  14.     btn_Start: TButton;  
  15.     Label1: TLabel;  
  16.     procedure btn_StartClick(Sender: TObject);  
  17.   private  
  18.     { Private declarations }  
  19.     Server: TSocket;  
  20.     FStop: Boolean;  
  21.     FServerThread: TServerThread;  
  22.     FServerSocket : TSocketServer;  
  23.     procedure DoGetData(const stream:TMemoryStream);  
  24.     procedure DoClientConnected(const socket:TSocket);  
  25.     procedure DoClientDisConnected;  
  26.   public  
  27.     { Public declarations }  
  28.   end;  
  29.   
  30. var  
  31.   ServerForm: TServerForm;  
  32.   
  33. implementation  
  34.   
  35. {$R *.dfm}  
  36. procedure TServerForm.btn_StartClick(Sender: TObject);  
  37. begin  
  38.   FServerSocket := TSocketServer.Create(Self.Handle,False);  
  39.   FServerSocket.Port := StrToInt(ed_Port.Text);  
  40.   FServerSocket.OnGetData := Self.DoGetData;  
  41.   FServerSocket.OnClientConnected := Self.DoClientConnected;  
  42.   if FServerSocket.ListenStart then  
  43.     FServerSocket.ResumeThread;  
  44. end;  
  45.   
  46. procedure TServerForm.DoClientConnected(const socket: TSocket);  
  47. begin  
  48.   ListBox1.Items.Add('用户:'+FServerSocket.IP[FServerSocket.ClientSocketList.Count - 1]+'已连接!');  
  49.   Label1.Caption := '客户端连接数:'+ IntToStr(FServerSocket.ClientCount);  
  50. end;  
  51.   
  52. procedure TServerForm.DoClientDisConnected;  
  53. begin  
  54.   ListBox1.Items.Add('用户:'+FServerSocket.IP[FServerSocket.ClientSocketList.Count - 1]+'已断开!');  
  55.   Label1.Caption := '客户端连接数:'+ IntToStr(FServerSocket.ClientCount);  
  56. end;  
  57.   
  58. procedure TServerForm.DoGetData(const stream: TMemoryStream);  
  59. var  
  60.   i:integer;  
  61.   buffer : array [0..1023of AnsiChar;  
  62.   s : AnsiString;  
  63. begin  
  64.   FillChar(buffer,Length(buffer),0);  
  65.   stream.Position := 0;  
  66.   stream.Read(buffer,Length(buffer));  
  67.   SetString(s,buffer,Length(buffer));  
  68.   
  69.   //Exit为退出标志   
  70.   if Trim(s) = 'Exit' then  
  71.   begin  
  72.     if FServerSocket.ClientIndex = -1 then  
  73.       Exit;  
  74.     DoClientDisConnected;  
  75.     //将退出的套接字的活动状态设置为False   
  76.     FServerSocket.ItemActived[FServerSocket.ClientIndex] := False;  
  77.     Exit;  
  78.   end;  
  79.   ListBox1.Items.Add(s);  
  80. end;  
  81.   
  82. end.  

 

Delphi WinSocket 通信_第1张图片

下面是客户端,客户端比较简单,只是实现了发送字符串。

 

客户端单元:SocketClient

[delphi] view plain copy print ?
  1. unit SocketClient;  
  2.   
  3. interface  
  4.   
  5. uses  
  6.   Windows, Classes,SyncObjs ,WinSock ,SysUtils;  
  7.   
  8. type  
  9.   TSocketClient = class  
  10.   private  
  11.     //客户端套接字描述   
  12.     FClientSocket : TSocket;  
  13.     //服务端地址   
  14.     FHost : AnsiString;  
  15.     //端口   
  16.     FPort : Integer;  
  17.   
  18.     //初始化套接字   
  19.     function InitSocket:Boolean;  
  20.   public  
  21.     constructor Create;overload;  
  22.     //连接服务端   
  23.     function ConnectToServer:Boolean;  
  24.     //发送字符串   
  25.     procedure SendData(s:AnsiString);  
  26.   
  27.     property Host : AnsiString read FHost write FHost;  
  28.     property Port : Integer read FPort write FPort;  
  29.   end;  
  30.   
  31. implementation  
  32. const  
  33.   BufLen = 1024;  
  34. { TServerClient }  
  35.   
  36. function TSocketClient.ConnectToServer: Boolean;  
  37. var  
  38.   addr : sockaddr_in;  
  39.   hostaddr : u_long;  
  40. begin  
  41.   Result := False;  
  42.   FClientSocket := socket(PF_INET,Sock_Stream,IPPROTO_IP);  
  43.   if FClientSocket <> INVALID_SOCKET then  
  44.   begin  
  45.     addr.sin_family := PF_INET;  
  46.     addr.sin_port := htons(FPort);  
  47.     hostaddr := inet_addr(PAnsiChar(FHost));  
  48.     if hostaddr = -1 then  
  49.     begin  
  50.       //ShowMessage('IP地址错误!');   
  51.       Exit;  
  52.     end;  
  53.     addr.sin_addr.S_addr := hostaddr;  
  54.     if connect(FClientSocket,addr,SizeOf(addr)) <> 0 then  
  55.     begin  
  56.       //ShowMessage('连接服务器超时!');   
  57.       Exit;  
  58.     end;  
  59.   end;  
  60.   Result := True;  
  61.   //Label3.Caption := '连接成功!'+IntToStr(Client);   
  62. end;  
  63.   
  64. constructor TSocketClient.Create;  
  65. begin  
  66.   InitSocket;  
  67. end;  
  68.   
  69. function TSocketClient.InitSocket: Boolean;  
  70. var  
  71.   WSA : TWSAData;  
  72. begin  
  73.   Result := True;  
  74.   if WSAStartup($0101,WSA) <> 0 then  
  75.   begin  
  76.     Result := False;  
  77.   end;  
  78. end;  
  79.   
  80. procedure TSocketClient.SendData(s:AnsiString);  
  81. var  
  82.   buffer : array [0..1023of byte;  
  83. begin  
  84.   FillChar(buffer,Length(buffer),0);  
  85.   Move(s[1],buffer,Length(s));  
  86.   WinSock.send(FClientSocket,buffer,Length(s),0);  
  87. end;  
  88.   
  89. end.  

客户端窗体:ClientMain

[delphi] view plain copy print ?
  1. unit ClientMain;  
  2.   
  3. interface  
  4.   
  5. uses  
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  
  7.   Dialogs, StdCtrls,WinSock,SocketClient;  
  8.   
  9. type  
  10.   TClientForm = class(TForm)  
  11.     Label1: TLabel;  
  12.     Label2: TLabel;  
  13.     ed_Host: TEdit;  
  14.     ed_Port: TEdit;  
  15.     btn_Connect: TButton;  
  16.     Label3: TLabel;  
  17.     Button3: TButton;  
  18.     Edit3: TEdit;  
  19.     Button5: TButton;  
  20.     procedure FormCreate(Sender: TObject);  
  21.     procedure btn_ConnectClick(Sender: TObject);  
  22.     procedure Button3Click(Sender: TObject);  
  23.     procedure Button5Click(Sender: TObject);  
  24.   private  
  25.     { Private declarations }  
  26.     Client : TSocket;  
  27.     FServerClient : TSocketClient;  
  28.   public  
  29.     { Public declarations }  
  30.   end;  
  31.   
  32. var  
  33.   ClientForm: TClientForm;  
  34.   
  35. implementation  
  36. {$R *.dfm}  
  37.   
  38. procedure TClientForm.btn_ConnectClick(Sender: TObject);  
  39. begin  
  40.   FServerClient := TSocketClient.Create;  
  41.   FServerClient.Port := StrToIntDef(ed_Port.Text,80);  
  42.   FServerClient.Host := AnsiString(ed_Host.Text);  
  43.   if FServerClient.ConnectToServer then  
  44.     Label3.Caption := '连接服务端成功!';  
  45. end;  
  46.   
  47. procedure TClientForm.Button3Click(Sender: TObject);  
  48. begin  
  49.   FServerClient.SendData(AnsiString(Edit3.Text));  
  50. end;  
  51.   
  52. procedure TClientForm.Button5Click(Sender: TObject);  
  53. begin  
  54.   //发送退出标志   
  55.   FServerClient.SendData('Exit');  
  56.   //关闭套接字   
  57.   WinSock.closesocket(Client);  
  58. end;  
  59.   
  60. procedure TClientForm.FormCreate(Sender: TObject);  
  61. var  
  62.   WSA : TWSAData;  
  63. begin  
  64.   if WSAStartup($0101,WSA) <> 0 then  
  65.   begin  
  66.     ShowMessage('创建套接字失败!');  
  67.   end;  
  68. end;  
  69.   
  70. end.  

 

Delphi WinSocket 通信_第2张图片

 

你可能感兴趣的:(socket,Delphi)