I can build a raw IP packet that contains a UDP packet that contains useful data (DNS request).
I can send it and see that it's sent in Wireshark. Wireshark parses it as a legal DNS request, so everything looks smoothly except the DNS answer - I get no answer, nothing.
My code (sorry, it's far from prod-level code):
var
D:WSAData;
SendSocket, ReceiveSocket: TSocket;
bytes: Integer;
bOpt : Integer;
Buf : TPacketBuffer;
SendAddrIn : TSockAddrIn;
RecvAddIn: TSockAddrIn;
sockAddrSize: Integer;
iTotalSize : Word;
begin
try
if WSAStartup($202, D)<>0 then
begin
writeln('error..');
exit;
end;
SendSocket:=socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if SendSocket=INVALID_SOCKET then
writeln(WSAGetLastError);
// Option: Header Include
bOpt := 1;
bytes := SetSockOpt(SendSocket, IPPROTO_IP, IP_HDRINCL, #bOpt, SizeOf(bOpt));
if bytes = SOCKET_ERROR then
begin
Writeln('setsockopt(IP_HDRINCL) failed: '+IntToStr(WSAGetLastError));
exit;
end;
BuildHeaders(SrcIP, SrcPort,
DestIP, DestPort,
dns,
Buf, SendAddrIn, iTotalSize);
Writeln(inttostr(iTotalSize) + ' bytes to send');
bytes := SendTo(SendSocket, buf, iTotalSize, 0, #SendAddrIn, SizeOf(SendAddrIn));
if bytes = SOCKET_ERROR then
writeln('sendto() failed: '+IntToStr(WSAGetLastError))
else
writeln('send '+IntToStr(bytes)+' bytes.');
ReceiveSocket:=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
RecvAddIn.sin_addr.s_addr := htonl(0);
RecvAddIn.sin_family := AF_INET;
RecvAddIn.sin_port := htons(SrcPort);
if bind(ReceiveSocket, TSockAddr(RecvAddIn), sizeof(RecvAddIn)) = SOCKET_ERROR then
begin
writeln('bind() failed: '+IntToStr(WSAGetLastError));
exit;
end;
FillChar(buf, SizeOf(buf), 0);
sockAddrSize := sizeof(RecvAddIn);
bytes := RecvFrom(ReceiveSocket, buf, SizeOf(buf), 0, TSockAddr(RecvAddIn), sockAddrSize);
if bytes = SOCKET_ERROR then
writeln('RecvFrom() failed: '+IntToStr(WSAGetLastError))
else
writeln('RecvFrom '+IntToStr(bytes)+' bytes.');
CloseSocket(SendSocket);
CloseSocket(ReceiveSocket);
WSACleanup;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Wireshark shows this packet as:
I tried to create two sockets with the same local port to send and to receive data, each of its own type. What is wrong?..
UPDATE:
Thank you guys for the ideas.
Indeed, the receiving socket has to be fully initialized before any sending.
But as I've found - the main issue is with UDP packet checksum calculation. I've found out that a simple "ping" tool generates a checksum that doesn't equal the one generated by my code (of course, for the same input values). And when I just used their value (again, all the input values were preserved) - the DNS server returned the response!
To generate the checksum I use the next code:
function CheckSum(var Buffer; Size : integer) : Word;
type
TWordArray = array[0..1] of Word;
var
ChkSum : LongWord;
i : Integer;
begin
ChkSum := 0;
i := 0;
while Size > 1 do
begin
ChkSum := ChkSum + TWordArray(Buffer)[i];
inc(i);
Size := Size - SizeOf(Word);
end;
if Size=1 then
ChkSum := ChkSum + Byte(TWordArray(Buffer)[i]);
ChkSum := (ChkSum shr 16) + (ChkSum and $FFFF);
ChkSum := ChkSum + (Chksum shr 16);
Result := Word(ChkSum);
end;
procedure BuildHeaders(FromIP : string; iFromPort : Word;
ToIP : string; iToPort : Word;
StrMessage : TBytes; var Buf: TPacketBuffer;
var remote : TSockAddrIn; var iTotalSize: Word);
var
dwFromIP : LongWord;
dwToIP : LongWord;
iIPVersion : Word;
iIPSize : Word;
ipHdr : T_IP_Header;
udpHdr : T_UDP_Header;
iUdpSize : Word;
iUdpChecksumSize : Word;
cksum : Word;
Ptr : ^Byte;
procedure IncPtr(Value : Integer);
begin
ptr := pointer(integer(ptr) + Value);
end;
begin
dwFromIP := inet_Addr(PAnsiChar(AnsiString(FromIP)));
dwToIP := inet_Addr(PAnsiChar(AnsiString(ToIP)));
iTotalSize := sizeof(ipHdr) + sizeof(udpHdr) + length(strMessage);
iIPVersion := 4;
iIPSize := sizeof(ipHdr) div sizeof(LongWord);
//
// IP version goes in the high order 4 bits of ip_verlen. The
// IP header length (in 32-bit words) goes in the lower 4 bits.
//
ipHdr.ip_verlen := (iIPVersion shl 4) or iIPSize;
ipHdr.ip_tos := 0; // IP type of service
ipHdr.ip_totallength := htons(iTotalSize); // Total packet len
ipHdr.ip_id := $1545; // Unique identifier: set to 0
ipHdr.ip_offset := 0; // Fragment offset field
ipHdr.ip_ttl := 128;
ipHdr.ip_protocol := $11; // Protocol(UDP)
ipHdr.ip_checksum := 0 ; // IP checksum
ipHdr.ip_srcaddr := dwFromIP; // Source address
ipHdr.ip_destaddr := dwToIP; // Destination address
iUdpSize := sizeof(udpHdr) + length(strMessage);
udpHdr.src_portno := htons(iFromPort) ;
udpHdr.dst_portno := htons(iToPort) ;
udpHdr.udp_length := htons(iUdpSize) ;
udpHdr.udp_checksum := 0 ;
//
// Build the UDP pseudo-header for calculating the UDP checksum.
// The pseudo-header consists of the 32-bit source IP address,
// the 32-bit destination IP address, a zero byte, the 8-bit
// IP protocol field, the 16-bit UDP length, and the UDP
// header itself along with its data (padded with a 0 if
// the data is odd length).
//
iUdpChecksumSize := 0;
ptr := #buf[0];
FillChar(Buf, SizeOf(Buf), 0);
Move(ipHdr.ip_srcaddr, ptr^, SizeOf(ipHdr.ip_srcaddr));
IncPtr(SizeOf(ipHdr.ip_srcaddr));
iUdpChecksumSize := iUdpChecksumSize + sizeof(ipHdr.ip_srcaddr);
Move(ipHdr.ip_destaddr, ptr^, SizeOf(ipHdr.ip_destaddr));
IncPtr(SizeOf(ipHdr.ip_destaddr));
iUdpChecksumSize := iUdpChecksumSize + sizeof(ipHdr.ip_destaddr);
IncPtr(1);
Inc(iUdpChecksumSize);
Move(ipHdr.ip_protocol, ptr^, sizeof(ipHdr.ip_protocol));
IncPtr(sizeof(ipHdr.ip_protocol));
iUdpChecksumSize := iUdpChecksumSize + sizeof(ipHdr.ip_protocol);
Move(udpHdr.udp_length, ptr^, sizeof(udpHdr.udp_length));
IncPtr(sizeof(udpHdr.udp_length));
iUdpChecksumSize := iUdpChecksumSize + sizeof(udpHdr.udp_length);
move(udpHdr, ptr^, sizeof(udpHdr));
IncPtr(sizeof(udpHdr));
iUdpChecksumSize := iUdpCheckSumSize + sizeof(udpHdr);
Move(StrMessage[1], ptr^, Length(strMessage));
IncPtr(Length(StrMessage));
iUdpChecksumSize := iUdpChecksumSize + length(strMessage);
cksum := checksum(buf, iUdpChecksumSize);
udpHdr.udp_checksum := $FA8B;//cksum;
//
// Now assemble the IP and UDP headers along with the data
// so we can send it
//
FillChar(Buf, SizeOf(Buf), 0);
Ptr := #Buf[0];
Move(ipHdr, ptr^, SizeOf(ipHdr)); IncPtr(SizeOf(ipHdr));
Move(udpHdr, ptr^, SizeOf(udpHdr)); IncPtr(SizeOf(udpHdr));
Move(StrMessage[0], ptr^, length(StrMessage));
remote.sin_family := AF_INET;
remote.sin_port := htons(iToPort);
remote.sin_addr.s_addr := dwToIP;
end;
If anyone has another well-working implementation, please share...
You are creating separate sockets to send and receive the DNS packets, but you are creating the receiving socket after sending the request. It is possible/likely that the response arrives before the receiving socket is ready (use Wireshark to confirm that), in which case the response will simply be discarded by the OS.
You need to fully prepare the receiving socket before you send the request.
Ok, I've found the bug in the checksum calculation.
The next edition works fine and generates the correct checksum:
function CheckSum(var Buffer; Size : integer) : Word;
type
TWordArray = array[0..1] of Word;
var
ChkSum : LongWord;
i : Integer;
Item: Word;
begin
ChkSum := 0;
i := 0;
while Size > 1 do
begin
Item := TWordArray(Buffer)[i];
Item := Swap(Item);
ChkSum := ChkSum + Item;
inc(i);
Size := Size - SizeOf(Word);
end;
if Size=1 then
ChkSum := ChkSum + Byte(TWordArray(Buffer)[i]);
ChkSum := (ChkSum shr 16) + (ChkSum and $FFFF);
ChkSum := not ChkSum;
// ChkSum := ChkSum + (Chksum shr 16);
Result := Word(ChkSum);
end;
If you see any issues with it, please share your thoughts.
Related
I'm trying to send a Wake-On-Lan packet to a PC in my Office using an application on my Mobile device at home.
This code works for me when I'm connected to the Office's network:
procedure WakeOnLan(const AMacAddress: string);
type
TMacAddress = array [1..6] of Byte;
TWakeRecord = packed record
Waker : TMACAddress;
MAC : array [0..15] of TMacAddress;
end;
var
I : Integer;
WR : TWakeRecord;
MacAddress : TMacAddress;
UDPClient : TIdUDPClient;
sData : string;
begin
FillChar(MacAddress, SizeOf(TMacAddress), 0);
sData := Trim(AMacAddress);
if Length(sData) = 17 then begin
for I := 1 to 6 do begin
MacAddress[I] := StrToIntDef('$' + Copy(sData, 1, 2), 0);
sData := Copy(sData, 4, 17);
end;
end;
for I := 1 to 6 do WR.Waker[I] := $FF;
for I := 0 to 15 do WR.MAC[I] := MacAddress;
UDPClient := TIdUDPClient.Create(nil);
try
UDPClient.Host := '255.255.255.255';
UDPClient.Port := 32767;
UDPClient.BroadCastEnabled := True;
UDPClient.Broadcast(RawToBytes(WR, SizeOf(TWakeRecord)), 7);
UDPClient.SendBuffer(RawToBytes(WR, SizeOf(TWakeRecord)));
UDPClient.BroadcastEnabled := False;
finally
UDPClient.Free;
end;
end;
But, if I'm connected to my Home network, the code does not work.
How do I connect to the Office Modem/PC before sending the Magic Packets?
Do I need to use TSocket instead of TIdUDPClient?
In my project I receive data from a tcp connection with a custom protocol in packets of 1095 bytes, then I must look for a sync word and try to show gray scale image.
At first step I read data and save them in a TStringList fifo
procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
rowFrame : string;
data: TIdBytes;
begin
offReCStatus := false;
repeat
AContext.Connection.IOHandler.ReadBytes(data, 1099, False);
rowFrame :='';
for I := 0 to length(data)-1 do
begin
rowFrame := rowFrame + (data[i].ToHexString);
end;
tcpFrameList.Append( rowFrame );
until offReCStatus = true;
end;
Then in a separated thread, I try the data from the list.
{I added some comments in code}
Get first string from string list
Convert it to binary and append to previous data
Find sync word and copy data after sync word
Split image data to 1024 * 10 bits to load image
Draw image from data
Find new sync word(number 3)
Note: one very important thing is the sync-word is not byte,its bits and can start from middle of a byte for example 10 101011-00010101-00001100-10011001-01111111-00 111111 in this case 10 at first and 111111 at the end merged to sync word and its not AC543265FC any more.in the past in fpga I wrote code that shift the bits until find the 40 bits sync word but i don't know how this can be done in Delphi!
procedure TMyThread.Execute;
var
str3,str4,frameStr,frameId,strData, str6 : string;
iPos,y ,imageBit , frameIdNum :integer;
imageRol : TStringList;
begin
while not Terminated do
begin
FTermEvent.WaitFor( 500 );
if not Terminated then
begin
while tcpFrameList.Count >0 do //process que
begin
try
dta := dta + HexStrToBinStr(tcpFrameList[0]);//convert hex data to binary string and append to olddata
tcpFrameList.Delete(0);//delete converted thread
str3 := '1010110001010100001100100110010111111100';//sync word "AC543265FC"
iPos := pos( str3 , dta );//find 1st sync word in binary data
while dta.Length>20000 do //process data to find sync words
begin
Delete(dta,1, iPos-1 );//delete data until first sync word
str4 := copy( dta , 1, 12240);//copy image frame data after sync word
Delete(dta,1, 12240 );//delete image frame data that copied
strData := copy(BinToHex(str4),11); //hex image data
frameId := copy( strData , 1, 6 ); //get image column id from data
frameStr := copy( strData , 107, 330 );//get image color data as protocol
frameStr := frameStr + copy( strData , 501, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 1011, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 1521, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 2031, 446 );//get image data as in protocol
frameStr := frameStr + copy( strData , 2541, 446 );//get image data as in protocol
imageBin := HexStrToBinStr( frameStr );
//now we have 10240 bit that for one frame column .10240 is 1024 of 10 bits for each pixel
imageRol := TstringList.Create;
imageRol := spliToLength( imageBin,10);//split 10240 to 1024 *10
frameIdNum := HexToDec(frameId);//frame id to show image
//application.ProcessMessages;
TThread.Synchronize (TThread.CurrentThread,
procedure () var y,n:integer;
begin
form1.Image1.Width := frameIdNum+1;//set TImage width
for y := 0 to imageRol.Count-1 do //process imageRol to grab 1024 pixel color of new column
begin
str6 := imageRol[y];
imageBit := trunc( BinToDec( str6 ) /4 );//div 10bit(1024) to 4 to get a number 0-255 for color
form1.Image1.Canvas.Pixels[frameIdNum ,y)] := RGB( imageBit , imageBit , imageBit );//gray scale image
end;
end);
iPos := pos( str3 , dta );
end;
except
on E : Exception do
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
form1.Memo1.Lines.Add(E.ClassName+' , message: '+E.Message);
end);
end;
end;
end;
end;
end;
The code above is working good but its slow..
I don't know how can process data as bits so try to convert data between hex and string to complete the process. Is there a way to do this job without any hex converting from tcp layer!?
I commented the code to explain what happening.but tell me to add some more data where necessary.
Here is an example how you could process the Binary data.
DISCLAMER
This code sample is far from optimized as I tried to keep it simple so one can grasp the concept how to process binary data.
The main concept here is that we have a 40 bit sync word (marker) but since we are dealing with individual bits, it can be on a non byte boundary. So all we need to do is read at least 48 bits (6 bytes) into a 64 bit integer and shift the bits to the right until we find our marker. I did not include the RGB pixel extraction logic, I leave that as an exercise for you :), I think you can decode it with WIC as GUID_WICPixelFormat32bppBGR101010
program SO59584303;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Classes,
System.SysUtils;
type ImageArray = TArray<Byte>;
const FrameSync : UInt64 = $AC543265FC; // we need Int64 as our marker is > 32 bits
function GetByte(const Value : UInt64; const ByteNum : Byte) : Byte; inline;
begin
Result := (Value shr ((ByteNum-1)*8)) and $FF ;
end;
procedure WriteInt64BigEndian(const Value: UInt64; NumberOfBytes : Integer; var Stream : TBytes; var Ps : Integer);
var
I : Integer;
begin
for I := NumberOfBytes downto 1 do
begin
Stream[Ps] := GetByte(Value, I);
Inc(Ps);
end;
end;
function ReadInt64BigEndian(const NumberOfBytes : Integer; const Stream : TBytes; var Ps : Integer) : UInt64;
var
I : Integer;
B : Byte;
begin
Result := 0;
for I := NumberOfBytes downto 1 do
begin
B := Stream[Ps];
Result := Result or (UInt64(B) shl ((I-1)* 8));
Inc(Ps);
// sanity check
if Ps >= Length(Stream) then
Exit;
end;
end;
procedure ReadPixelData(const Stream : TBytes; Var Ps : Integer; const Shift : Byte; var Buffer : ImageArray);
// our buffer
var
I : UInt64;
BPos : Integer;
begin
BPos := 0;
// 1024 * 10 bit pixel = 10240 bits = 1280 bytes // initialize buffer
SetLength(Buffer, 1280);
// fill with 0's
FillChar(Buffer[0], Length(Buffer), 0);
if Shift = 0 then
begin
// if we are byte boundary, we can just copy our data
Move(Stream[Ps], Buffer[0], Length(Buffer));
Inc(Ps, Length(Buffer));
end
else
while Bpos < Length(Buffer) do
begin
// Read 8 bytes at a time and shift x bits to the right, mask off highest byte
// this means we can get max 7 bytes at a time
I := (ReadInt64BigEndian(8, Stream, Ps) shr Shift) and $00FFFFFFFFFFFFFF;
// Write 7 bytes to our image data buffer
WriteInt64BigEndian(I, 7, Buffer, BPos);
// go one position back for the next msb bits
Dec(Ps);
end;
end;
procedure WritePixelData(var Stream : TBytes; Var Ps : Integer; var Shift : Byte);
var
Count : Integer;
ByteNum : Byte;
Data : UInt64;
begin
for Count := 1 to 160 do
begin
// write four bytes at a time, due to the shifting we get 5 bytes in total
Data := $F1F2F3F4;
if (Shift > 0) then
begin
// special case, we need to fillup shift bits on last written byte in the buffer with highest byte from our UInt64
Data := Data shl Shift;
Stream[Ps-1] := Stream[Ps-1] or GetByte(Data, 5);
end;
WriteInt64BigEndian(Data, 4, Stream, Ps);
Data := $F5F6F7F8;
if (Shift > 0) then
begin
// special case, we need to fillup shift bits on last written byte in the buffer with highest byte from our UInt64
Data := Data shl Shift;
Stream[Ps-1] := Stream[Ps-1] or GetByte(Data, 5);
end;
WriteInt64BigEndian(Data, 4, Stream, Ps);
end;
end;
procedure GenerateData(var Stream : TBytes);
var
Count : Integer;
I : UInt64;
Ps : Integer;
Shift : Byte;
begin
Count := 1285*4+10;
SetLength(Stream, Count); // make room for 4 Imageframes (1280 bytes or 10240 bits) and 5 byte marker (40 bits) + 10 bytes extra room
FillChar(Stream[0], Count, 0);
Ps := 1;
// first write some garbage
Stream[0] := $AF;
// our first marker will be shifted 3 bits to the left
Shift := 3;
I := FrameSync shl Shift;
// write our Framesync (40+ bits = 6 bytes)
WriteInt64BigEndian(I, 6, Stream, Ps);
// add our data, 1280 bytes or 160 times 8 bytes, we use $F1 F2 F3 F4 F5 F6 F7 F8 as sequence
// (fits in Int 64) so that we can verify our decoding stage later on
WritePixelData(Stream, Ps, Shift);
// write some garbage
Stream[Ps] := $AE;
Inc(Ps);
// our second marker will be shifted 2 bits to the left
Shift := 2;
I := FrameSync shl Shift;
WriteInt64BigEndian(I, 6, Stream, Ps);
WritePixelData(Stream, Ps, Shift);
// write some garbage
Stream[Ps] := $AD;
Inc(Ps);
// our third marker will be shifted 1 bit to the left
Shift := 1;
I := FrameSync shl Shift;
WriteInt64BigEndian(I, 6, Stream, Ps);
WritePixelData(Stream, Ps, Shift);
// write some garbage
Stream[Ps] := $AC;
Inc(Ps);
// our third marker will be shifted 5 bits to the left
Shift := 5;
I := FrameSync shl Shift;
WriteInt64BigEndian(I, 6, Stream, Ps);
WritePixelData(Stream, Ps, Shift);
SetLength(Stream, Ps-1)
end;
procedure DecodeData(const Stream : TBytes);
var
Ps : Integer;
OrgPs : Integer;
BPos : Integer;
I : UInt64;
Check : UInt64;
Shift : Byte;
ByteNum : Byte;
ImageData : ImageArray;
begin
Ps := 0;
Shift := 0;
while Ps < Length(Stream) do
begin
// try to find a marker
// determine the number of bytes we need to read, 40bits = 5 bytes,
// when we have shifted bits this will require 6 bytes
if Shift = 0 then
ByteNum := 5
else
ByteNum := 6;
// save initial position in the stream
OrgPs := Ps;
// read our marker
I := ReadInt64BigEndian(ByteNum, Stream, Ps);
// if we have shifted bits, shift them on byte boundary and make sure we only have the 40 lower bits
if Shift > 0 then
I := (I shr Shift) and $FFFFFFFFFF;
if I = FrameSync then
begin
// we found our marker, process pixel data (ie read next 10240 bits, taking shift into account)
// If we have shift, our first bits will be found in the last marker byte, so go back one position in the stream
if Shift > 0 then
Dec(Ps);
ReadPixelData(Stream, Ps, Shift, ImageData);
// process Image array accordingly, here we will just check that we have our written data back
BPos := 0;
Check := $F1F2F3F4F5F6F7F8;
for ByteNum := 1 to 160 do
begin
I := ReadInt64BigEndian(8, ImageData, BPos);
// if our data is not correct, raise error
Assert(I = Check, 'error decoding image data');
end;
end
else
begin
Ps := OrgPs;
// we did not find our marker, advance 1 bit
Inc(Shift);
if Shift > 7 then
begin
// reset shift value
Shift := 0;
// advance to next byte boundary
Inc(Ps);
end;
end;
end;
end;
Var
AStream : TBytes;
begin
try
GenerateData(AStream);
DecodeData(AStream);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Modern Warefare 2 (MWF2): is a Video game.
RCON TOOL: is a tool used to send commands to Game servers using UDP.
i am trying to send a command using indy to a server, its easy to send some string using idUDPclient but my Problem is that
i supposed to send it in this format:
ÿÿÿÿrcon "1234" kick cheater101
where :-
password quoted: "1234"
and command is : kick cheater101
as byte like this
FFFFFFFF72636F6E2020223132333422206B69636B2063686561746572313031
notice that anything sent must start with FFFFFFFF
and that how it should look in Wireshark..
the problem is i couldn't send it like the above .. i just sent like a string ..
i need to make it with indyUDP because I'm planning to test it on android.
here is my code for i am trying:
function rcon(const IP: String; Port: TIdPort; const Pass, Command: String): String;
var
Query: TIdBytes;
Buffer, Data:
TIdBytes;
Len: Integer;
begin
SetLength(Query, 4);
Query[0] := $FF;
Query[1] := $FF;
Query[2] := $FF;
Query[3] := $FF;
AppendString(Query, 'rcon "' + Pass + '" ' + Command);
SetLength(Data, 0);
with TIdUDPClient.Create do try ReceiveTimeout := 2000;
SendBuffer(IP, Port, Query);
repeat SetLength(Buffer, 10000);
Len := ReceiveBuffer(Buffer);
if Len < 1 then Break;
SetLength(Buffer, Len);
AppendBytes(Data, Buffer);
until False; finally Free;
end; // preprocess Data as needed... Result := BytesToString(Data);
end
usage!
rcon('10.0.0.4', 28961, '1234', 'kick cheater101');
Wireshark:
You are encountering a DISPLAY issue, not a DATA issue. In your Wireshark screenshot, you are looking at the raw bytes as-is, not at the hex formatted representation of those same bytes. Wireshark can show you both.
The actual bytes you are sending are fine - except that you are missing a 2nd space character between rcon and the quoted password.
function rcon(const IP: String; Port: TIdPort; const Pass, Command: String): String;
var
Query: TIdBytes;
Buffer, Data: TIdBytes;
Len: Integer;
begin
SetLength(Query, 4);
FillBytes(Query, 4, $FF);
AppendString(Query, 'rcon "' + Pass + '" ' + Command);
SetLength(Data, 0);
with TIdUDPClient.Create do
try
SendBuffer(IP, Port, Query);
ReceiveTimeout := 2000;
SetLength(Buffer, 10000);
repeat
Len := ReceiveBuffer(Buffer);
if Len < 1 then Break;
AppendBytes(Data, Buffer, 0, Len);
until False;
finally
Free;
end;
// preprocess Data as needed...
Result := BytesToString(Data);
end;
Also, in your Wireshark screenshot, it clearly shows 70 bytes being sent for each command, where the command itself is 32 bytes and the rest is 38 bytes of padding. If the server is expecting that padding, you need to add it to your outgoing Query:
function rcon(const IP: String; Port: TIdPort; const Pass, Command: String): String;
var
Query: TIdBytes;
Buffer, Data: TIdBytes;
Len: Integer;
begin
SetLength(Query, 4);
FillBytes(Query, 4, $FF);
AppendString(Query, 'rcon "' + Pass + '" ' + Command);
if (Length(Query) < 70) then
ExpandBytes(Query, Length(Query), 70-Length(Query));
SetLength(Data, 0);
with TIdUDPClient.Create do
try
SendBuffer(IP, Port, Query);
ReceiveTimeout := 2000;
SetLength(Buffer, 10000);
repeat
Len := ReceiveBuffer(Buffer);
if Len < 1 then Break;
AppendBytes(Data, Buffer, 0, Len);
until False;
finally
Free;
end;
// preprocess Data as needed...
Result := BytesToString(Data);
end;
this works, well it will return the proper byte array (as an AnsiString, anyways)!. I used format to combine the inputs, and prepended the FFFFFFFFs
Function MakeKickCommand(pass, user: LPCSTR): AnsiString; //is wide strings okay
var
Temp : AnsiString;
len, I: integer;
a: integer;
Begin
Temp:= AnsiString( format('rcon "%s" kick %s', [pass, user]) );
len := length(Temp);
//set length of the result
SetLength(Result, 4+len);
//add FF FF FF FF
FillChar (Result[1], 4, $FF);
//copy the formatted string
Move(Temp[1], Result[5], len);
End;
While I can successfully encode and decode the user data part of an SMS message when a UDH is not present, I'm having trouble doing so when a UDH is present (in this case, for concatenated SMS).
When I decode or encode the user data, do I need to prepend the UDH to the text before doing so?
This article provides an encoding routine sample that compensates for the UDH with padding bits (which I still don't completely understand) but it doesn't give an example of data being passed to the routine so I don't have a clear use case (and I could not find a decoding sample on the site):
http://mobiletidings.com/2009/07/06/how-to-pack-gsm7-into-septets/.
So far, I have been able to get some results if I prepend the UDH to the user data before decoding it, but I suspect this is just a coincidence.
As an example (using values from https://en.wikipedia.org/wiki/Concatenated_SMS):
UDH := '050003000302';
ENCODED_USER_DATA_PART := 'D06536FB0DBABFE56C32'; // with padding, evidently
DecodedUserData := Decode7Bit(UDH + ENCODED_USER_DATA_PART);
Writeln(DecodedUserData);
Output: "ß#ø¿Æ #hello world"
EncodedUserData := Encode7Bit(DecodedUserData);
DecodedUserData := Decode7Bit(EncodedEncodedUserData);
Writeln(DecodedUserData);
Same Output: "ß#ø¿Æ #hello world"
Without prepending the UDH I get garbage:
DecodedUserData := Decode7Bit(ENCODED_USER_DATA_PART);
Writeln(DecodedUserData);
Output: "PKYY§An§eYI"
What is correct way of handling this?
Am I supposed to include the UDH with the text when encoding the user data?
Am I supposed to strip off the garbage characters after decoding, or am I (as I suspect) completely off base with this assumption?
While the decoding algorithm here seems to work without a UDH it doesn't seem to take any UDH information into account:
Looking for GSM 7bit encode/decode algorithm.
I would be eternally grateful if someone could set me straight on the correct way to proceed. Any clear examples/code samples would be very much appreciated. ;-)
I will also provide a small sample application that includes the algorithms if anyone feels it will help solve the riddle.
EDIT 1:
I'm using Delphi XE2 Update 4 Hotfix 1
EDIT 2:
Thanks to help from #whosrdaddy, I was able to successfully get my encoding/decoding routines to work.
As a side note, I was curious as to why the user data needed to be on a 7-bit boundary when the UDH wasn't encoded with it, but the last sentence in the paragraph from the ETSI specification quoted by #whosrdaddy answered that:
If 7 bit data is used and the TP-UD-Header does not finish on a septet boundary then fill bits are inserted after the last
Information Element Data octet so that there is an integral number of
septets for the entire TP-UD header. This is to ensure that the SM
itself starts on an octet boundary so that an earlier phase mobile
will be capable of displaying the SM itself although the TP-UD Header
in the TP-UD field may not be understood
My code is based in part on examples from the following resources:
Looking for GSM 7bit encode/decode algorithm
https://en.wikipedia.org/wiki/Concatenated_SMS
http://mobiletidings.com/2009/02/18/combining-sms-messages/
http://mobiletidings.com/2009/07/06/how-to-pack-gsm7-into-septets/
http://mobileforensics.files.wordpress.com/2007/06/understanding_sms.pdf
http://www.dreamfabric.com/sms/
http://www.mediaburst.co.uk/blog/concatenated-sms/
Here's the code for anyone else who's had trouble with SMS encoding/decoding. I'm sure it can be simplified/optimized (and comments are welcome), but I've tested it with several different permutations and UDH header lengths with success. I hope it helps.
unit SmsUtils;
interface
uses Windows, Classes, Math;
function Encode7Bit(const AText: string; AUdhLen: Byte;
out ATextLen: Byte): string;
function Decode7Bit(const APduData: string; AUdhLen: Integer): string;
implementation
var
g7BitToAsciiTable: array [0 .. 127] of Byte;
gAsciiTo7BitTable: array [0 .. 255] of Byte;
procedure InitializeTables;
var
AsciiValue: Integer;
i: Integer;
begin
// create 7-bit to ascii table
g7BitToAsciiTable[0] := 64; // #
g7BitToAsciiTable[1] := 163;
g7BitToAsciiTable[2] := 36;
g7BitToAsciiTable[3] := 165;
g7BitToAsciiTable[4] := 232;
g7BitToAsciiTable[5] := 223;
g7BitToAsciiTable[6] := 249;
g7BitToAsciiTable[7] := 236;
g7BitToAsciiTable[8] := 242;
g7BitToAsciiTable[9] := 199;
g7BitToAsciiTable[10] := 10;
g7BitToAsciiTable[11] := 216;
g7BitToAsciiTable[12] := 248;
g7BitToAsciiTable[13] := 13;
g7BitToAsciiTable[14] := 197;
g7BitToAsciiTable[15] := 229;
g7BitToAsciiTable[16] := 0;
g7BitToAsciiTable[17] := 95;
g7BitToAsciiTable[18] := 0;
g7BitToAsciiTable[19] := 0;
g7BitToAsciiTable[20] := 0;
g7BitToAsciiTable[21] := 0;
g7BitToAsciiTable[22] := 0;
g7BitToAsciiTable[23] := 0;
g7BitToAsciiTable[24] := 0;
g7BitToAsciiTable[25] := 0;
g7BitToAsciiTable[26] := 0;
g7BitToAsciiTable[27] := 0;
g7BitToAsciiTable[28] := 198;
g7BitToAsciiTable[29] := 230;
g7BitToAsciiTable[30] := 223;
g7BitToAsciiTable[31] := 201;
g7BitToAsciiTable[32] := 32;
g7BitToAsciiTable[33] := 33;
g7BitToAsciiTable[34] := 34;
g7BitToAsciiTable[35] := 35;
g7BitToAsciiTable[36] := 164;
g7BitToAsciiTable[37] := 37;
g7BitToAsciiTable[38] := 38;
g7BitToAsciiTable[39] := 39;
g7BitToAsciiTable[40] := 40;
g7BitToAsciiTable[41] := 41;
g7BitToAsciiTable[42] := 42;
g7BitToAsciiTable[43] := 43;
g7BitToAsciiTable[44] := 44;
g7BitToAsciiTable[45] := 45;
g7BitToAsciiTable[46] := 46;
g7BitToAsciiTable[47] := 47;
g7BitToAsciiTable[48] := 48;
g7BitToAsciiTable[49] := 49;
g7BitToAsciiTable[50] := 50;
g7BitToAsciiTable[51] := 51;
g7BitToAsciiTable[52] := 52;
g7BitToAsciiTable[53] := 53;
g7BitToAsciiTable[54] := 54;
g7BitToAsciiTable[55] := 55;
g7BitToAsciiTable[56] := 56;
g7BitToAsciiTable[57] := 57;
g7BitToAsciiTable[58] := 58;
g7BitToAsciiTable[59] := 59;
g7BitToAsciiTable[60] := 60;
g7BitToAsciiTable[61] := 61;
g7BitToAsciiTable[62] := 62;
g7BitToAsciiTable[63] := 63;
g7BitToAsciiTable[64] := 161;
g7BitToAsciiTable[65] := 65;
g7BitToAsciiTable[66] := 66;
g7BitToAsciiTable[67] := 67;
g7BitToAsciiTable[68] := 68;
g7BitToAsciiTable[69] := 69;
g7BitToAsciiTable[70] := 70;
g7BitToAsciiTable[71] := 71;
g7BitToAsciiTable[72] := 72;
g7BitToAsciiTable[73] := 73;
g7BitToAsciiTable[74] := 74;
g7BitToAsciiTable[75] := 75;
g7BitToAsciiTable[76] := 76;
g7BitToAsciiTable[77] := 77;
g7BitToAsciiTable[78] := 78;
g7BitToAsciiTable[79] := 79;
g7BitToAsciiTable[80] := 80;
g7BitToAsciiTable[81] := 81;
g7BitToAsciiTable[82] := 82;
g7BitToAsciiTable[83] := 83;
g7BitToAsciiTable[84] := 84;
g7BitToAsciiTable[85] := 85;
g7BitToAsciiTable[86] := 86;
g7BitToAsciiTable[87] := 87;
g7BitToAsciiTable[88] := 88;
g7BitToAsciiTable[89] := 89;
g7BitToAsciiTable[90] := 90;
g7BitToAsciiTable[91] := 196;
g7BitToAsciiTable[92] := 204;
g7BitToAsciiTable[93] := 209;
g7BitToAsciiTable[94] := 220;
g7BitToAsciiTable[95] := 167;
g7BitToAsciiTable[96] := 191;
g7BitToAsciiTable[97] := 97;
g7BitToAsciiTable[98] := 98;
g7BitToAsciiTable[99] := 99;
g7BitToAsciiTable[100] := 100;
g7BitToAsciiTable[101] := 101;
g7BitToAsciiTable[102] := 102;
g7BitToAsciiTable[103] := 103;
g7BitToAsciiTable[104] := 104;
g7BitToAsciiTable[105] := 105;
g7BitToAsciiTable[106] := 106;
g7BitToAsciiTable[107] := 107;
g7BitToAsciiTable[108] := 108;
g7BitToAsciiTable[109] := 109;
g7BitToAsciiTable[110] := 110;
g7BitToAsciiTable[111] := 111;
g7BitToAsciiTable[112] := 112;
g7BitToAsciiTable[113] := 113;
g7BitToAsciiTable[114] := 114;
g7BitToAsciiTable[115] := 115;
g7BitToAsciiTable[116] := 116;
g7BitToAsciiTable[117] := 117;
g7BitToAsciiTable[118] := 118;
g7BitToAsciiTable[119] := 119;
g7BitToAsciiTable[120] := 120;
g7BitToAsciiTable[121] := 121;
g7BitToAsciiTable[122] := 122;
g7BitToAsciiTable[123] := 228;
g7BitToAsciiTable[124] := 246;
g7BitToAsciiTable[125] := 241;
g7BitToAsciiTable[126] := 252;
g7BitToAsciiTable[127] := 224;
// create ascii to 7-bit table
ZeroMemory(#gAsciiTo7BitTable, SizeOf(gAsciiTo7BitTable));
for i := 0 to High(g7BitToAsciiTable) do
begin
AsciiValue := g7BitToAsciiTable[i];
gAsciiTo7BitTable[AsciiValue] := i;
end;
end;
function ConvertAsciiTo7Bit(const AText: string; AUdhLen: Byte): AnsiString;
const
ESC = #27;
ESCAPED_ASCII_CODES = [#94, #123, #125, #92, #91, #126, #93, #124, #164];
var
Septet: Byte;
Ch: AnsiChar;
i: Integer;
begin
for i := 1 to Length(AText) do
begin
Ch := AnsiChar(AText[i]);
if not(Ch in ESCAPED_ASCII_CODES) then
Septet := gAsciiTo7BitTable[Byte(Ch)]
else
begin
Result := Result + ESC;
case (Ch) of
#12: Septet := 10;
#94: Septet := 20;
#123: Septet := 40;
#125: Septet := 41;
#92: Septet := 47;
#91: Septet := 60;
#126: Septet := 61;
#93: Septet := 62;
#124: Septet := 64;
#164: Septet := 101;
else Septet := 0;
end;
end;
Result := Result + AnsiChar(Septet);
end;
end;
function Convert7BitToAscii(const AText: AnsiString): string;
const
ESC = #27;
var
TextLen: Integer;
Ch: Char;
i: Integer;
begin
Result := '';
TextLen := Length(AText);
i := 1;
while (i <= TextLen) do
begin
Ch := Char(AText[i]);
if (Ch <> ESC) then
Result := Result + Char(g7BitToAsciiTable[Ord(Ch)])
else
begin
Inc(i); // skip ESC
if (i <= TextLen) then
begin
Ch := Char(AText[i]);
case (Ch) of
#10: Ch := #12;
#20: Ch := #94;
#40: Ch := #123;
#41: Ch := #125;
#47: Ch := #92;
#60: Ch := #91;
#61: Ch := #126;
#62: Ch := #93;
#64: Ch := #124;
#101: Ch := #164;
end;
Result := Result + Ch;
end;
end;
Inc(i);
end;
end;
function StrToHex(const AText: AnsiString): AnsiString; overload;
var
TextLen: Integer;
begin
// set the text buffer size
TextLen := Length(AText);
// set the length of the result to double the string length
SetLength(Result, TextLen * 2);
// convert the string to hex
BinToHex(PAnsiChar(AText), PAnsiChar(Result), TextLen);
end;
function StrToHex(const AText: string): string; overload;
begin
Result := string(StrToHex(AnsiString(AText)));
end;
function HexToStr(const AText: AnsiString): AnsiString; overload;
var
ResultLen: Integer;
begin
// set the length of the result to half the Text length
ResultLen := Length(AText) div 2;
SetLength(Result, ResultLen);
// convert the hex back into a string
if (HexToBin(PAnsiChar(AText), PAnsiChar(Result), ResultLen) <> ResultLen) then
Result := 'Error Converting Hex To String: ' + AText;
end;
function HexToStr(const AText: string): string; overload;
begin
Result := string(HexToStr(AnsiString(AText)));
end;
function Encode7Bit(const AText: string; AUdhLen: Byte;
out ATextLen: Byte): string;
// AText: Ascii text
// AUdhLen: Length of UDH including UDH Len byte (e.g. '050003CC0101' = 6 bytes)
// ATextLen: returns length of text that was encoded. This can be different
// than Length(AText) due to escape characters
// Returns text as encoded PDU hex string
var
Text7Bit: AnsiString;
Pdu: AnsiString;
PduIdx: Integer;
PduLen: Byte;
PaddingBits: Byte;
BitsToMove: Byte;
Septet: Byte;
Octet: Byte;
PrevOctet: Byte;
ShiftedOctet: Byte;
i: Integer;
begin
Result := '';
Text7Bit := ConvertAsciiTo7Bit(AText, AUdhLen);
ATextLen := Length(Text7Bit);
BitsToMove := 0;
// determine how many padding bits needed based on the UDH
if (AUdhLen > 0) then
PaddingBits := 7 - ((AUdhLen * 8) mod 7)
else
PaddingBits := 0;
// calculate the number of bytes needed to store the 7-bit text
// along with any padding bits that are required
PduLen := Ceil(((ATextLen * 7) + PaddingBits) / 8);
// reserve space for the PDU bytes
Pdu := AnsiString(StringOfChar(#0, PduLen));
PduIdx := 1;
for i := 1 to ATextLen do
begin
if (BitsToMove = 7) then
BitsToMove := 0
else
begin
// convert the current character to a septet (7-bits) and make room for
// the bits from the next one
Septet := (Byte(Text7Bit[i]) shr BitsToMove);
if (i = ATextLen) then
Octet := Septet
else
begin
// convert the next character to a septet and copy the bits from it
// to the octet (PDU byte)
Octet := Septet or
Byte((Byte(Text7Bit[i + 1]) shl Byte(7 - BitsToMove)));
end;
Byte(Pdu[PduIdx]) := Octet;
Inc(PduIdx);
Inc(BitsToMove);
end;
end;
// The following code pads the pdu on the *right* by shifting it to the *left*
// by <PaddingBits>. It does this by using the same bit storage convention as
// the 7-bit compression routine above, by taking the most significant
// <PaddingBits> from each PDU byte and moving them to the least significant
// bits of the next PDU byte. If there is no room in the last PDU byte for the
// high bits of the previous byte that were removed, then those bits are
// placed into an additional byte reserved for this purpose.
// Note: <PduLen> has already been set to account for the reserved byte if
// it is required.
if (PaddingBits > 0) then
begin
SetLength(Result, (PduLen * 2));
PrevOctet := 0;
for PduIdx := 1 to PduLen do
begin
Octet := Byte(Pdu[PduIdx]);
if (PduIdx = 1) then
ShiftedOctet := Byte(Octet shl PaddingBits)
else
ShiftedOctet := Byte(Octet shl PaddingBits) or
Byte(PrevOctet shr (8 - PaddingBits));
Byte(Pdu[PduIdx]) := ShiftedOctet;
PrevOctet := Octet;
end;
end;
Result := string(StrToHex(Pdu));
end;
function Decode7Bit(const APduData: string; AUdhLen: Integer): string;
// APduData: Hex string representation of PDU data
// AUdhLen: Length of UDH including UDH Len (e.g. '050003CC0101' = 6 bytes)
// Returns decoded Ascii text
var
Pdu: AnsiString;
NumSeptets: Byte;
Septets: AnsiString;
PduIdx: Integer;
PduLen: Integer;
by: Byte;
currBy: Byte;
left: Byte;
mask: Byte;
nextBy: Byte;
Octet: Byte;
NextOctet: Byte;
PaddingBits: Byte;
ShiftedOctet: Byte;
i: Integer;
begin
Result := '';
PaddingBits := 0;
// convert hex string to bytes
Pdu := AnsiString(HexToStr(APduData));
PduLen := Length(Pdu);
// The following code removes padding at the end of the PDU by shifting it
// *right* by <PaddingBits>. It does this by taking the least significant
// <PaddingBits> from the following PDU byte and moving them to the most
// significant the current PDU byte.
if (AUdhLen > 0) then
begin
PaddingBits := 7 - ((AUdhLen * 8) mod 7);
for PduIdx := 1 to PduLen do
begin
Octet := Byte(Pdu[PduIdx]);
if (PduIdx = PduLen) then
ShiftedOctet := Byte(Octet shr PaddingBits)
else
begin
NextOctet := Byte(Pdu[PduIdx + 1]);
ShiftedOctet := Byte(Octet shr PaddingBits) or
Byte(NextOctet shl (8 - PaddingBits));
end;
Byte(Pdu[PduIdx]) := ShiftedOctet;
end;
end;
// decode
// number of septets in PDU after excluding the padding bits
NumSeptets := ((PduLen * 8) - PaddingBits) div 7;
Septets := AnsiString(StringOfChar(#0, NumSeptets));
left := 7;
mask := $7F;
nextBy := 0;
PduIdx := 1;
for i := 1 to NumSeptets do
begin
if mask = 0 then
begin
Septets[i] := AnsiChar(nextBy);
left := 7;
mask := $7F;
nextBy := 0;
end
else
begin
if (PduIdx > PduLen) then
Break;
by := Byte(Pdu[PduIdx]);
Inc(PduIdx);
currBy := ((by AND mask) SHL (7 - left)) OR nextBy;
nextBy := (by AND (NOT mask)) SHR left;
Septets[i] := AnsiChar(currBy);
mask := mask SHR 1;
left := left - 1;
end;
end; // for
// remove last character if unused
// this is kind of a hack, but frankly I don't know how else to compensate
// for it.
if (Septets[NumSeptets] = #0) then
SetLength(Septets, NumSeptets - 1);
// convert 7-bit alphabet to ascii
Result := Convert7BitToAscii(Septets);
end;
initialization
InitializeTables;
end.
no you don't include the UDH part when encoding, but you if read the GSM phase 2 specification on page 57, they mention this fact : "If 7 bit data is used and the TP-UD-Header does not finish on a septet boundary then fill bits are inserted
after the last Information Element Data octet so that there is an integral number of septets for the entire
TP-UD header". When you include a UDH part this could not be the case, so all you need to do is calculate the offset (= number of fill bits)
Calculating the offset, this code assumes that UDHPart is a AnsiString:
Len := Length(UDHPart) shr 1;
Offset := 7 - ((Len * 8) mod 7); // fill bits
now when encoding the 7bit data, you proceed as normal but at the end, you shift the data Offset bits to the left, this code has the encoded data in variable result (ansistring):
// fill bits
if Offset > 0 then
begin
v := Result;
Len := Length(v);
BytesRemain := ceil(((Len * 7)+Offset) / 8);
Result := StringOfChar(#0, BytesRemain);
for InPos := 1 to BytesRemain do
begin
if InPos = 1 then
Byte(Result[InPos]) := Byte(v[InPos]) shl offset
else
Byte(Result[InPos]) := (Byte(v[InPos]) shl offset) or (Byte(v[InPos-1]) shr (8 - offset));
end;
end;
Decoding is same thing really, you first shift the 7 bit data offset bits to the right before decoding...
I hope this will set you onto the right track...
In your case
Data is D06536FB0DBABFE56C32
Get first char is D0 => h (in first 7 bit, the 8th bit not use)
The rest is 6536FB0DBABFE56C32
In bin
(01100101)0011011011111011000011011011101010111111111001010110110000110010
Shift right to left. => each right 7 bit is a char!
001100100110110011100101101111111011101000001101111 1101100 110110(0 1100101)
I shift 7 to left. you can get string from above. but i do for easy show :D
(1100101)(1101100)(1101100)(1101111)(0100000)(1110111)(1101111)(1110010)(1101100)(1100100)00
And the string is "ello world"
combine with first char you get "hello world"
I have problem with sockets.
If i send more then two lines of text using TClientSocket then server receives one line instead of two.
Client part:
ClientSocket1.Socket.SendText(Edit1.Text);//Text is 'Line1'
ClientSocket1.Socket.SendText(Edit2.Text);//Text is 'Line2'
Server part:
var
s: String;
begin
s := Socket.ReceiveText;
Memo1.Lines.Add(S);
The Memo1 created line is 'Line1Line2'
Why?
Sorry for my english!
SendText does not send a CRLF. If you need to send a new line, you'll have to do it explicitly:
ClientSocket1.Socket.SendText(Edit1.Text + #13#10);
ClientSocket1.Socket.SendText(Edit2.Text + #13#10);
TClientSocket and TServerSocket implement TCP/IP, which is a byte stream that has no concept of message boundaries (unlike UDP, which does). When you call SendText(), it just dumps the String contents as-is onto the socket. When you call ReceiveText(), it returns whatever is currently in the socket buffer at that moment. That is why you see the server receive 'Line1Line2'. If you want to differentiate between the two lines, then you need to send a delimiter between them, such as a CRLF sequence, and then your server code needs to be updated to look for that. Since TCP/IP is a byte stream, there is no guaranteed 1-to-1 relationship between writes and reads. Case in point, you wrote 5 bytes followed by 5 bytes, but the server received 10 bytes all at once. So your reading code needs to buffer everything it receives and then you can check your buffer for the data you are looking for, eg:
Client:
ClientSocket1.Socket.SendText(Edit1.Text + #13#10);
ClientSocket1.Socket.SendText(Edit2.Text + #13#10);
Server:
procedure TForm1.ServerSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
begin
Socket.Data := TMemoryStream.Create;
end;
procedure TForm1.ServerSocket1Disconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
TMemoryStream(Socket.Data).Free;
Socket.Data := nil;
end;
procedure TForm1.ServerSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
var
Strm: TMemoryStream;
RecvLen: Integer;
StrmSize, I: Int64;
Ptr: PByte;
B: Byte;
s: AnsiString;
begin
Strm := TMemoryStream(Socket.Data);
RecvLen := Socket.ReceiveLength;
if RecvLen <= 0 then Exit;
StrmSize := Strm.Size;
Strm.Size := StrmSize + RecvLen;
Ptr := PByte(Strm.Memory);
Inc(Ptr, Strm.Position);
RecvLen := Socket.ReceiveBuf(Ptr^, RecvLen);
if RecvLen <= 0 then
begin
Strm.Size := StrmSize;
Exit;
end;
Strm.Size := StrmSize + RecvLen;
while (Strm.Size - Strm.Position) >= 2 do
begin
Strm.ReadBuffer(B, 1);
if B <> 13 then Continue;
Strm.ReadBuffer(B, 1);
if B <> 10 then
begin
if B = 13 then
begin
Strm.Seek(-1, soCurrent);
Continue;
end;
end;
SetString(s, PAnsiChar(Strm.Memory), Strm.Position-2);
StrmSize := Strm.Size - Strm.Position;
if StrmSize then
begin
Strm.Clear;
end else
begin
Ptr := PByte(Strm.Memory);
Inc(Ptr, Strm.Position);
Move(Ptr^, Strm.Memory^, StrmSize);
Strm.Size := StrmSize;
Strm.Position := 0;
end;
Memo1.Lines.Add(S);
end;
end;
You need to add a CRLF or newline to Edit1.Text and Edit2.Text