mysql socket delphi_异步SOCKET分包和组包的一种通用算法

unit uPackage;

// 应用协议

// cxg 2016-9-23

// 包=包头+包体

interface

uses

SysUtils, Classes, PeachCtrl.Net.IocpTcpServer, System.Generics.Collections,

Winapi.Windows, System.SyncObjs, PeachCtrl.Net.BlockingTcpClient, untLog,

System.Math;

const // 包长

pack_len = 8192;

const // 版本号

ver_1 = 1;

const // 命令分类

cmd_qry_req = 1;

cmd_qry_res = 2;

cmd_post_req = 3;

cmd_post_res = 4;

cmd_up_file_req = 5;

cmd_up_file_res = 6;

cmd_down_file_req = 7;

cmd_down_file_res = 8;

cmd_data = 9;

type

THead = packed record // 包头

cmd: Byte;

len: Integer;

packNo: Integer;

packQty: Integer;

ver: Byte;

end;

type

TTask = record // 任务

context: TCustomIocpTcpServer.TPerHandleData;

body: TBytes;

cmd: Byte;

ver: Byte;

end;

PTTask = ^TTask;

type

TTaskClient = record

Recved: Boolean;

body: TBytes;

end;

var

g_tmpList: TList; // 临时队列

g_task_client: TTaskClient;

g_iocp_handle: THandle;

function ValidHead(AHead: THead): Boolean;

function GetTask(AContext: TCustomIocpTcpServer.TPerHandleData): PTTask;

procedure ServerProcessRecved(AContext: TCustomIocpTcpServer.TPerHandleData);

procedure ClientProcessRecved(ARecvThread: TCustomBlockingTcpClient.TRecvThread);

procedure ServerSendStream(const AStream: TStream; AContext: TCustomIocpTcpServer.TPerHandleData; cmd, ver: Byte);

function StreamToBytes(aStream: TStream): TBytes;

function BytesToStream(aBytes: TBytes): TStream;

procedure QuerySQL(const aSQL: string; aClient: TBlockingTcpClient);

procedure InitTaskClient;

function GetBodyLen: Integer;

function GetHeadLen: Integer;

function GetPackQty(AStream: TStream): Integer;

function GetLastPackLen(AStream: TStream): Integer;

implementation

var

FCS: TCriticalSection;

function GetLastPackLen(AStream: TStream): Integer;

begin

Result := AStream.Size mod (pack_len - SizeOf(THead));

if Result = 0 then

Result := pack_len

else

Result := Result + SizeOf(THead);

end;

function GetPackQty(AStream: TStream): Integer;

begin

Result := Ceil(AStream.Size / (pack_len - SizeOf(THead)));

end;

function GetHeadLen: Integer;

begin

Result := SizeOf(THead);

end;

function GetBodyLen: Integer;

begin

Result := pack_len - SizeOf(THead);

end;

procedure InitTaskClient;

begin

g_task_client.Recved := False;

SetLength(g_task_client.body, 0);

end;

procedure QuerySQL(const aSQL: string; aClient: TBlockingTcpClient);

var

head: THead;

headLen, bodyLen, packLen: integer;

buf, body: TBytes;

begin

if aSQL = '' then

Exit;

try

head.cmd := cmd_qry_req;

headLen := SizeOf(THead);

bodyLen := Length(aSQL);

packLen := headLen + bodyLen;

head.len := packLen;

head.packNo := 1;

head.packQty := 1;

head.ver := 1;

SetLength(buf, packLen);

Move(head, buf[0], headLen);

body := BytesOf(aSQL);

Move(body[0], buf[headLen], bodyLen);

aClient.SendBuffer(buf[0], packLen); // 发送请求

except

on e: Exception do

begin

Log.WriteLog('QuerySQL ' + e.Message);

end;

end;

end;

function BytesToStream(aBytes: TBytes): TStream;

begin

Result := TMemoryStream.Create;

Result.Write(aBytes[0], length(aBytes));

Result.Position := 0;

end;

function StreamToBytes(aStream: TStream): TBytes;

begin

try

SetLength(Result, aStream.Size);

aStream.Position := 0;

aStream.Read(Result[0], aStream.Size);

finally

if aStream <> nil then

aStream.Free;

end;

end;

function ValidHead(AHead: THead): Boolean;

begin

Result := (AHead.cmd >= 1) and (AHead.len > SizeOf(THead)) and (AHead.packNo >= 1) and (AHead.packQty >= 1);

end;

function GetTask(AContext: TCustomIocpTcpServer.TPerHandleData): PTTask;

var

i: Integer;

begin

Result := nil;

if AContext = nil then

Exit;

FCS.Enter;

try

try

for i := 0 to g_tmpList.Count - 1 do

begin

if g_tmpList.Items[i].context = AContext then

begin

Result := g_tmpList.Items[i];

Exit;

end;

end;

except

on e: Exception do

begin

Log.WriteLog('GetTask ' + e.Message);

end;

end;

finally

FCS.Leave;

end;

end;

procedure ClientProcessRecved(ARecvThread: TCustomBlockingTcpClient.TRecvThread);

var

buf: TBytes;

head: THead;

bodyLen: Integer;

headLen: Integer;

begin

headLen := GetHeadLen;

if ARecvThread.RingBuffer.NoProcessBufLen < headLen then

Exit;

ARecvThread.RingBuffer.Peep(head, headLen); // 取包头

if not ValidHead(head) then // 校验包头

Exit;

try

if head.packQty = 1 then // 一批次只有一个包

begin

if ARecvThread.RingBuffer.NoProcessBufLen < head.len then

Exit;

InitTaskClient; // 初始化

bodyLen := head.len - headLen;

SetLength(g_task_client.body, bodyLen);

ZeroMemory(g_task_client.body, bodyLen);

SetLength(buf, head.len);

ZeroMemory(buf, head.len);

ARecvThread.RingBuffer.Pop(buf[0], head.len);

Move(buf[headLen], g_task_client.body[0], bodyLen);

SetLength(buf, 0);

g_task_client.Recved := True; // 包都收齐了

end

else if head.packQty > 1 then // 一批次有多个包

begin

if head.packNo = 1 then // 首包

begin

if ARecvThread.RingBuffer.NoProcessBufLen < pack_len then

Exit;

InitTaskClient; // 初始化

SetLength(g_task_client.body, head.len - head.packQty * headLen); // 一次分好缓存

ZeroMemory(g_task_client.body, head.len - head.packQty * headLen);

SetLength(buf, pack_len);

ZeroMemory(buf, pack_len);

ARecvThread.RingBuffer.Pop(buf[0], pack_len);

bodyLen := pack_len - headLen;

Move(buf[headLen], g_task_client.body[0], bodyLen);

SetLength(buf, 0);

end

else if head.packNo = head.packQty then // 尾包

begin

if ARecvThread.RingBuffer.NoProcessBufLen < head.len then

exit;

SetLength(buf, head.len);

ZeroMemory(buf, head.len);

ARecvThread.RingBuffer.Pop(buf[0], head.len);

bodyLen := pack_len - headLen;

Move(buf[headLen], g_task_client.body[(head.packNo - 1) * bodyLen], head.len - headLen);

SetLength(buf, 0);

g_task_client.Recved := True;

end

else

begin // 夹在首包和尾包中间的包

if ARecvThread.RingBuffer.NoProcessBufLen < head.len then

Exit;

SetLength(buf, pack_len);

ZeroMemory(buf, pack_len);

ARecvThread.RingBuffer.Pop(buf[0], pack_len);

bodyLen := pack_len - headLen;

Move(buf[headLen], g_task_client.body[(head.packNo - 1) * bodyLen], bodyLen);

SetLength(buf, 0);

end;

end;

except

on e: Exception do

begin

Log.WriteLog('ClientProcessRecved ' + e.Message);

end;

end;

end;

procedure ServerProcessRecved(AContext: TCustomIocpTcpServer.TPerHandleData);

var

pTask: PTTask;

buf: TBytes;

head: THead;

bodyLen: Integer;

headLen: Integer;

begin

if AContext = nil then

Exit;

headLen := GetHeadLen;

if AContext.RingBuffer.NoProcessBufLen < headLen then // 校验

Exit;

AContext.RingBuffer.Peep(head, headLen); // 取包头

if not ValidHead(head) then // 校验包头

Exit;

try

bodyLen := GetBodyLen; // 包体长

if head.packQty = 1 then // 一批次只有一个包

begin

if AContext.RingBuffer.NoProcessBufLen < head.len then // 校验

Exit;

New(pTask); // 新任务

pTask.context := AContext;

pTask.cmd := head.cmd;

pTask.ver := head.ver;

SetLength(pTask.body, head.len - headLen);

SetLength(buf, head.len);

AContext.RingBuffer.Pop(buf[0], head.len); // 包头数据

Move(buf[headLen], pTask.body[0], head.len - headLen); // 包体数据

SetLength(buf, 0);

PostQueuedCompletionStatus(g_iocp_handle, 0, 0, POverlapped(pTask));

end

else if head.packQty > 1 then // 一批次有多个包

begin

if head.packNo = 1 then // 首包

begin

if AContext.RingBuffer.NoProcessBufLen < pack_len then // 校验

Exit; // 新任务

New(pTask);

pTask.context := AContext;

pTask.cmd := head.cmd;

pTask.ver := head.ver;

SetLength(pTask.body, head.len - head.packQty * headLen); // 一次分好缓存

SetLength(buf, pack_len);

AContext.RingBuffer.Pop(buf[0], pack_len);

Move(buf[headLen], pTask.body[0], bodyLen);

SetLength(buf, 0);

FCS.Enter;

g_tmpList.Add(pTask); // 提交临时队列

end

else if head.packNo > 1 then // 非首包

begin

if AContext.RingBuffer.NoProcessBufLen < head.len then

Exit;

pTask := GetTask(AContext);

if pTask = nil then

Exit;

SetLength(buf, head.len);

AContext.RingBuffer.Pop(buf[0], head.len);

Move(buf[headLen], pTask.body[(head.packNo - 1) * bodyLen], bodyLen);

SetLength(buf, 0);

if head.packNo = head.packQty then // 包都收齐了

begin

PostQueuedCompletionStatus(g_iocp_handle, 0, 0, POverlapped(pTask));

FCS.Enter;

try

g_tmpList.Delete(g_tmpList.IndexOf(pTask)); // 从临时队列中删除

finally

FCS.Leave;

end;

end;

end;

end;

except

on e: Exception do

begin

log.WriteLog('ServerProcessRecved ' + e.Message);

end;

end;

end;

procedure ServerSendStream(const AStream: TStream; AContext: TCustomIocpTcpServer.TPerHandleData; cmd, ver: Byte);

var

buf: TBytes;

head: THead;

headLen, bodyLen: integer;

packQty, i, lastPackLen: Integer;

begin

if (AStream = nil) or (AContext = nil) then

Exit;

try

headLen := GetHeadLen;

bodyLen := GetBodyLen;

packQty := GetPackQty(AStream);

lastPackLen := GetLastPackLen(AStream);

if packQty > 1 then

begin

for i := 1 to packQty do // 要分包传输

begin

if i = 1 then // 首包

begin

head.cmd := cmd;

head.len := (packQty - 1) * pack_len + lastPackLen; // 总长

head.packNo := 1;

head.packQty := packQty;

head.ver := ver;

SetLength(buf, pack_len);

ZeroMemory(buf, pack_len);

Move(head, buf[0], headLen);

AStream.Position := 0; // 指针定位

AStream.Read(buf[headLen], bodyLen);

AContext.SendBuffer(buf[0], pack_len);

SetLength(buf, 0);

end

else if i = packQty then // 尾包

begin

head.cmd := cmd;

head.len := lastPackLen;

head.packNo := packQty;

head.packQty := packQty;

head.ver := ver;

SetLength(buf, lastPackLen);

ZeroMemory(buf, lastPackLen);

Move(head, buf[0], headLen);

AStream.Read(buf[headLen], lastPackLen - headLen);

AContext.SendBuffer(buf[0], head.len);

SetLength(buf, 0);

end

else // 夹在包头和包尾中间的包

begin

head.cmd := cmd;

head.len := pack_len;

head.packNo := i;

head.packQty := packQty;

head.ver := ver;

SetLength(buf, pack_len);

ZeroMemory(buf, pack_len);

Move(head, buf[0], headLen);

AStream.Read(buf[headLen], bodyLen);

AContext.SendBuffer(buf[0], head.len);

SetLength(buf, 0);

end;

end;

end

else if packQty = 1 then

begin // 只要传输一包

head.cmd := cmd;

head.len := AStream.Size + headLen;

head.packNo := 1;

head.packQty := 1;

head.ver := ver;

SetLength(buf, head.len);

ZeroMemory(buf, head.len);

Move(head, buf[0], headLen);

AStream.Position := 0;

AStream.Read(buf[headLen], AStream.Size);

AContext.SendBuffer(buf[0], head.len);

SetLength(buf, 0);

end;

except

on e: Exception do

begin

Log.WriteLog('uPackage.ServerSendStream ' + e.Message);

Exit;

end;

end;

end;

initialization

FCS := TCriticalSection.Create;

finalization

FCS.Free;

end.

### 回答1: `pdo_mysql.default_socket` 是 PHP 的配置选项之一,它用于指定 MySQL 数据库服务器的 Unix 套接字文件路径。当使用 PDO 连接 MySQL 数据库时,如果没有手动指定连接参数的 Unix 套接字路径,那么 PDO 将会使用 `pdo_mysql.default_socket` 指定的路径作为默认值。 如果你的 MySQL 数据库服务器安装在默认的位置,并且 PHP 和 MySQL 安装在同一台服务器上,你可以将 `pdo_mysql.default_socket` 设置为 `/var/run/mysqld/mysqld.sock`,这是许多 Linux 系统上 MySQL 默认的 Unix 套接字路径。如果你的 MySQL 数据库服务器安装在不同的位置,你需要根据实际情况修改 `pdo_mysql.default_socket` 的值。 你可以通过修改 `php.ini` 文件来设置 `pdo_mysql.default_socket`。在 `php.ini` 文件中搜索 `pdo_mysql.default_socket`,并将其设置为你需要的 Unix 套接字路径。如果你不知道 Unix 套接字文件的路径,可以通过运行 `mysql_config --socket` 命令来获取。 ### 回答2: php.ini是PHP的配置文件之一,它用于配置PHP的运行参数和选项。而pdo_mysql.default_socket是php.ini中的一个设置,用于指定PDO使用的MySQL套接字文件的路径。 MySQL套接字文件是MySQL数据库连接的一种方式,通过套接字文件,PHP可以与MySQL进行通信和交互。这个设置允许我们指定套接字文件的路径,以便PHP可以正确地连接到MySQL数据库。 在php.ini文件中,当pdo_mysql.default_socket没有设置时,PHP会尝试使用默认的套接字文件路径。这个默认的路径通常是由MySQL服务器的安装位置确定的。然而,如果MySQL服务器的安装位置不同,或者我们希望使用不同的套接字文件,就需要通过修改php.ini文件来指定pdo_mysql.default_socket的值。 例如,如果我们的MySQL服务器安装在/usr/local/mysql目录下,而默认的套接字文件路径是/tmp/mysql.sock,我们可以通过修改php.ini中的pdo_mysql.default_socket参数来指定新的套接字文件路径,如下所示: pdo_mysql.default_socket = /usr/local/mysql/mysql.sock 这样一来,PHP在连接MySQL数据库时就会使用我们指定的套接字文件路径。 总之,pdo_mysql.default_socket是php.ini中用于指定PDO使用的MySQL套接字文件路径的设置。根据实际需要,我们可以通过修改php.ini文件来设置该值,以确保PHP能够正确地连接到MySQL数据库。 ### 回答3: php.ini是PHP的配置文件,用于配置PHP运行环境的各种参数和选项。其中的pdo_mysql.default_socket是一个用于指定PDO连接MySQL数据库所使用的Unix套接字文件路径的选项。 由于PHP在连接MySQL数据库时,默认使用的是MySQL的TCP/IP协议进行通信,所以pdo_mysql.default_socket选项默认为空。这种情况下,PHP通过TCP/IP连接MySQL数据库,在连接字符串中指定MySQL服务器的IP地址和端口号。 如果想要使用Unix套接字文件进行连接,可以通过修改php.ini文件中pdo_mysql.default_socket的值来实现。例如,可以将pdo_mysql.default_socket的值设置为"/tmp/mysql.sock",表示连接MySQL数据库的时候使用套接字文件"/tmp/mysql.sock"。 使用Unix套接字文件连接MySQL数据库相对于TCP/IP连接有一些优势,如更快的速度、更高的安全性和更少的资源占用。因此,在某些情况下,使用Unix套接字文件连接MySQL数据库可能会更加适用。 需要注意的是,修改php.ini文件后,需要重启Web服务器或者PHP-FPM才能使修改生效。此外,还可以在代码中使用ini_set()函数来修改pdo_mysql.default_socket的值,在连接MySQL数据库之前进行动态配置。 综上所述,pdo_mysql.default_socket是用于指定PDO连接MySQL数据库所使用的Unix套接字文件路径的选项,在需要使用Unix套接字文件进行连接时,可以通过修改php.ini文件中的该选项的值来实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值