最精简的IOCP封装
最精简的IOCP封装,DELPHI XE8直接编译通过。Winsock2.pas即使用DELPHI自带的,相信XE7也能编译,或者XE6,XE5也能。
单说Winsock2.pas,我见过无数种版本的了,各版本WINSOCK 2的API的方法的参数的数据类型居然都有出入,使用不同人封装的Winsock2.pas源码都要进行相应的调整,
否则无法编译通过,我认为还是使用DELPHI官方的最为靠谱。
要用于实际应用的话,还要进行“粘包处理”。
我在DELPHI XE8下测试OK。
unit Unit1;
interface
uses
Winsock2, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
const
BUF_SIZE=1024;
type
//单IO数据结构
LPER_IO_DATA = ^TPER_IO_DATA;
TPER_IO_DATA = packed record
Overlapped: WSAOverlapped;
DataBuf: WSABuf;
Buf: array [0..BUF_SIZE-1] of AnsiChar;
SendBytes: DWORD;
RecvBytes: DWORD;
end;
//单句柄数据结构
LPER_HANDLE_DATA = ^TPER_HANDLE_DATA;
TPER_HANDLE_DATA = packed record
Socket: TSocket;
end;
TListenThread = class(TThread)
private
protected
procedure Execute;override;
public
constructor Create;
end;
TForm1 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
ServerSocket: TSocket;
implementation
{$R *.dfm}
//工作者线程
function WorkThread(CompletionPortID: Pointer):DWORD; stdcall;
var
CompletionPort: THandle;
BytesTransferred: DWORD;
PerHandleData: LPER_HANDLE_DATA;
PerIOData: LPER_IO_DATA;
Flags: DWORD;
RecvBytes: DWORD;
begin
CompletionPort:= THandle(CompletionPortID);
while True do
begin
//获取完成端口上的队列的完成状态
GetQueuedCompletionStatus(CompletionPort, BytesTransferred, ULONG_PTR(PerHandleData), POverlapped(PerIOData), INFINITE);
//判断是客户端发来的数据还是服务端发出的数据
if PerIOData.RecvBytes = 0 then
begin
PerIOData.RecvBytes:= BytesTransferred;
PerIOData.SendBytes:= 0;
end else
PerIOData.SendBytes:= PerIOData.SendBytes + BytesTransferred;
if PerIOData.RecvBytes > PerIOData.SendBytes then
begin
ZeroMemory(@(PerIOData.Overlapped), SizeOf(WSAOverlapped));
PerIOData.DataBuf.buf:= PerIOData.Buf + PerIOData.SendBytes;
PerIOData.DataBuf.len:= PerIOData.RecvBytes - PerIOData.SendBytes;
//显示收到的数据,这样做是不安全的,示例而已 :)
Form1.Memo1.Lines.Add(string(PerIOData.Buf));
end;
//重置数据
PerIOData.RecvBytes:= 0;
PerIOData.DataBuf.len:= BUF_SIZE;
PerIOData.DataBuf.buf:= @PerIOData.Buf;
//再次投递
WSARecv(PerHandleData.Socket, @(PerIOData.DataBuf), 1, RecvBytes, Flags,
@(PerIOData.Overlapped), nil);
end;
end;
{ TWorkThread }
constructor TListenThread.Create;
begin
inherited Create(False);
FreeOnTerminate:= True;
end;
procedure TListenThread.Execute;
var
WSData: TWSAData;
CompletionPort: THandle;
SI: TSystemInfo;
Idx: Integer;
ThreadID: DWORD;
LocalAddr:sockaddr_in;
ClientAddr: sockaddr;
ClientSocket: TSocket;
PER_HANDLE_DATA: LPER_HANDLE_DATA;
PER_IO_DATA: LPER_IO_DATA;
RecvBytes: DWORD;
Flags: DWORD;
begin
//初始化Winsock
WSAStartUp($202, WSData);
//创建完成端口
CompletionPort:= CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
//根据处理器数量创建工作者线程的数量
GetSystemInfo(SI);
for Idx := 1 to SI.dwNumberOfProcessors do
//创建工作者线程,并将完成端口句柄传递给线程
CreateThread(nil, 0, @WorkThread, Pointer(CompletionPort), 0, ThreadID);
//创建监听套接字
ServerSocket:= WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nil, 0, WSA_FLAG_OVERLAPPED);
//设置LocalAddr的参数
LocalAddr.sin_family:= AF_INET; //IPV4族
LocalAddr.sin_addr.S_addr:= INADDR_ANY;//这里不能写Inet_addr('127.0.0.1'),否则会绑定失败,不清楚原因是什么;
LocalAddr.sin_port:= Htons(8000); //Host To Net Short,主机字节顺序转为网络字节顺序
//绑定本机IP地址、端口,绑定之前先设置好LocalAddr的参数
Bind(ServerSocket, sockaddr(LocalAddr), SizeOf(LocalAddr));
//开始监听
Listen(ServerSocket, 5);
while not Terminated do
begin
ClientSocket:= WSAAccept(ServerSocket,@ClientAddr, nil, nil, 0);
//创建TPER_HANDLE_DATA结构的变量保存客户端Socket
PER_HANDLE_DATA:= LPER_HANDLE_DATA(GlobalAlloc(GPTR, SizeOf(TPER_HANDLE_DATA)));
PER_HANDLE_DATA.Socket:= ClientSocket;
//把完成端口和客户端套接字关联起来
CreateIOCompletionPort(ClientSocket, CompletionPort, ulong_ptr(PER_HANDLE_DATA), 0);
//创建TPER_IO_DATA结构的变量,关联WSARecv函数
PER_IO_DATA:= LPER_IO_DATA(GlobalAlloc(GPTR, SizeOf(TPER_IO_DATA)));
ZeroMemory(@PER_IO_DATA.Overlapped, SizeOf(WSAOverlapped));
PER_IO_DATA.SendBytes:= 0;
PER_IO_DATA.RecvBytes:= 0;
PER_IO_DATA.DataBuf.len:= BUF_SIZE;
PER_IO_DATA.DataBuf.buf:= @PER_IO_DATA.Buf;
WSARecv(ClientSocket, @(PER_IO_DATA.DataBuf), 1, RecvBytes,
Flags, @(PER_IO_DATA.Overlapped), nil);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
//创建监听线程
TListenThread.Create();
end;
end.
最精简的IOCP封装的更多相关文章
- [分享] RT7LITE精简后无法封装解决办法
eagleonly 发表于 2016-6-9 11:00:01 https://www.itsk.com/forum.php?mod=viewthread&tid=368090&hi ...
- 采用完成端口(IOCP)实现高性能网络服务器(Windows c++版)
前言 TCP\IP已成为业界通讯标准.现在越来越多的程序需要联网.网络系统分为服务端和客户端,也就是c\s模式(client \ server).client一般有一个或少数几个连接:server则需 ...
- c++ 网络编程(八) LINUX-epoll/windows-IOCP下 socket opoll函数用法 优于select方法的epoll 以及windows下IOCP 解决多进程服务端创建进程资源浪费问题
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/9622548.html 锲子:关于并发服务器中的I/O复用实现方式,前面在网络编程系列四还是五来 ...
- Java 代码精简
Java 代码精简 利用语法 利用三元表达式 普通 String title; if (isMember(phone)) { title = "会员"; } else { titl ...
- 高性能、高可用性Socket通讯库介绍 - 采用完成端口、历时多年调优!(附文件传输程序)
前言 本人从事编程开发十余年,因为工作关系,很早就接触socket通讯编程.常言道:人在压力下,才可能出非凡的成果.我从事的几个项目都涉及到通讯,为我研究通讯提供了平台,也带来了动力.处理socket ...
- 端口快速扫描程序(c#版 一次可发起1000个连接)
前言 为了探测本机或对方开放了哪些端口,需要用到端口扫描程序.扫描端口的原理很简单:就是尝试连接对方:如果成功,对方就开放了此端口.扫描程序的关键是速度,如果一次只能发起几个连接,显然速度太慢.如果对 ...
- windows 精简/封装/部署
给一个精简过的Windows7安装net35,提示自己到『打开或关闭Windows功能』里打开,然而发现并没有,只有一个ie9的功能.搜索尝试各种办法,显然都不行.用dism部署功能的工具,挂载一个完 ...
- 对百度WebUploader的二次封装,精简前端代码之图片预览上传(两句代码搞定上传)
前言 本篇文章上一篇: 对百度WebUploader开源上传控件的二次封装,精简前端代码(两句代码搞定上传) 此篇是在上面的基础上扩展出来专门上传图片的控件封装. 首先我们看看效果: 正文 使用方式同 ...
- 对MBProgressHUD进行二次封装并精简使用
对MBProgressHUD进行二次封装并精简使用 https://github.com/jdg/MBProgressHUD 几个效果图: 以下源码是MBProgressHUD支持最新的iOS8的版本 ...
随机推荐
- Java面向对象编程概述
一. 01.软件开发经历周期: 软件分析:分析问题领域,了解客户的需求 软件设计:确定软件的总体结构,把整个软件系统划分为大大小小的多个子系统,设计每个子系统的具体结构 软件编码:用选定的编程语言来编 ...
- dom4j创建格式化的xml文件
import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java. ...
- UVA 489 Hangman Judge (字符匹配)
题意:给一个字符串A,只含小写字符数个.再给一个字符串B,含小写字符数个.规则如下: 1.字符串B从左至右逐个字符遍历,对于每个字符,如果该字符在A中存在,将A中所有该字符删掉,若不存在,则错误次数+ ...
- python练习程序(c100经典例2)
题目: 企业发放的奖金根据利润提成.利润(I)低于或等于10万元时,奖金可提10%:利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可可提成7.5%:20万到40 ...
- poj2387 Til the Cows Come Home
解题思路:最短路的模板题,注意一个细节处理即可. 见代码: #include<cstdio> #include<cstring> #include<algorithm&g ...
- nodejs的调试(node-inspector)
我们在接触客户端javascript的时候,调试利器就是firebug ,也是当年为何喜欢用上firefox 浏览器的主要动力,当然,后来 chrome 插件里也出现了firebug的身影..... ...
- 基于CentOS与VmwareStation10搭建Oracle11G RAC 64集群环境:2.搭建环境-2.3配置共享磁盘
2.3.配置共享磁盘 2.3.1.创建共享磁盘 在cmd中进入WMware Workstation 10.0 安装目录: 1.创建存储Oracle Clusterware文件 (Oracle Clu ...
- [Everyday Mathematics]20150214
设 $\dps{x\in \sex{0,\frac{\pi}{2}}}$, 试比较 $\tan(\sin x)$ 和 $\sin(\tan x)$.
- 网络加速手段之一,JS文件资源合并下载
有过ftp下载文件经验的人都有体验,单独下载一个100M的文件比分开下载100个1M的文件速度快很多,这是因为下载需要解析域名,建立连接等额外开销的时间,因此下载100个文件就要做100次这种额外的开 ...
- Drupal如何处理系统变量?
Drupal的系统变量都保存在数据库variable表中: 然后,开发人员可以通过下面的API函数操作这些系统变量: function variable_get($name, $default = N ...