最精简的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的版本 ...
随机推荐
- Spring MVC 下index.jsp访问
spring-mvc.xml配置 <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 --> <bean class="org.springframework.we ...
- c#中获取服务器IP,客户端IP以及Request.ServerVariables详细说明
客户端ip: Request.ServerVariables.Get("Remote_Addr").ToString(); 客户端主机名: Request.ServerVaria ...
- iOS NSNotificationCenter(消息机制)
转自:http://blog.csdn.net/liliangchw/article/details/8276803 对象之间进行通信最基本的方式就是消息传递,在Cocoa中提供Notificatio ...
- HDU 5269 ZYB loves Xor I (二分法)
题意: 给出一个序列,对每两个数求异或结果后取最低位的1出来作为一个数,然后求这些数字的和.比如:{a,b,c},结果是lowbit(a^b)+lowbit(a^c)+lowbit(b^a)+lowb ...
- 20160204.CCPP体系详解(0014天)
程序片段(01):define.h+data.h&data.c+control.h&control.c+view.h&view.c+AI.h&AI.c+main.c 内 ...
- Intent七大属性
一.Intent的作用是什么? 1.Intent 用于封装程序的”调用意图“.两个Activity之间,可以把需要交换的数据封装成Bundle对象,然后使用Intent携带Bundle对象,实现 ...
- 使用JQuery Mobile实现手机新闻浏览器
jQuery Mobile项目是jQuery项目下的在移动开发方面的又一力作,在本文中,笔者将带你一步步更深入学习使用jQuery Mobile框架去实现一个能在android手机上运行的新闻浏览器, ...
- android学习视频(实战项目演练)
1.基于Android平台实战无线点餐系统(客户端(Client)和服务端(Server))①http://kuai.xunlei.com/d/xmBrDwI8CAAyXVFRa3d②http://k ...
- PHP中超全局变量$GLOBALS和global的区别
一.超全局变量$GLOBALS PHP超全局变量有很多,如下的都属于超全局变量(Superglobal): $GLOBALS,$_SERVER,$_GET,$_POST,$_FILES,$_COOKI ...
- ERROR 1130: Host '192.168.1.3' is not allowed to connect to this MySQL ERROR 1062 (23000): Duplicate entry '%-root' for key 'PRIMARY'
use mysql mysql> select host, user from user; 将相应用户数据表中的host字段改成'%': update user set host='%' whe ...