Delphi的多线程技术在Socket编程中的应用
- 格式:pdf
- 大小:106.25 KB
- 文档页数:2
引言概述:Delphi程序设计是一种面向对象的编程语言,它以其良好的可视化开发环境和丰富的组件库而闻名。
本文将深入探讨Delphi程序设计的进阶知识,为读者提供更深入的理解和应用。
主要包括:Delphi的多线程编程、数据库操作、图形用户界面设计、网络编程和常用算法的实现等五个大点。
正文内容:一、Delphi的多线程编程1.理解多线程编程的概念和原理2.使用TThread类创建和管理线程3.线程同步和互斥的技术和方法4.处理线程间通信和数据共享的问题5.性能优化和异常处理的注意事项二、数据库操作1.连接和配置数据库的方法2.SQL语句的使用和优化3.使用数据集和数据绑定技术实现数据可视化4.事务处理和数据的增删改查操作5.数据库连接池和安全性的考虑三、图形用户界面设计1.Delphi中的控件和容器的使用2.自定义控件和界面风格3.响应用户交互和输入的事件处理4.使用图像和图表进行数据可视化5.快捷键和界面布局的优化四、网络编程1.TCP/IP协议和Socket编程的基本概念2.使用TClientSocket和TServerSocket实现基于TCP的通信4.安全通信和网络编程中的加密技术5.服务器端的负载均衡和高并发处理五、常用算法的实现1.排序算法的比较和选择2.查找算法和数据结构的应用3.图算法和最短路径的查找4.字符串处理和正则表达式的应用5.数据压缩和加密算法的实现总结:本文对Delphi程序设计的进阶知识进行了详细的阐述,包括多线程编程、数据库操作、图形用户界面设计、网络编程和常用算法的实现。
通过掌握这些知识,读者可以提高自己的程序设计能力,并在实际项目中运用到Delphi开发中。
希望本文对读者有所帮助,为其进一步学习和探索Delphi程序设计打下坚实的基础。
为Delphi移动开发准备的跨平台异步Socket为Delphi移动开发准备的跨平台异步Socket 浏览:650加⼊我的收藏楼主:⼀、引⾔在D10.1 Berlin之前,Delphi移动开发⼀直缺少⼀个使⽤⽅便、跨平台的异步socket控件,除了Indy组件,连最基本的socket单元都缺少。
所以,移动开发时实现移动客户端和电脑服务器之间、⼿机之间的异步通信⽐较困难。
本⼈以前在实现⼿机端的消息传输时,还是通过HTTP轮询形式实现,响应速度慢、服务端压⼒⼤,不适合⼤并发即时通信开发。
D10.1 Berlin版本发布之后,这样的局⾯改观了,因为Berlin中提供了⼀个跨平台Socket单元,尽管不是异步的,但是,通过我们通过使⽤多线程等技术的再度封装,就可以实现异步通信——收到消息、连通、断开、出错等,都激发相应的事件。
本⼈近⽇就为QuickBurro中间件的移动开发组件增加了⼀个跨平台的异步Socket控件: TMBSocket。
下⾯的代码是类声明,后图⽰意其属性、事件:typeTConnectedEvent = procedure(Sender: TObject; Socket: TSocket) of object;TDisconnectedEvent = procedure(Sender: TObject; Socket: TSocket) of Object;TDataArrivedEvent = procedure(Sender: TObject; Socket: TSocket) of Object;TErrorEvent = procedure(Sender: TObject; Socket: TSocket; const Error: string; var CanClose: boolean) of Object; //TMBSocket=class(TComponent)privateRawSocket: TSocket;fHostName: string;fHostAddress: string;fPort: integer;fActive: boolean;fConnected: boolean;//fOnConnect: TConnectedEvent;fOnDisconnect: TDisconnectedEvent;fOnDataArrive: TDataArrivedEvent;fOnError: TErrorEvent;//procedure SetHostName(aHostName: string);procedure SetHostAddress(aHostAddress: string);procedure SetPort(aPort: integer);procedure SetActive(aActive: boolean);publicconstructor Create(AOwner: TComponent); override;destructor Destroy(); override;procedure Open;procedure Close;function SendBuff(const BufferPtr: pointer; const DataLength: integer): boolean;procedure ReceiveData(const BufferPtr: pointer; const Bytes: integer);function DataLength(): integer;publishedproperty HostName: string read fHostName write SetHostName;property HostAddress: string read fHostAddress write SetHostAddress;property Port: integer read fPort write SetPort;property Active: boolean read fActive write SetActive;property Connected: boolean read fConnected;//property OnConnect: TConnectedEvent read fOnConnect write fOnConnect;property OnDisconnect: TDisconnectedEvent read fOnDisconnect write fOnDisconnect; property OnDataArrive: TDataArrivedEvent read fOnDataArrive write fOnDataArrive;property OnError: TErrorEvent read fOnError write fOnError;End;此帖⼦包含附件:PNG 图像⼤⼩:21.1K----------------------------------------------樵夫的⼤马甲作者: jopher3 (樵夫的马六甲)▲▲△△△-注册会员2017-1-2212:46:11⼆、电脑端服务程序为了测试⼿机端TMBSocket控件的异步通信效果,我们先⽤TServerSocket快速搭建⼀个测试服务器,让它与⼿机端程序进⾏通信。
Delphi7网络通信控件ServerSocket 和ClientSocket步步学(1)一.首先DELPHI7安装这俩控件打开delphi菜单component->install packages->Add选择delphi安装目录下的Bin 下的dclsockets70.bpl 确定后就安装在internet标签中了这时就在DELPHI7的组件(internet)面板上看到两个新加入的控件ServerSocket 和ClientSocket (下图)二.开始使用上面的两个网络控件编程1.建立一个工程file-------new--------application 名为project1.dpr2.放五个控件在桌面上edit1memo1button1serversocket1clientsocket1Serversocket 和clientsocket 的属性设置如下图上图说明: PORT 通信端口号ADDRESS 服务器IPHOST 服务器名SERVERTYPE 通信方式: 阻塞方式, 非阻塞方式CLIENTTYPE 通信方式: 阻塞方式, 非阻塞方式三.编写程序1.发送字符串implementation{$R *.dfm}procedure TForm1.FormShow(Sender: TObject);beginserversocket1.Open;clientsocket1.Open;end;procedure TForm1.Button1Click(Sender: TObject);beginclientsocket1.Socket.SendText(edit1.Text);end;procedure TForm1.ServerSocket1ClientRead(Sender: TObject;Socket: TCustomWinSocket);beginmemo1.Lines.Add(socket.ReceiveText);end;2.发送十六进制数implementation{$R *.dfm}procedure TForm1.FormShow(Sender: TObject); beginserversocket1.Open;clientsocket1.Open;end;procedure TForm1.Button1Click(Sender: TObject); var date:array[1..5] of byte;begindate[1]:=$aa;date[2]:=$12;date[3]:=$a4;date[4]:=$7f;date[5]:=$de;clientsocket1.Socket.SendBuf(date,5); end;procedure TForm1.ServerSocket1ClientRead(Sender: TObject;Socket: TCustomWinSocket);var s:string;date:array[1..5] of byte;beginsocket.ReceiveBuf(date,5);s:=inttohex(date[1],2)+' '+inttohex(date[2],2)+' '+inttohex(date[3],2) +' '+inttohex(date[4],2)+' '+inttohex(date[5],2);memo1.Lines.Add(s);end;。
delphi7 serversocket的多线程模式的用法在 Delphi 7 中,使用 ServerSocket 组件实现多线程模式可以通过以下步骤进行:1. 在 Delphi 7 的主界面上,双击 "ServerSocket" 组件,将其添加到窗体上。
2. 在 "ServerSocket1" 组件的 "Properties" 属性中,设置"Active" 属性为 True,启用服务器端。
3. 在 "ServerSocket1" 组件的 "Properties" 属性中,设置 "Port" 属性为服务器监听的端口号。
例如,设置为 1234。
4. 在 "ServerSocket1" 组件的 "Events" 事件属性中添加以下代码,实现多线程处理客户端连接请求:```procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket);begin// 创建一个线程处理客户端连接TMyThread.Create(Socket);end;```5. 创建一个继承自 TThread 的自定义线程类,用于处理客户端连接和通信。
代码示例如下:```typeTMyThread = class(TThread)privateSocket: TCustomWinSocket;publicconstructor Create(ASocket: TCustomWinSocket);procedure Execute; override;end;constructor TMyThread.Create(ASocket: TCustomWinSocket); begininherited Create(False);Socket := ASocket;end;procedure TMyThread.Execute;varBuffer: array[0..1023] of AnsiChar;BytesReceived: Integer;begin// 处理客户端连接trywhile not Terminated dobegin// 接收客户端发送的数据BytesReceived := Socket.ReceiveBuf(Buffer, SizeOf(Buffer) - 1);Buffer[BytesReceived] := #0; // 末尾添加字符串结束标志// 在主线程执行 GUI 操作(如果需要)Synchronize(procedurebegin// 在此处更新界面或执行其他需要在主线程执行的操作 // Example: Memo1.Lines.Add(Buffer);end);// 处理接收到的数据// ...end;finally// 关闭客户端连接Socket.Close;Socket.Free;end;end;```在上述代码中,TMyThread 类继承自 TThread,通过重写Execute 方法,实现在独立线程中处理客户端连接和通信的逻辑。
Delphi 多线程介绍,以及线程类TThread 分析Delphi 中有一个线程类TThread 用来实现多线程编程TThread 类的几个成员作一简单介绍,再说明一下Execute 的实现和Synchronize 的用法就完了。
然而这并不是多线程编程的全部,我写此文的目的在于对此作一个补充。
线程 本质上是进程中一段 并发 运行的代码。
一个进程至少有一个线程,即所谓的主线程。
同时还可以有多个子线程。
当一个进程中用到超过一个线程时,就是所谓的“多线程”。
1、CreateThread 、long _beginthread 、BeginThread 介绍 用Windows API 来创建线程,API 函数 CreateThread 的定义原型:1 2 3 4 5 6 7 8 HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, //线程属性(用于在NT 下进行线程的安全属性设置,在9X 下无效) DWORD dwStackSize, //堆栈大小 LPTHREAD_START_ROUTINE lpStartAddress, //起始地址,即线程函数的入口,直至线程函数结束,线程结束 LPVOID lpParameter, //参数 DWORD dwCreationFlags, //创建标志(用于设置线程创建时的状态) LPDWORD lpThreadId //线程ID ); //最后返回线程HandleCreateThread 参数很多,而且在C Runtime Library 里提供了一个通用的线程函数(理论上可以在任何支持线程的OS 中使用):1 unsigned long _beginthread(void (_USERENTRY *__start)(void *), unsigned __stksize, void *__arg);Delphi 也提供了一个相同功能的类似函数:1 f unction BeginThread(2 3 4 5 6 7 8 SecurityAttributes: Pointer;StackSize: LongWord;ThreadFunc: TThreadFunc;Parameter: Pointer;CreationFlags: LongWord;var ThreadId: LongWord): Integer;这三个函数的功能是基本相同的,它们都是将线程函数中的代码放到一个独立的线程中执行。
delphi网络编程掌握网络编程的基本原理掌握网络控制的使用方法具备通过使用网络控制开发简单网络应用程序的能力Delphi 的Socket 编程概述Socket 是建立在传输层协议(主要是TCP 和UDP)上的一种套接字规范,它定义两台计算机间进行通信的规范(即一种编程规范),如果说两台计算机是利用一个“通道”进行通信,那么这个“通道”的两端就是两个套接字。
套接字屏蔽了底层通信软件和具体操作系统的差异,使得任何两台安装了TCP 协议软件和实现了套接字规范的计算机之间的通信成为可能。
在Delphi 中,其底层的Socket 也应该是Windows的Socket。
Socket减轻了编写计算机间通信软件的难度。
Inprise在Delphi中对Windows Socket进行了有效的封装,使用户可以很方便地编写网络通信程序。
TCP/IP 协议及特点1. TCP/IP体系结构TCP/IP 协议实际上就是在物理网上的一组完整的网络协议。
其中TCP 是提供传输层服务,而IP 则是提供网络层服务。
TCP/IP协议簇(如图1所示)包括协议如下。
(1) IP:网间协议(Internet Protocol)。
此协议负责主机间数据的路由和网络上数据的存储。
同时为ICMP,TCP,UDP提供分组发送服务,用户进程通常不需要涉及这一层。
(2) ARP:地址解析协议(Address Resolution Protocol)。
此协议将网络地址映射到硬件地址。
(3) RARP:反向地址解析协议(Reverse Address Resolution Protocol)。
此协议将硬件地址映射到网络地址(4) ICMP:网间报文控制协议(Internet Control Message Protocol)。
此协议处理信关和主机的差错和传送控制。
(5) TCP:传送控制协议(Transmission Control Protocol)。
unit UnitTCPUDP;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls,WinSock, ExtCtrls, ComCtrls,inif iles,StrUtils;constWM_SOCK = WM_USER + 82; {自定义windows消息}//在tcp 服务器方式下,WM_SOCK为监听消息// WM_SOCK+1到 WM_SOCK+MAX_ACCEPT 为与连接客户端进行通讯时的消息MAX_ACCEPT=100;FD_SET= MAX_ACCEPT;typeTFormTCPUDP = class(TForm)BtnSend: TButton;MemoReceive: TMemo;EditSend: TEdit;Label2: TLabel;Label3: TLabel;Bevel2: TBevel;STOpCode: TStaticText;STIndex: TStatic Text;STCommand: TStatic Text;GroupBox1: TGroupBox;GroupBox2: TGroupBox;GroupBox3: TGroupBox;RBTCP: TRadioButton;RBUDP: TRadioButton;Panel1: TPanel;RBClient: TRadioButton;RBServer: TRadioButton;GroupBox4: TGroupBox;BtnConnect: TButton;BtnClose: TButton;Bevel1: TBevel;StatusBar1: TStatusBar;PanelDest: TPanel;Label4: TLabel;EditRemoteHost: TEdit;Label5: TLabel;EditRemotePort: TEdit;Label6: TLabel;CmbSendTo: TComboBox;Label7: TLabel;PanelLocal: TPanel;ChkBind: TCheckBox;EditHostPort: TEdit;Label1: TLabel;procedure BtnSendClick(Sender: TObject);procedure BtnConnectClick(Sender: TObject);procedure RBTCPClick(Sender: TObject);procedure RBUDPClick(Sender: TObject);procedure BtnCloseClick(Sender: TObject);procedure FormClose(Sender: TObject; var Action: TCloseAction);procedure RBClientClick(Sender: TObject);procedure RBServerClick(Sender: TObject);procedure ChkBindClick(Sender: TObject);procedure FormCreate(Sender: TObject);procedure EditHostPortChange(Sender: TObject);procedure EditRemoteHostChange(Sender: TObject);procedure EditRemotePortChange(Sender: TObject);procedure FormActivate(Sender: TObject);procedure CmbSendToKeyPress(Sender: TObject; var Key: Char); {消息接送}private{ Private declarations }FirstFlag:Boolean;INIPath:String;procedure R eadData(var Message: TMessage);function ReadTCPUDPIni():boolean; //读取配置信息procedure Wri t eIniStr(FileName:String;section:string;Ident:string;StringValue:string);//写系统信息 procedure Wri t eIniBool(FileName:String;section:string;Ident:string;BoolValue:Boolean);//写系统信息 protected{ Protected declarations }{ other fields and methods}procedure wndproc(var message:Tmessage);override;public{ Public declarations }end;constDATA_LENGTH =120; //数据长度typeTUDPaction = packed recordopcode:byte; //操作码index:word; //序列号Command:byte; //命令字data:array[0..(DATA_LENGTH-1)] of char; //数据end;varFormTCPUDP: TFormTCPUDP;AcceptSock:Array[0..MAX_ACCEPT] OF Tsocket;FSockAccept : Array[0..MAX_ACCEPT] OF TSockAddrIn;AcceptSockFlag: Array[0..MAX_ACCEPT] OF boolean;AcceptNum:integer=0;FSockLocal : TSockAddrIn;PackageID:integer=0; //包序号BindFlag:Boolean=true;TcpFlag:Boolean=false;ServerFlag:Boolean=false;function WinSockInital(Handle: HWnd):bool;Procedure WinSockClose();implementation{$R *.dfm}{始化SOCKET}function WinSockInital(Handle: HWnd):bool;var TempWSAData: TWSAData;i:integer;beginresult := false;{ 1 初始化SOCKET}if WSAStartup(2, TempWSAData)=1 then //2表示启用winsock2 exi t;{若是用UDP通信,则用}if TcpFlag thenAcceptSock[0]:=Socket(AF_INET,SOCK_STREAM,0)elseAcceptSock[0]:=Socket(AF_INET,SOCK_DGRAM,0);if AcceptSock[0]=SOCKET_ERROR thenexi t;if (BindFlag and not tcpflag) or (Serverflag and tcpflag) thenif bind(AcceptSock[0],FSockLocal,sizeof(FSockLocal))<>0 then beginWinSockClose();exit;end;if Tcpflag thenif Serverflag thenbeginif Listen(AcceptSock[0],1)<>0 then //等待连接队列的最大长度为1begin WinSockClose();exi t;end;endelseif connect(AcceptSock[0],FSockAccept[0],sizeof(FSockAccept[0]))<>0 thenbeginWinSockClose();exi t;end;{FD_READ 在读就绪的时候, 产生WM_SOCK 自定义消息号}if not TcpFlag thenWSAAsyncSelect(AcceptSock[0], Handle , WM_SOCK, FD_READ)else if Serverflag thenWSAAsyncSelect(AcceptSock[0], Handle , WM_SOCK, FD_READ or FD_ACCEPT or FD_CLOSE) elseWSAAsyncSelect(AcceptSock[0], Handle , WM_SOCK, FD_READ or FD_CLOSE);R esult:=true;end;{关闭SOCKET}Procedure WinSockClose();var i:integer;beginfor i:=1 to MAX_ACCEPT DOif AcceptSockFlag[i] thenbeginCloseSocket(AcceptSock[i]);AcceptSockFlag[i]:=false;end;CloseSocket(AcceptSock[0]); {closesocket函数用来关闭一个描述符为AcceptSock[0]套接字}WSACleanup;end;function TFormTCPUDP.ReadTCPUDPIni():boolean;var ti:TiniFile;beginti:=TIniFile.Create(INIPath+'TCPUDP.ini');EditHostPort.text:=ti.ReadString('Setting','LocalPort','');ChkBind.Checked:=ti.ReadBool('Setting','BindStatus',false);EditR emotePort.text:=ti.ReadString('Setting','RemotePort','');EditR emoteHost.text:=ti.ReadString('Setting','R emoteHost','');RBTCP.Checked:=ti.ReadBool('Setting','TCPStatus',false);RBUDP.Checked:=not RBTCP.Checked;RBServer.Checked:=ti.R eadBool('Setting','ServerStatus',false);RBClient.Checked:=not RBServer.Checked;end;procedure TFormTCPUDP.WriteIniStr(FileName:String;Section:string;Ident:string;StringValue:string); var ti:TiniFile;beginti:=TIniFile.Create(FileName);ti.writestring(section,Ident,StringValue);ti.Free;end;procedure TFormTCPUDP.WriteIniBool(FileName:String;Section:string;Ident:string;BoolValue:Boolean); var ti:TiniFile;beginti:=TIniFile.Create(FileName);ti.writebool(section,Ident,BoolValue);ti.Free;end;procedure TFormTCPUDP.BtnSendClick(Sender: TObject);var SEND_PACKAGE : TUDPaction; //数据发送i:integer;s:String;beginFillchar(SEND_PACKAGE.data,Data_Length,chr(0));SEND_PACKAGE.data[0]:='1';SEND_PACKAGE.data[1]:='2';SEND_PACKAGE.data[2]:='3';SEND_PACKAGE.opcode:=2;SEND_PACKAGE.index:=PackageID;SEND_mand:=3;s:=editsend.Text;for i:=0 to length(EditSend.Text)-1 doSEND_PACKAGE.data[i]:=s[i+1];PackageID:=PackageID+1;if not (Tcpflag and Serverflag) thensendto(AcceptSock[0], SEND_PACKAGE,sizeof(SEND_PACKAGE), 0, FSockAccept[0], sizeof(FSockAcce pt[0]))else if AcceptNum=0 thenApplication.MessageBox('没有一个客户端和您建立连接','信息提示',MB_OK)elsebegini:=pos(' ',CmbSendto.Text);if i>0 thenbegini:=strtoint(MidStr(CmbSendTo.Text,8,i-8));sendto(AcceptSock[i], SEND_PACKAGE,sizeof(SEND_PACKAGE), 0, FSockAccept[i], sizeof(FSockAcce pt[i]));endelseApplication.MessageBox('您没有选择发送方','错误提示',MB_OK);end;// sendto(AcceptSock[0], NbtstatPacket,50, 0, FSockAccept[0], sizeof(FSockAccept[0]));end;procedure TFormTCPUDP.BtnConnectClick(Sender: TObject);var s:String;i:integer;begins:='正在建立连接....';StatusBar1.Panels[0].Text:=s;Application.ProcessMessages;FSockLocal.sin_family:=AF_INET;FSockLocal.sin_port:=htons(strtoint(Edi t Hostport.Text));FSockAccept[0].sin_family:=AF_INET;FSockAccept[0].sin_port:=htons(strtoint(EditRemoteport.Text));FSockAccept[0].SIn_Addr.S_addr := inet_addr(PChar(EditR emoteHost.Text));//inet_addr(pchar(IP)); if WinSockInital(FormTCPUDP.Handle) thenbeginBtnConnect.Enabled:=false;BtnClose.Enabled:=true;BtnSend.Enabled:=true;s:='连接成功!';if ChkBind.Checked thens:=s+', ---绑定端口';if RBTcp.Checked thenbegins:=s+',---TCP方式';if RBServer.Checked thens:=s+',---服务端'elses:=s+',---客户端';endelses:=s+',---UDP方式';if tcpflag and Serverflag thenbeginAcceptNum:=0;CmbSendto.Clear;StatusBar1.Panels[2].Text:='共有:'+inttostr(AcceptNum)+'个连接';end;endelsebeginfor i:=0 to StatusBar1.Panels.count-1 doStatusBar1.Panels[i].Text:='';s:='创建套接字失败!!';end;StatusBar1.Panels[0].Text:=s;end;procedure TFormTCPUDP.wndproc(var Message: TMessage);beginif (Message.Msg>=WM_SOCK) and (Message.Msg<=WM_SOCK+MAX_ACCEPT) thenReadData(Message)elseinherited wndproc(message);end;procedure TFormTCPUDP.ReadData(var Message: TMessage);varReceive_PACKAGE : TUDPaction; //数据发送flen,len,i,index: integer;Event: word;beginIndex:=(Message.Msg-WM_SOCK);flen:=sizeof(FSockAccept[Index]);Event := WSAGetSelectEvent(Message.LParam);if Event = FD_READ thenbeginlen := recvfrom(AcceptSock[Index], Receive_PACKAGE, sizeof(R eceive_PACKAGE), 0, FSockAccept[In dex], Flen);if len> 0 thenbeginStatusBar1.Panels[0].Text:='收到来自ip地址:'+inet_ntoa(FSockAccept[Index].sin_addr)+' 端口:'+inttostr(ntohs(FSockAccept[Index].sin_port))+'的数据';StOpCode.Caption:= format('%.2d',[Receive_PACKAGE.opCode]);StIndex.Caption:= format('%d',[Receive_PACKAGE.Index]);StCommand.Caption:= format('%.2d',[R eceive_mand]);MemoR eceive.Lines.Add(StrPas(Receive_PACKAGE.data))end;endelse if Event=FD_ACCEPT thenbeginfor i:=1 to MAX_ACCEPT DOif not AcceptSockFlag[i] thenbeginflen:=Sizeof(FSockAccept[i]);AcceptSock[i]:=accept(AcceptSock[0],@FSockAccept[i],@flen);WSAAsyncSelect(AcceptSock[i], Handle , WM_SOCK+i, FD_READ or FD_CLOSE);AcceptSockFlag[i]:=true;AcceptNum:=AcceptNum+1;CmbSendto.I tems.Add('套接口:'+inttostr(i)+' 地址:'+inet_ntoa(FSockAccept[i].sin_addr)+' 端口:'+inttostr(ntohs(FSockAccept[i].sin_port)));break;end;StatusBar1.Panels[2].Text:='共有:'+inttostr(AcceptNum)+'个连接';endelse if Event=FD_CLOSE thenbeginWSAAsyncSelect(AcceptSock[index], FormTCPUDP.Handle, 0, 0);if index<>0 thenbeginfor i:=0 to CmbSendto.I tems.Count-1 doif CmbSendto.I tems.Strings[i]= '套接口:'+inttostr(index)+' 地址:'+inet_ntoa(FSockAccept[index].sin_addr)+' 端口:'+inttostr(ntohs(FSockAccept[index].sin_port)) thenbeginCmbSendto.Items.Delete(i);break;end;CloseSocket(AcceptSock[index]);AcceptSockFlag[index]:=false;AcceptNum:=AcceptNum-1;StatusBar1.Panels[2].Text:='共有:'+inttostr(AcceptNum)+'个连接';end;end;end;procedure TFormTCPUDP.RBTCPClick(Sender: TObject);beginwriteiniBool(INIPath+'TCPUDP.ini','Setting','TCPStatus',true);RBServer.Enabled:=true;RBClient.Enabled:=true;if RBServer.Checked thenbeginPanelDest.Visible:=false;CmbSendto.Enabled:=true;endelsebeginPanelDest.Visible:=true;PanelLocal.Visible:=false;end;ChkBind.Enabled:=false;TcpFlag:=true;end;procedure TFormTCPUDP.RBUDPClick(Sender: TObject);beginwriteiniBool(INIPath+'TCPUDP.ini','Setting','TCPStatus',false);RBServer.Enabled:=false;RBClient.Enabled:=false;PanelDest.Visible:=true;TcpFlag:=false;ChkBind.Enabled:=true;CmbSendto.Enabled:=false;PanelLocal.Visible:=true;end;procedure TFormTCPUDP.BtnCloseClick(Sender: TObject);var i:integer;beginWinSockClose();BtnConnect.Enabled:=true;BtnClose.Enabled:=false;BtnSend.Enabled:=false;CmbSendto.Clear;for i:=0 to StatusBar1.Panels.count-1 doStatusBar1.Panels[i].Text:='';Statusbar1.Panels[0].Text:='已关闭套接字!!';end;procedure TFormTCPUDP.FormClose(Sender: TObject; var Action: TCloseAction); beginif BtnClose.Enabled then WinSockClose();end;procedure TFormTCPUDP.RBClientClick(Sender: TObject);beginwriteiniBool(INIPath+'TCPUDP.ini','Setting','ServerStatus',false);ServerFlag:=false;PanelDest.Visible:=true;CmbSendto.Enabled:=false;if Tcpflag thenPanelLocal.Visible:=falseelsePanelLocal.Visible:=true;end;procedure TFormTCPUDP.RBServerClick(Sender: TObject);beginwriteiniBool(INIPath+'TCPUDP.ini','Setting','ServerStatus',true);ServerFlag:=true;if Tcpflag thenbeginPanelDest.Visible:=false;CmbSendto.Enabled:=true;ChkBind.Enabled:=false;ChkBind.Checked:=true;endelseChkBind.Enabled:=true;PanelLocal.Visible:=true;end;procedure TFormTCPUDP.ChkBindClick(Sender: TObject);beginwriteiniBool(INIPath+'TCPUDP.ini','Setting','BindStatus',ChkBind.Checked); BindFlag:=ChkBind.Checked;end;procedure TFormTCPUDP.FormCreate(Sender: TObject);var i:integer;beginFirstFlag:=true;for i:=1 to MAX_ACCEPT doAcceptSockFlag[i]:=false;INIPath:=extractFilePath(ParamStr(0));end;procedure TFormTCPUDP.EditHostPortChange(Sender: TObject);beginwriteiniStr(INIPath+'TCPUDP.ini','Setting','LocalPort',EditHostPort.Text);end;procedure TFormTCPUDP.EditRemoteHostChange(Sender: TObject);beginwriteiniStr(INIPath+'TCPUDP.ini','Setting','RemoteHost',EditR emoteHost.Text); end;procedure TFormTCPUDP.EditRemotePortChange(Sender: TObject);beginwriteiniStr(INIPath+'TCPUDP.ini','Setting','RemotePort',EditRemotePort.Text); end;procedure TFormTCPUDP.FormActivate(Sender: TObject);beginif FirstFlag thenbeginFirstFlag:=false;ReadTCPUDPIni();end;end;procedure TFormTCPUDP.CmbSendToKeyPress(Sender: TObject; var Key: Char); beginkey:=chr(0);end;end.。
DELPHI高性能大容量SOCKET并发(一):IOCP完成端口例子介绍例子主要包括IOCP控件封装、服务端实现、传输协议和日志、控制、SQL查询、上传、下载等协议实现,并包括一些初步的性能测试结果。
服务端:界面截图如下:提供服务和桌面方式运行,桌面方式可直接打开程序,方便日常调试,可以使用命令行注册或卸载服务,在CMD中输入D:\DEMO\IOCPDemo\Bin\IOCPDemoSvr.exe -install来注册服务,在CMD输入D:\DEMO\IOCPDemo\Bin\IOCPDemoSvr.exe -uninstall来卸载服务。
客户端:界面截图如下:主要实现了服务端日志查看,服务端协议类表查看,SQL语句执行协议,上传、下载协议实现,其中对上传、下载实现了一个多线程同时传,用于测试服务器并发性能。
性能:支持超过2000个链接及以上同时上传文件,不过每个连接上传速度只有1到2K。
支持超过2W个连接同时在线传输命令。
单实例上传下载测试结果:从测试结果可以看出随着发送包增大,速度变快。
这里存在一个风险,就是SOCKET传输失败的次数也会增加。
(二):IOCP完成端口控件封装IOCP完成端口介绍:完成端口模型是Windows平台下SOCKET端口模型最为复杂的一种I/O模型。
如果一个应用程序需要同时管理为数众多的套接字,而且希望随着系统内安装的CPU数量的增多,应用程序的性能也可以线性提升,采用完成端口模型,往往可以达到最佳的系统性能。
完成端口可以管理成千上万的连接,长连接传文件可以支持5000个以上,长连接命令交互可以支持20000个以上。
这么大并发的连接,更需要考虑的是应用场景,按照100M的网卡传输速度12.5MB/S,如果是5000个传文件连接,则每个连接能分到的速度2.56KB/S;如果是20000个命令交互连接,则每个连接分到的吞吐量是655B/S,这种速度的吞吐量对很多应用是不满足,这时就要考虑加大网卡的传输速度或实现水平扩展,这个我们后续会介绍。
利用Delphi编写Socket通信程序一、前言随着计算机技术的不断发展,网络应用得到了越来越广泛的应用,而Socket编程作为网络编程中最基础、最重要的部分,具有非常重要的意义。
Delphi是一种构建Windows应用程序的高级集成开发工具,支持对象化设计、事件驱动、可视化编程等多种编程方式。
Delphi具有非常强大的GUI设计、数据访问、组件开发等特点,可以方便地应用到Socket编程中,实现Socket通信程序的开发。
本文就介绍利用Delphi编写Socket通信程序相关的知识。
二、Socket编程基础Socket是一种用于网络通信的编程接口,它把网络通信抽象成发送和接收数据的过程。
Socket编程分为客户端和服务器两部分,客户端负责向服务器发起连接请求,服务器则负责接收客户端的连接请求,并根据请求提供相应的服务。
Socket编程中,通常使用TCP/IP协议进行数据传输。
TCP协议是一种可靠的、面向连接的协议,它通过三次握手建立连接,保证数据的可靠性、完整性和顺序性。
而UDP协议则是一种不可靠的、无连接的协议,它不保证数据的可靠性和完整性,但具有数据传递速度快等特点。
通常,TCP协议适用于对数据可靠性要求较高的应用场景,如文件传输、邮件系统、即时通信等;而UDP协议适用于对数据传输速度要求较高的应用场景,如视频直播、网络游戏等。
三、利用Delphi编写Socket通信程序1. 创建Socket使用Delphi进行Socket编程,可通过TServerSocket和TClientSocket组件来实现。
TServerSocket组件用于创建服务器Socket,TClientSocket组件用于创建客户端Socket。
在Delphi中,可以通过在组件面板中拖拽TServerSocket或TClientSocket组件来创建Socket。
使用TServerSocket组件创建服务器Socket的示例代码如下:```delphiprocedure TMyForm.FormCreate(Sender: TObject);begin ServerSocket1.Port := 8888;ServerSocket1.Active := True;end;procedure TMyForm.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket);beginMemo1.Lines.Add(Socket.RemoteAddress + ' connected.');end;```上述代码中,将ServerSocket1的Port属性设置为8888,表示该服务器监听8888端口的连接请求。
delphi之多线程编程(⼀)本⽂的内容取⾃⽹络,并重新加以整理,在此留存仅仅是⽅便⾃⼰学习和查阅。
所有代码均亲⾃测试 delphi7下测试有效。
图⽚均为⾃⼰制作。
多线程应该是编程⼯作者的基础技能, 但这个基础我从来没学过,所以仅仅是看上去会⼀些,明⽩了2+2的时候,其实我还不知道1+1。
开始本应该是⼀篇洋洋洒洒的⽂字, 不过我还是提倡先做起来, 在尝试中去理解.先试试这个:procedure TForm1.Button1Click(Sender: TObject);vari: Integer;beginfor i := 0 to 500000 dobeginCanvas.TextOut(10, 10, IntToStr(i));end;end;上⾯程序运⾏时, 我们的窗体基本是 "死" 的, 可以在你在程序运⾏期间拖动窗体试试...Delphi 为我们提供了⼀个简单的办法(Application.ProcessMessages)来解决这个问题:procedure TForm1.Button1Click(Sender: TObject);vari: Integer;beginfor i := 0 to 500000 dobeginCanvas.TextOut(10, 10, IntToStr(i));Application.ProcessMessages;end;end;这个 Application.ProcessMessages; ⼀般⽤在⽐较费时的循环中, 它会检查并先处理消息队列中的其他消息.但这算不上多线程, 譬如: 运⾏中你拖动窗体, 循环会暂停下来...在使⽤多线程以前, 让我们先简单修改⼀下程序:function MyFun: Integer;vari: Integer;beginfor i := 0 to 500000 dobeginForm1.Canvas.Lock;Form1.Canvas.TextOut(10, 10, IntToStr(i));Form1.Canvas.Unlock;end;Result := 0;end;procedure TForm1.Button1Click(Sender: TObject);beginMyFun;end;细数上⾯程序的变化:1、⾸先这还不是多线程的, 也会让窗体假 "死" ⼀会;2、把执⾏代码写在了⼀个函数⾥, 但这个函数不属于 TForm1 的⽅法, 所以使⽤ Canvas 是必须冠以名称(Form1);3、既然是个函数, (不管是否必要)都应该有返回值;4、使⽤了 500001 次 Lock 和 Unlock.Canvas.Lock 好⽐在说: Canvas(绘图表⾯)正忙着呢, 其他想⽤ Canvas 的等会;Canvas.Unlock : ⽤完了, 解锁!在 Canvas 中使⽤ Lock 和 Unlock 是个好习惯, 在不使⽤多线程的情况下这⽆所谓, 但保不准哪天程序会扩展为多线程的; 我们现在学习多线程, 当然应该⽤.在 Delphi 中使⽤多线程有两种⽅法: 调⽤ API、使⽤ TThread 类; 使⽤ API 的代码更简单.function MyFun(p: Pointer): Integer; stdcall;vari: Integer;beginfor i := 0 to 500000 dobeginForm1.Canvas.Lock;Form1.Canvas.TextOut(10, 10, IntToStr(i));Form1.Canvas.Unlock;end;Result := 0;end;procedure TForm1.Button1Click(Sender: TObject);varID: THandle;beginCreateThread(nil, 0, @MyFun, nil, 0, ID);end;代码分析:CreateThread ⼀个线程后, 算上原来的主线程, 这样程序就有两个线程、是标准的多线程了;CreateThread 第三个参数是函数指针, 新线程建⽴后将⽴即执⾏该函数, 函数执⾏完毕, 系统将销毁此线程从⽽结束多线程的故事. CreateThread 要使⽤的函数是系统级别的, 不能是某个类(譬如: TForm1)的⽅法, 并且有严格的格式(参数、返回值)要求, 不管你暂时是不是需要都必须按格式来;因为是系统级调⽤, 还要缀上 stdcall, stdcall 是协调参数顺序的, 虽然这⾥只有⼀个参数没有顺序可⾔, 但这是使⽤系统函数的惯例. CreateThread 还需要⼀个 var 参数来接受新建线程的 ID, 尽管暂时没⽤, 但这也是格式; 其他参数以后再说吧.这样⼀个最简单的多线程程序就出来了, 咱们再⽤ TThread 类实现⼀次typeTMyThread = class(TThread)protectedprocedure Execute; override;end;procedure TMyThread.Execute;vari: Integer;beginFreeOnTerminate := True; {这可以让线程执⾏完毕后随即释放}for i := 0 to 500000 dobeginForm1.Canvas.Lock;Form1.Canvas.TextOut(10, 10, IntToStr(i));Form1.Canvas.Unlock;end;end;procedure TForm1.Button1Click(Sender: TObject);beginTMyThread.Create(False);end;TThread 类有⼀个抽象⽅法(Execute), 因⽽是个抽象类, 抽象类只能继承使⽤, 上⾯是继承为 TMyThread.继承 TThread 主要就是实现抽象⽅法 Execute(把我们的代码写在⾥⾯), 等我们的 TMyThread 实例化后, ⾸先就会执⾏ Execute ⽅法中的代码.按常规我们⼀般这样去实例化:procedure TForm1.Button1Click(Sender: TObject);varMyThread: TMyThread;beginMyThread := TMyThread.Create(False);end;因为 MyThread 变量在这⾥毫⽆⽤处(并且编译器还有提⽰), 所以不如直接写做 TMyThread.Create(False);我们还可以轻松解决⼀个问题, 如果: TMyThread.Create(True) ?这样线程建⽴后就不会⽴即调⽤ Execute, 可以在需要的时候再⽤ Resume ⽅法执⾏线程, 譬如:procedure TForm1.Button1Click(Sender: TObject);varMyThread: TMyThread;beginMyThread := TMyThread.Create(True);MyThread.Resume;end;//可简化为:procedure TForm1.Button1Click(Sender: TObject);beginwith TMyThread.Create(True) do Resume;end;⼀、⼊门㈠、function CreateThread(lpThreadAttributes: Pointer; {安全设置}dwStackSize: DWORD; {堆栈⼤⼩}lpStartAddress: TFNThreadStartRoutine; {⼊⼝函数}lpParameter: Pointer; {函数参数}dwCreationFlags: DWORD; {启动选项}var lpThreadId: DWORD {输出线程 ID }): THandle; stdcall; {返回线程句柄}在 Windows 上建⽴⼀个线程, 离不开 CreateThread 函数;TThread.Create 就是先调⽤了 BeginThread (Delphi ⾃定义的), BeginThread ⼜调⽤的 CreateThread.既然有建⽴, 就该有释放, CreateThread 对应的释放函数是: ExitThread, 譬如下⾯代码:procedure TForm1.Button1Click(Sender: TObject);beginExitThread(0); {此句即可退出当前程序, 但不建议这样使⽤}end;代码注释:当前程序是⼀个进程, 进程只是⼀个⼯作环境, 线程是⼯作者;每个进程都会有⼀个启动线程(或叫主线程), 也就是说: 我们之前⼤量的编码都是写给这个主线程的;上⾯的 ExitThread(0); 就是退出这个主线程;系统不允许⼀个没有线程的进程存在, 所以程序就退出了.另外: ExitThread 函数的参数是⼀个退出码, 这个退出码是给之后的其他函数⽤的, 这⾥随便给个⽆符号整数即可.或许你会说: 这个 ExitThread 挺好⽤的; 其实不管是⽤ API 还是⽤ TThread 类写多线程, 我们很少⽤到它; 因为:1、假如直接使⽤ API 的 CreateThread, 它执⾏完⼊⼝函数后会⾃动退出, ⽆需 ExitThread;2、⽤ TThread 类建⽴的线程⼜绝不能使⽤ ExitThread 退出; 因为使⽤ TThread 建⽴线程时会同时分配更多资源(譬如你⾃定义的成员、还有它的祖先类(TObject)分配的资源等等), 如果⽤ ExitThread 给草草退出了, 这些资源将得不到释放⽽导致内存泄露. 尽管 Delphi 提供了EndThread(其内部调⽤ ExitThread), 这也不需要我们⼿动操作(假如⾮要⼿动操作也是件很⿇烦的事情, 因为很多时候你不知道线程是什么时候执⾏完毕的).除了 CreateThread, 还有⼀个 CreateRemoteThread, 可在其他进程中建⽴线程, 这不应该是现在学习的重点;现在先集中精⼒把 CreateThread 的参数搞彻底.倒着来吧, 先谈谈 CreateThread 将要返回的 "线程句柄"."句柄" 类似指针, 但通过指针可读写对象, 通过句柄只是使⽤对象;有句柄的对象⼀般都是系统级别的对象(或叫内核对象); 之所以给我们的是句柄⽽不是指针, ⽬的只有⼀个: "安全";貌似通过句柄能做很多事情, 但⼀般把句柄提交到某个函数(⼀般是系统函数)后, 我们也就到此为⽌很难了解更多了; 事实上是系统并不相信我们.不管是指针还是句柄, 都不过是内存中的⼀⼩块数据(⼀般⽤结构描述), 微软并没有公开句柄的结构细节, 猜⼀下它应该包括: 真实的指针地址、访问权限设置、引⽤计数等等.既然 CreateThread 可以返回⼀个句柄, 说明线程属于 "内核对象".实际上不管线程属于哪个进程, 它们在系统的怀抱中是平等的; 在优先级(后⾯详谈)相同的情况下, 系统会在相同的时间间隔内来运⾏⼀下每个线程, 不过这个间隔很⼩很⼩, 以⾄于让我们误以为程序是在不间断地运⾏.这时你应该有⼀个疑问: 系统在去执⾏其他线程的时候, 是怎么记住前⼀个线程的数据状态的?有这样⼀个结构 TContext, 它基本上是⼀个 CPU 寄存器的集合, 线程是数据就是通过这个结构切换的, 我们也可以通过 GetThreadContext 函数读取寄存器看看.附上这个结构 TContext(或叫: CONTEXT、_CONTEXT) 的定义:PContext = ^TContext;_CONTEXT = recordContextFlags: DWORD;Dr0: DWORD;Dr1: DWORD;Dr2: DWORD;Dr3: DWORD;Dr6: DWORD;Dr7: DWORD;FloatSave: TFloatingSaveArea;SegGs: DWORD;SegFs: DWORD;SegEs: DWORD;SegDs: DWORD;Edi: DWORD;Esi: DWORD;Ebx: DWORD;Edx: DWORD;Ecx: DWORD;Eax: DWORD;Ebp: DWORD;Eip: DWORD;SegCs: DWORD;EFlags: DWORD;Esp: DWORD;SegSs: DWORD;end;CreateThread 的最后⼀个参数是 "线程的 ID";既然可以返回句柄, 为什么还要输出这个 ID? 现在我知道的是:1、线程的 ID 是唯⼀的; ⽽句柄可能不只⼀个, 譬如可以⽤ GetCurrentThread 获取⼀个伪句柄、可以⽤ DuplicateHandle 复制⼀个句柄等等.2、ID ⽐句柄更轻便.在主线程中 GetCurrentThreadId、MainThreadID、MainInstance 获取的都是主线程的 ID.㈡、启动选项function CreateThread(lpThreadAttributes: Pointer;dwStackSize: DWORD;lpStartAddress: TFNThreadStartRoutine;lpParameter: Pointer;dwCreationFlags: DWORD; {启动选项}var lpThreadId: DWORD): THandle; stdcall;CreateThread 的倒数第⼆个参数 dwCreationFlags(启动选项) 有两个可选值:0: 线程建⽴后⽴即执⾏⼊⼝函数;CREATE_SUSPENDED: 线程建⽴后会挂起等待.可⽤ ResumeThread 函数是恢复线程的运⾏; 可⽤ SuspendThread 再次挂起线程.这两个函数的参数都是线程句柄, 返回值是执⾏前的挂起计数.什么是挂起计数?SuspendThread 会给这个数 +1; ResumeThread 会给这个数 -1; 但这个数最⼩是 0.当这个数 = 0 时, 线程会运⾏; > 0 时会挂起.如果被 SuspendThread 多次, 同样需要 ResumeThread 多次才能恢复线程的运⾏.在下⾯的例⼦中, 有新线程不断给⼀个全局变量赋随机值;同时窗体上的 Timer 控件每隔 1/10 秒就把这个变量写在窗体标题;在这个过程中演⽰了 ResumeThread、SuspendThread 两个函数.//上⾯图⽚中演⽰的代码。