Hello,
I’m new to Oxygen and was introduced by Jens. He tought me a lot about the commonly used construct of a project written in this language. But first and foremost he brought me back from Delphi XE6 on the right track.
Now I have to write a small TCP-Client for iOS. I found the following code in Objective-C:
How to open a TCP socket in Objective-C
Then I translated the code to Oxygen, so it looks like this Client.pas (3.2 KB)
If I try to send some data to my server (written in D7) I will notice a connection but no data will be received.
The server hangs up at “Server.Socket.ReadLn;”
The problem may be the following part:
method ServerConnector.writeOut(s: NSString);
begin
var buf:= Byte(s.UTF8String); // originally: uint8_t *buf = (uint8_t *)[s UTF8String];
fOutputStream.&write(@buf) maxLength(Char(buf).stringValue.length); // originally: [outputStream write:buf maxLength:strlen((char *)buf)];
end;
I think I have done some mistakes while translating.
I would be glad if you could help me.
Best regards
Benny
mh
(marc hoffman)
July 24, 2014, 4:43pm
2
whats wrong with just strlen(buf)
for the last parameter?
You’re casting the pointer to a char, calling stringValue on that (which gives you a new NSString of just that one Char ± not actually even the first char of the string, but the poiter, reinterpreted as Char), and then calling length (which gives you back “1”)
strlen expects a ^AnsiChar and If I try to cast this
OutputStream.&write(@buf) maxLength(strlen(^AnsiChar(buf)));
I ll get a BAD_ACCESS exception
mh
(marc hoffman)
July 24, 2014, 5:30pm
4
Come to think, there’s more wrong with this. s.UTF8String
returns a char pointer (think pChar), but you cast it to a byte. not to a byte pointer , to a plain byte. That’s all but the lowest 8 bits gone, right there.
Then you pass the address of that byte to write() maxLength()
. There’s at most 1 valid byte to find at that address (the lowest 8 bits truncated from the original char’s address.
You’ll want something like:
var buf:= s.UTF8String; // no cast, this will be an ^AnsiChar
fOutputStream.&write(buf) maxLength(strlen(but))
ad if that deposit like passing an AnsiChar^
to write() maxLength()
, you might need to add a case to ^Bute, such as
fOutputStream.&write(^Byte(buf)) maxLength(strlen(but))
But what you can’t to is just cast from poster types to simply types, and back.
Hope this helps,
marc
1 Like
Now I understand. Thank you for that!
But the server still hangs up at
Server.Socket.ReadLn;
It seems that the stream didn’t get flush. But also after closing the app (from the dock) my server doesn’t receive any data.
mh
(marc hoffman)
July 25, 2014, 10:37am
6
I’d probably need to see more to comment on that new part.
Here is the whole class:
interface
uses UIKit;
type
Communicator = public class(INSStreamDelegate)
private
fReadStream: CFReadStreamRef;
fWriteStream: CFWriteStreamRef;
fInputStream: NSInputStream;
fOutputStream: NSOutputStream;
method setup;
method open;
method close;
method stream(stream: NSStream) handleEvent(&event: NSStreamEvent);
method readIn(s: NSString);
method writeOut(s: NSString);
public
constructor;
end;
implementation
constructor Communicator;
begin
setup;
writeOut('Test\n');
end;
method Communicator.setup;
begin
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, 'xx.xxx.xxx.xxx', 8080, @fReadStream, @fWriteStream);
if not CFWriteStreamOpen(fWriteStream) then
begin
NSLog('Error, WriteStream not open');
exit;
end;
self.open();
NSLog('Status of OutputStream: %i', fOutputStream.streamStatus());
exit;
end;
method Communicator.open;
begin
NSLog('Opening streams.');
fInputStream := bridge<NSInputStream>(fReadStream);
fOutputStream := bridge<NSOutputStream>(fWriteStream);
fInputStream.setDelegate(self);
fOutputStream.setDelegate(self);
fInputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop()) forMode(NSDefaultRunLoopMode);
fOutputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop()) forMode(NSDefaultRunLoopMode);
fInputStream.open();
fOutputStream.open();
end;
method Communicator.close;
begin
NSLog('Closing streams.');
fInputStream.close();
fOutputStream.close();
fInputStream.removeFromRunLoop(NSRunLoop.currentRunLoop()) forMode(NSDefaultRunLoopMode);
fOutputStream.removeFromRunLoop(NSRunLoop.currentRunLoop()) forMode(NSDefaultRunLoopMode);
fInputStream.setDelegate(nil);
fOutputStream.setDelegate(nil);
fInputStream := nil;
fOutputStream := nil;
end;
method Communicator.stream(stream: NSStream) handleEvent(&event: NSStreamEvent);
begin
NSLog('Stream triggered.');
case &event of
NSStreamEvent.NSStreamEventHasSpaceAvailable: begin
if stream = fOutputStream then
begin
NSLog('OutputStream is ready.');
end;
end;
NSStreamEvent.NSStreamEventHasBytesAvailable: begin
if stream = fInputStream then
begin
NSLog('InputStream is ready.');
var buf: array[1024] of uint8_t;
var len: Integer := 0;
len := fInputStream.&read(buf) maxLength(1024);
if len > 0 then
begin
var data: NSMutableData := NSMutableData.alloc().initWithLength(0);
data.appendBytes(@buf) length(len); // originally: [data appendBytes: (const void *)buf length:len];
var s: NSString := NSString.alloc().initWithData(data) encoding(NSStringEncoding.NSASCIIStringEncoding);
self.readIn(s);
end;
end;
end;
else begin
NSLog('Stream is sending an Event: %i', &event);
end;
end;
end;
method Communicator.readIn(s: NSString);
begin
NSLog('Reading in the following:');
NSLog('%@', s);
end;
method Communicator.writeOut(s: NSString);
begin
var buf:= s.UTF8String();
fOutputStream.&write(^Byte(buf)) maxLength(strlen(buf));
NSLog('Writing out the following:');
NSLog('%@', s);
end;
end.
My server uses the TIdTCPServer (Indy 10) and has the following method:
procedure TServerEventHandler.OnServerExecute(AContext: TIdContext);
var StreamSize: Int64;
Cmd: String;
Response: TMemoryStream;
RequestHandler: TRequestHandler;
begin
Cmd:= Trim(AContext.Connection.IOHandler.ReadLn);
WriteLn(Format('[%s, %s] Requested: [%s]', // doesn't get reached
[DateTimeToStr(now),
AContext.Connection.Socket.Binding.IP,
Cmd]));
Response:= TMemoryStream.Create;
RequestHandler:= TRequestHandler.Create(Response);
try
RequestHandler.GetResponse(Cmd);
Response.Seek(0, soFromBeginning);
StreamSize:= Response.Size;
AContext.Connection.IOHandler.Write(StreamSize);
AContext.Connection.IOHandler.Write(Response);
AContext.Connection.DisconnectNotifyPeer;
finally
RequestHandler.Free;
Response.Free;
end;
end;