Winsock学习笔记5:I/O Overlapped (重叠)模式
unit
Unit1;
interface
uses
WinSock2, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TWorkThread = class (TThread)
private
FMemo: TMemo;
FBuff: array [ 0 .. 10 ] of Char;
FClientSocket: TSocket;
procedure ShowRecv;
protected
procedure Execute; override ;
public
constructor Create(Memo: TMemo; ClientSocket: TSocket);
end ;
TForm1 = class (TForm)
Memo1: TMemo;
Button1: TButton;
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end ;
var
Form1: TForm1;
ServerSocket: TSocket;
implementation
{ $R *.dfm }
{ TWorkThread }
constructor TWorkThread.Create(Memo: TMemo; ClientSocket: TSocket);
begin
inherited Create(False);
FMemo: = Memo;
FClientSocket: = ClientSocket;
end ;
procedure TWorkThread.Execute;
var
WSBuff: WSABuf;
EventTotal: Integer;
AcceptOverLapped: WSAOverLapped;
WSAEventArray: array [ 0 ..WSA_MAXIMUM_WAIT_EVENTS - 1 ] of WSAEvent;
Idx: Integer;
ReceiveBytes: DWORD;
Flags: DWORD;
ByteTransferred: DWORD;
begin
inherited ;
FreeOnTerminate: = True;
EventTotal: = 0 ;
// 创建事件
WSAEventArray[EventTotal]: = WSACreateEvent;
// 置零
ZeroMemory(@AcceptOverLapped, SizeOf(WSAOverLapped));
// 关联事件
AcceptOverLapped.hEvent: = WSAEventArray[EventTotal];
WSBuff.len: = 22 ; // 缓冲区的长度要大于客户端发送的长度,如果小于客户端发送长度,会出现乱码
WSBuff.buf: = FBuff;
Inc(EventTotal);
while not Terminated do
begin
// 收消息
WSARecv(FClientSocket, @WSBuff, EventTotal, ReceiveBytes, Flags, @AcceptOverlapped, nil );
// 用WSAWaitForMultipleEvents检测是否有“事件”发生
Idx: = WSAWaitForMultipleEvents(EventTotal, @WSAEventArray[ 0 ], False, WSA_INFINITE, False);
// 用WSAGetOverlappedResult取得结果
WSAGetOverlappedResult(FClientSocket, @AcceptOverlapped, @ByteTransferred, False, Flags);
// 显示收到的内容
Synchronize(ShowRecv);
// 清零AcceptOverLapped
ZeroMemory(@AcceptOverLapped, SizeOf(WSAOverLapped));
// 关联事件
AcceptOverLapped.hEvent: = WSAEventArray[Idx - WSA_WAIT_EVENT_ 0 ];
// 重置Event
WSAResetEvent(WSAEventArray[Idx - WSA_WAIT_EVENT_ 0 ]);
end ;
end ;
procedure TWorkThread.ShowRecv;
begin
FMemo.Lines.Add(FBuff);
end ;
procedure TForm1.FormCreate(Sender: TObject);
var
WSData: TWSAData;
LocalAddr: TSockaddr;
SocketMode: Cardinal;
begin
// 初始化Winsock
WSAStartUp($ 202 , WSData);
// 创建套接字
ServerSocket: = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 设置LocalAddr的参数
LocalAddr.sin_family: = AF_INET; // IPV4族
LocalAddr.sin_addr.S_addr: = INADDR_ANY; // 这里不能写Inet_addr( ' 127.0.0.1 ' ),负责会绑定失败,不清楚原因是什么;
LocalAddr.sin_port: = Htons( 1077 ); // Host To Net Short,主机字节顺序转为网络字节顺序
// 绑定本机IP地址、端口,绑定之前先设置好LocalAddr的参数
Bind(ServerSocket, @LocalAddr, SizeOf(LocalAddr));
SocketMode: = 1 ;
IoCtlSocket(ServerSocket, FIONBIO, SocketMode);
// 开始监听
Listen(ServerSocket, 5 );
end ;
procedure TForm1.Timer1Timer(Sender: TObject);
var
ClientAddr: TSockAddr;
ClientAddrLen: Integer;
ClientSocket: TSocket;
begin
// 接受客户端连接,为对应的客户端创建独立的线程
ClientAddrLen: = SizeOf(ClientAddr);
ClientSocket: = Accept(ServerSocket, ClientAddr, ClientAddrLen);
if ClientSocket <> INVALID_SOCKET then
TWorkThread.Create(Memo1, ClientSocket);
end ;
end .
interface
uses
WinSock2, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TWorkThread = class (TThread)
private
FMemo: TMemo;
FBuff: array [ 0 .. 10 ] of Char;
FClientSocket: TSocket;
procedure ShowRecv;
protected
procedure Execute; override ;
public
constructor Create(Memo: TMemo; ClientSocket: TSocket);
end ;
TForm1 = class (TForm)
Memo1: TMemo;
Button1: TButton;
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end ;
var
Form1: TForm1;
ServerSocket: TSocket;
implementation
{ $R *.dfm }
{ TWorkThread }
constructor TWorkThread.Create(Memo: TMemo; ClientSocket: TSocket);
begin
inherited Create(False);
FMemo: = Memo;
FClientSocket: = ClientSocket;
end ;
procedure TWorkThread.Execute;
var
WSBuff: WSABuf;
EventTotal: Integer;
AcceptOverLapped: WSAOverLapped;
WSAEventArray: array [ 0 ..WSA_MAXIMUM_WAIT_EVENTS - 1 ] of WSAEvent;
Idx: Integer;
ReceiveBytes: DWORD;
Flags: DWORD;
ByteTransferred: DWORD;
begin
inherited ;
FreeOnTerminate: = True;
EventTotal: = 0 ;
// 创建事件
WSAEventArray[EventTotal]: = WSACreateEvent;
// 置零
ZeroMemory(@AcceptOverLapped, SizeOf(WSAOverLapped));
// 关联事件
AcceptOverLapped.hEvent: = WSAEventArray[EventTotal];
WSBuff.len: = 22 ; // 缓冲区的长度要大于客户端发送的长度,如果小于客户端发送长度,会出现乱码
WSBuff.buf: = FBuff;
Inc(EventTotal);
while not Terminated do
begin
// 收消息
WSARecv(FClientSocket, @WSBuff, EventTotal, ReceiveBytes, Flags, @AcceptOverlapped, nil );
// 用WSAWaitForMultipleEvents检测是否有“事件”发生
Idx: = WSAWaitForMultipleEvents(EventTotal, @WSAEventArray[ 0 ], False, WSA_INFINITE, False);
// 用WSAGetOverlappedResult取得结果
WSAGetOverlappedResult(FClientSocket, @AcceptOverlapped, @ByteTransferred, False, Flags);
// 显示收到的内容
Synchronize(ShowRecv);
// 清零AcceptOverLapped
ZeroMemory(@AcceptOverLapped, SizeOf(WSAOverLapped));
// 关联事件
AcceptOverLapped.hEvent: = WSAEventArray[Idx - WSA_WAIT_EVENT_ 0 ];
// 重置Event
WSAResetEvent(WSAEventArray[Idx - WSA_WAIT_EVENT_ 0 ]);
end ;
end ;
procedure TWorkThread.ShowRecv;
begin
FMemo.Lines.Add(FBuff);
end ;
procedure TForm1.FormCreate(Sender: TObject);
var
WSData: TWSAData;
LocalAddr: TSockaddr;
SocketMode: Cardinal;
begin
// 初始化Winsock
WSAStartUp($ 202 , WSData);
// 创建套接字
ServerSocket: = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 设置LocalAddr的参数
LocalAddr.sin_family: = AF_INET; // IPV4族
LocalAddr.sin_addr.S_addr: = INADDR_ANY; // 这里不能写Inet_addr( ' 127.0.0.1 ' ),负责会绑定失败,不清楚原因是什么;
LocalAddr.sin_port: = Htons( 1077 ); // Host To Net Short,主机字节顺序转为网络字节顺序
// 绑定本机IP地址、端口,绑定之前先设置好LocalAddr的参数
Bind(ServerSocket, @LocalAddr, SizeOf(LocalAddr));
SocketMode: = 1 ;
IoCtlSocket(ServerSocket, FIONBIO, SocketMode);
// 开始监听
Listen(ServerSocket, 5 );
end ;
procedure TForm1.Timer1Timer(Sender: TObject);
var
ClientAddr: TSockAddr;
ClientAddrLen: Integer;
ClientSocket: TSocket;
begin
// 接受客户端连接,为对应的客户端创建独立的线程
ClientAddrLen: = SizeOf(ClientAddr);
ClientSocket: = Accept(ServerSocket, ClientAddr, ClientAddrLen);
if ClientSocket <> INVALID_SOCKET then
TWorkThread.Create(Memo1, ClientSocket);
end ;
end .
1.此代码只是为了学习函数的使用方法,为了使代码简单,此处使用Timer管理客户端连接,并为每个连接创建独立的线程,Overlapped的处理方式可在线程的执行部分查看,实际应用中不应该用这种模式;
2.此代码为消息型的重叠(I/O Overlapped)模式,还有一种“完成例程”模式(非完成端口);