现在IOCP的功能还剩下服务端数据的返回。
还是采用netty的方式。netty返回数据的调用是这样的contenxt.write(<TObject> obj);这样将obj对象发送给客户端。
1.将回传的对象进行编码成buffer
2.通过socket进行传送.
>>>>>>>>>>>>>>
下面我贴出回传数据的过程.
procedure TClientContext.writeObject(const pvDataObject:TObject); var lvOutBuffer:TBufferLink; begin lvOutBuffer := TBufferLink.Create; try TContextFactory.instance.FEncoder.Encode(pvDataObject, lvOutBuffer); TIOCPTools.SendBuffer(self.FSocket, lvOutBuffer); finally lvOutBuffer.Free; end; end;
在数据处理的时候,我尝试调用这段代码
procedure TClientContext.dataReceived(const pvDataObject:TObject); var lvJsonStream:TJSonStreamObject; lvFile:String; begin lvJsonStream := TJSonStreamObject(pvDataObject); //客户端发送文件 if lvJsonStream.JSon.I['cmdIndex'] = 102 then begin lvFile := ExtractFilePath(ParamStr(0)) + 'tempFile\'; ForceDirectories(lvFile); lvFile := lvFile + lvJsonStream.JSon.S['file']; TMemoryStream(lvJsonStream.Stream).Position := 0; TMemoryStream(lvJsonStream.Stream).SaveToFile(lvFile); end else begin //返回数据 writeObject(lvJsonStream); end; TLogClientWrapper.logINfo(lvJsonStream.JSon.AsJSon(True)); end;
在IOCP的工作线程中如果数据发送完成,回收内存块。
end else if PerIoData.IO_TYPE = IO_TYPE_Send then begin //发送完成数据<WSASend>完成 //回收数据块 TIODataMemPool.instance.giveBackIOData(PerIoData); end;
TIOCPTools.SendBuffer代码
unit IOCPTools; interface uses uBuffer, JwaWinsock2, uMemPool, Windows; const IO_TYPE_Accept = 1; IO_TYPE_Recv = 2; IO_TYPE_Send = 3; //发送数据 type TIOCPTools=class(TObject) public class procedure SendBuffer(pvSocket: TSocket; const ouBuf: TBufferLink); end; implementation class procedure TIOCPTools.SendBuffer(pvSocket: TSocket; const ouBuf: TBufferLink); var lvIOData:LPPER_IO_OPERATION_DATA; lvRet:Cardinal; begin while ouBuf.validCount > 0 do begin lvIOData := TIODataMemPool.instance.borrowIOData; lvIOData.IO_TYPE := IO_TYPE_Send; //这里我改变了内存块的大小,每次发送的长度不能超过设定的内存块大小。但是数据不够的情况下
//Databuf.len是指定了要发送内存块的大小。在回收内存块的时候,需要还原大小。 lvIOData.DataBuf.len := ouBuf.readBuffer(lvIOData.DataBuf.buf, lvIOData.DataBuf.len); if (WSASend(pvSocket, @lvIOData.DataBuf, 1, lvIOData.WorkBytes, lvIOData.WorkFlag, @lvIOData^, nil) = SOCKET_ERROR) then begin lvRet := GetLastError(); //重叠IO,出现ERROR_IO_PENDING是正常的, //表示数据尚未接收完成,如果有数据接收,GetQueuedCompletionStatus会有返回值 if (lvRet <> ERROR_IO_PENDING) then begin closesocket(pvSocket); Break; end; end; end; end; end.
//编码器代码,负责将发送的对象转换成流
unit uJSonStreamEncoder; interface uses uIOCPDecoder, uBuffer, Classes, superobject, SysUtils; type TJSonStreamEncoder = class(TIOCPEncoder) public /// <summary> /// 编码要发送的对象 /// </summary> /// <param name="pvDataObject"> 要进行编码的对象 </param> /// <param name="ouBuf"> 编码好的数据 </param> procedure Encode(pvDataObject:TObject; const ouBuf: TBufferLink); override; end; implementation uses uJSonStreamObject, Windows; procedure TJSonStreamEncoder.Encode(pvDataObject:TObject; const ouBuf: TBufferLink); var lvJSonStreamObject:TJSonStreamObject; lvJSonLength:Integer; lvStreamLength:Integer; sData:String; lvStream:TStream; lvTempBuf:PAnsiChar; begin if pvDataObject = nil then exit; lvJSonStreamObject := TJSonStreamObject(pvDataObject); sData := lvJSonStreamObject.JSon.AsJSon(True); lvJSonLength := Length(sData); lvStream := lvJSonStreamObject.Stream; ouBuf.AddBuffer(@lvJSonLength, SizeOf(lvJSonLength)); if lvStream <> nil then begin lvStreamLength := lvStream.Size; end else begin lvStreamLength := 0; end; ouBuf.AddBuffer(@lvStreamLength, SizeOf(lvStreamLength)); //json bytes ouBuf.AddBuffer(@sData[1], lvJSonLength); if lvStreamLength > 0 then begin //stream bytes GetMem(lvTempBuf, lvStreamLength); try lvStream.Position := 0; lvStream.ReadBuffer(lvTempBuf^, lvStreamLength); ouBuf.AddBuffer(lvTempBuf, lvStreamLength); finally FreeMem(lvTempBuf, lvStreamLength); end; end; end; end.
>>>>>好了关键性的代码我都贴出来了。这次就不提供demo了。如果有需要的请留言
下一次学习的主题是做一个压力测试的demo